--- a/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
086271e35b0a419b38e8bda9bebd70693811df0a jdk8-b48
cecd7026f30cbd83b0601925a7a5e059aec98138 jdk8-b49
38fe5ab028908cf64dd73a43336ba3211577bfc3 jdk8-b50
+382651d28f2502d371eca751962232c0e535e57a jdk8-b51
+b67041a6cb508da18d2f5c7687e6a31e08bea4fc jdk8-b52
--- a/.hgtags-top-repo Mon Aug 27 10:23:43 2012 +0800
+++ b/.hgtags-top-repo Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
3f6c72d1c2a6e5c9e7d81c3dc984886678a128ad jdk8-b48
c97b99424815c43818e3cc3ffcdd1a60f3198b52 jdk8-b49
2fd67618b9a3c847780ed7b9d228e862b6e2824c jdk8-b50
+57c0aee7309050b9d6cfcbd202dc704e9260b377 jdk8-b51
+8d24def5ceb3b8f2e857f2e18b2804fc59eecf8d jdk8-b52
--- a/corba/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/corba/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
7e2b179a5b4dbd3f097e28daa00abfcc72ba3e0b jdk8-b48
fe44e58a6bdbeae350ce96aafb49770a5dca5d8a jdk8-b49
d20d9eb9f093adbf392918c703960ad24c93a331 jdk8-b50
+9b0f841ca9f7ee9bacf16a5ab41c4f829276bc6b jdk8-b51
+80689ff9cb499837513f18a1136dac7f0686cd55 jdk8-b52
--- a/hotspot/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -267,3 +267,6 @@
72e0362c3f0cfacbbac8af8a5b9d2e182f21c17b hs24-b18
58f237a9e83af6ded0d2e2c81d252cd47c0f4c45 jdk8-b50
3b3ad16429701b2eb6712851c2f7c5a726eb2cbe hs24-b19
+663fc23da8d51c4c0552cbcb17ffc85f5869d4fd jdk8-b51
+4c8f2a12e757e7a808aa85827573e09f75d7459f hs24-b20
+6d0436885201db3f581523344a734793bb989549 jdk8-b52
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeBlob.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeBlob.java Mon Aug 27 10:58:40 2012 -0700
@@ -93,7 +93,6 @@
public boolean isUncommonTrapStub() { return false; }
public boolean isExceptionStub() { return false; }
public boolean isSafepointStub() { return false; }
- public boolean isRicochetBlob() { return false; }
public boolean isAdapterBlob() { return false; }
// Fine grain nmethod support: isNmethod() == isJavaMethod() || isNativeMethod() || isOSRMethod()
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java Mon Aug 27 10:58:40 2012 -0700
@@ -57,7 +57,6 @@
virtualConstructor.addMapping("BufferBlob", BufferBlob.class);
virtualConstructor.addMapping("nmethod", NMethod.class);
virtualConstructor.addMapping("RuntimeStub", RuntimeStub.class);
- virtualConstructor.addMapping("RicochetBlob", RicochetBlob.class);
virtualConstructor.addMapping("AdapterBlob", AdapterBlob.class);
virtualConstructor.addMapping("MethodHandlesAdapterBlob", MethodHandlesAdapterBlob.class);
virtualConstructor.addMapping("SafepointBlob", SafepointBlob.class);
@@ -127,10 +126,6 @@
Assert.that(result.blobContains(start) || result.blobContains(start.addOffsetTo(8)),
"found wrong CodeBlob");
}
- if (result.isRicochetBlob()) {
- // This should probably be done for other SingletonBlobs
- return VM.getVM().ricochetBlob();
- }
return result;
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/RicochetBlob.java Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * 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.
- *
- */
-
-package sun.jvm.hotspot.code;
-
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-
-/** RicochetBlob (currently only used by Compiler 2) */
-
-public class RicochetBlob extends SingletonBlob {
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static void initialize(TypeDataBase db) {
- Type type = db.lookupType("RicochetBlob");
-
- bounceOffsetField = type.getCIntegerField("_bounce_offset");
- exceptionOffsetField = type.getCIntegerField("_exception_offset");
- }
-
- private static CIntegerField bounceOffsetField;
- private static CIntegerField exceptionOffsetField;
-
- public RicochetBlob(Address addr) {
- super(addr);
- }
-
- public boolean isRicochetBlob() {
- return true;
- }
-
- public Address bounceAddr() {
- return codeBegin().addOffsetTo(bounceOffsetField.getValue(addr));
- }
-
- public boolean returnsToBounceAddr(Address pc) {
- Address bouncePc = bounceAddr();
- return (pc.equals(bouncePc) || pc.addOffsetTo(Frame.pcReturnOffset()).equals(bouncePc));
- }
-
-}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Frame.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Frame.java Mon Aug 27 10:58:40 2012 -0700
@@ -147,12 +147,6 @@
}
}
- public boolean isRicochetFrame() {
- CodeBlob cb = VM.getVM().getCodeCache().findBlob(getPC());
- RicochetBlob rcb = VM.getVM().ricochetBlob();
- return (cb == rcb && rcb != null && rcb.returnsToBounceAddr(getPC()));
- }
-
public boolean isCompiledFrame() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(!VM.getVM().isCore(), "noncore builds only");
@@ -216,8 +210,7 @@
public Frame realSender(RegisterMap map) {
if (!VM.getVM().isCore()) {
Frame result = sender(map);
- while (result.isRuntimeFrame() ||
- result.isRicochetFrame()) {
+ while (result.isRuntimeFrame()) {
result = result.sender(map);
}
return result;
@@ -631,9 +624,6 @@
if (Assert.ASSERTS_ENABLED) {
Assert.that(cb != null, "sanity check");
}
- if (cb == VM.getVM().ricochetBlob()) {
- oopsRicochetDo(oopVisitor, regMap);
- }
if (cb.getOopMaps() != null) {
OopMapSet.oopsDo(this, cb, regMap, oopVisitor, VM.getVM().isDebugging());
@@ -650,10 +640,6 @@
// }
}
- private void oopsRicochetDo (AddressVisitor oopVisitor, RegisterMap regMap) {
- // XXX Empty for now
- }
-
// FIXME: implement the above routines, plus add
// oops_interpreted_arguments_do and oops_compiled_arguments_do
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java Mon Aug 27 10:58:40 2012 -0700
@@ -87,8 +87,6 @@
private StubRoutines stubRoutines;
private Bytes bytes;
- private RicochetBlob ricochetBlob;
-
/** Flags indicating whether we are attached to a core, C1, or C2 build */
private boolean usingClientCompiler;
private boolean usingServerCompiler;
@@ -628,18 +626,6 @@
return stubRoutines;
}
- public RicochetBlob ricochetBlob() {
- if (ricochetBlob == null) {
- Type ricochetType = db.lookupType("SharedRuntime");
- AddressField ricochetBlobAddress = ricochetType.getAddressField("_ricochet_blob");
- Address addr = ricochetBlobAddress.getValue();
- if (addr != null) {
- ricochetBlob = new RicochetBlob(addr);
- }
- }
- return ricochetBlob;
- }
-
public VMRegImpl getVMRegImplInfo() {
if (vmregImpl == null) {
vmregImpl = new VMRegImpl();
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCFrame.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCFrame.java Mon Aug 27 10:58:40 2012 -0700
@@ -571,8 +571,6 @@
// registers callee-saved, then we will have to copy over
// the RegisterMap update logic from the Intel code.
- if (isRicochetFrame()) return senderForRicochetFrame(map);
-
// The constructor of the sender must know whether this frame is interpreted so it can set the
// sender's _interpreter_sp_adjustment field.
if (VM.getVM().getInterpreter().contains(pc)) {
@@ -945,20 +943,6 @@
}
- private Frame senderForRicochetFrame(SPARCRegisterMap map) {
- if (DEBUG) {
- System.out.println("senderForRicochetFrame");
- }
- //RicochetFrame* f = RicochetFrame::from_frame(fr);
- // Cf. is_interpreted_frame path of frame::sender
- Address youngerSP = getSP();
- Address sp = getSenderSP();
- map.makeIntegerRegsUnsaved();
- map.shiftWindow(sp, youngerSP);
- boolean thisFrameAdjustedStack = true; // I5_savedSP is live in this RF
- return new SPARCFrame(biasSP(sp), biasSP(youngerSP), thisFrameAdjustedStack);
- }
-
private Frame senderForEntryFrame(RegisterMap regMap) {
SPARCRegisterMap map = (SPARCRegisterMap) regMap;
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/sparc/SPARCRicochetFrame.java Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * 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.
- *
- */
-
-package sun.jvm.hotspot.runtime.sparc;
-
-import java.util.*;
-import sun.jvm.hotspot.asm.sparc.SPARCRegister;
-import sun.jvm.hotspot.asm.sparc.SPARCRegisters;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-
-public class SPARCRicochetFrame {
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private SPARCFrame frame;
-
- private static void initialize(TypeDataBase db) {
- // Type type = db.lookupType("MethodHandles::RicochetFrame");
-
- }
-
- static SPARCRicochetFrame fromFrame(SPARCFrame f) {
- return new SPARCRicochetFrame(f);
- }
-
- private SPARCRicochetFrame(SPARCFrame f) {
- frame = f;
- }
-
- private Address registerValue(SPARCRegister reg) {
- return frame.getSP().addOffsetTo(reg.spOffsetInSavedWindow()).getAddressAt(0);
- }
-
- public Address savedArgsBase() {
- return registerValue(SPARCRegisters.L4);
- }
- public Address exactSenderSP() {
- return registerValue(SPARCRegisters.I5);
- }
- public Address senderLink() {
- return frame.getSenderSP();
- }
- public Address senderPC() {
- return frame.getSenderPC();
- }
- public Address extendedSenderSP() {
- return savedArgsBase();
- }
-}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java Mon Aug 27 10:58:40 2012 -0700
@@ -269,7 +269,6 @@
if (isEntryFrame()) return senderForEntryFrame(map);
if (isInterpretedFrame()) return senderForInterpreterFrame(map);
- if (isRicochetFrame()) return senderForRicochetFrame(map);
if(cb == null) {
cb = VM.getVM().getCodeCache().findBlob(getPC());
@@ -288,16 +287,6 @@
return new X86Frame(getSenderSP(), getLink(), getSenderPC());
}
- private Frame senderForRicochetFrame(X86RegisterMap map) {
- if (DEBUG) {
- System.out.println("senderForRicochetFrame");
- }
- X86RicochetFrame f = X86RicochetFrame.fromFrame(this);
- if (map.getUpdateMap())
- updateMapWithSavedLink(map, f.senderLinkAddress());
- return new X86Frame(f.extendedSenderSP(), f.exactSenderSP(), f.senderLink(), f.senderPC());
- }
-
private Frame senderForEntryFrame(X86RegisterMap map) {
if (DEBUG) {
System.out.println("senderForEntryFrame");
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/x86/X86RicochetFrame.java Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * 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.
- *
- */
-
-package sun.jvm.hotspot.runtime.x86;
-
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-
-public class X86RicochetFrame extends VMObject {
- static {
- VM.registerVMInitializedObserver(new Observer() {
- public void update(Observable o, Object data) {
- initialize(VM.getVM().getTypeDataBase());
- }
- });
- }
-
- private static void initialize(TypeDataBase db) {
- Type type = db.lookupType("MethodHandles::RicochetFrame");
-
- senderLinkField = type.getAddressField("_sender_link");
- savedArgsBaseField = type.getAddressField("_saved_args_base");
- exactSenderSPField = type.getAddressField("_exact_sender_sp");
- senderPCField = type.getAddressField("_sender_pc");
- }
-
- private static AddressField senderLinkField;
- private static AddressField savedArgsBaseField;
- private static AddressField exactSenderSPField;
- private static AddressField senderPCField;
-
- static X86RicochetFrame fromFrame(X86Frame f) {
- return new X86RicochetFrame(f.getFP().addOffsetTo(- senderLinkField.getOffset()));
- }
-
- private X86RicochetFrame(Address addr) {
- super(addr);
- }
-
- public Address senderLink() {
- return senderLinkField.getValue(addr);
- }
- public Address senderLinkAddress() {
- return addr.addOffsetTo(senderLinkField.getOffset());
- }
- public Address savedArgsBase() {
- return savedArgsBaseField.getValue(addr);
- }
- public Address extendedSenderSP() {
- return savedArgsBase();
- }
- public Address exactSenderSP() {
- return exactSenderSPField.getValue(addr);
- }
- public Address senderPC() {
- return senderPCField.getValue(addr);
- }
-}
--- a/hotspot/make/hotspot_version Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/make/hotspot_version Mon Aug 27 10:58:40 2012 -0700
@@ -35,7 +35,7 @@
HS_MAJOR_VER=24
HS_MINOR_VER=0
-HS_BUILD_NUMBER=19
+HS_BUILD_NUMBER=20
JDK_MAJOR_VER=1
JDK_MINOR_VER=8
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -44,8 +44,10 @@
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
+#define STOP(error) stop(error)
#else
#define BLOCK_COMMENT(str) block_comment(str)
+#define STOP(error) block_comment(error); stop(error)
#endif
// Convert the raw encoding form into the form expected by the
@@ -992,7 +994,7 @@
save_frame(0); // to avoid clobbering O0
ld_ptr(pc_addr, L0);
br_null_short(L0, Assembler::pt, PcOk);
- stop("last_Java_pc not zeroed before leaving Java");
+ STOP("last_Java_pc not zeroed before leaving Java");
bind(PcOk);
// Verify that flags was zeroed on return to Java
@@ -1001,7 +1003,7 @@
tst(L0);
br(Assembler::zero, false, Assembler::pt, FlagsOk);
delayed() -> restore();
- stop("flags not zeroed before leaving Java");
+ STOP("flags not zeroed before leaving Java");
bind(FlagsOk);
#endif /* ASSERT */
//
@@ -1021,7 +1023,7 @@
andcc(last_java_sp, 0x01, G0);
br(Assembler::notZero, false, Assembler::pt, StackOk);
delayed()->nop();
- stop("Stack Not Biased in set_last_Java_frame");
+ STOP("Stack Not Biased in set_last_Java_frame");
bind(StackOk);
#endif // ASSERT
assert( last_java_sp != G4_scratch, "bad register usage in set_last_Java_frame");
@@ -1650,23 +1652,28 @@
void RegistersForDebugging::print(outputStream* s) {
+ FlagSetting fs(Debugging, true);
int j;
- for ( j = 0; j < 8; ++j )
- if ( j != 6 ) s->print_cr("i%d = 0x%.16lx", j, i[j]);
- else s->print_cr( "fp = 0x%.16lx", i[j]);
- s->cr();
-
- for ( j = 0; j < 8; ++j )
- s->print_cr("l%d = 0x%.16lx", j, l[j]);
+ for (j = 0; j < 8; ++j) {
+ if (j != 6) { s->print("i%d = ", j); os::print_location(s, i[j]); }
+ else { s->print( "fp = " ); os::print_location(s, i[j]); }
+ }
s->cr();
- for ( j = 0; j < 8; ++j )
- if ( j != 6 ) s->print_cr("o%d = 0x%.16lx", j, o[j]);
- else s->print_cr( "sp = 0x%.16lx", o[j]);
+ for (j = 0; j < 8; ++j) {
+ s->print("l%d = ", j); os::print_location(s, l[j]);
+ }
s->cr();
- for ( j = 0; j < 8; ++j )
- s->print_cr("g%d = 0x%.16lx", j, g[j]);
+ for (j = 0; j < 8; ++j) {
+ if (j != 6) { s->print("o%d = ", j); os::print_location(s, o[j]); }
+ else { s->print( "sp = " ); os::print_location(s, o[j]); }
+ }
+ s->cr();
+
+ for (j = 0; j < 8; ++j) {
+ s->print("g%d = ", j); os::print_location(s, g[j]);
+ }
s->cr();
// print out floats with compression
@@ -2020,8 +2027,8 @@
char* b = new char[1024];
sprintf(b, "untested: %s", what);
- if ( ShowMessageBoxOnError ) stop(b);
- else warn(b);
+ if (ShowMessageBoxOnError) { STOP(b); }
+ else { warn(b); }
}
@@ -2998,26 +3005,60 @@
}
+// virtual method calling
+void MacroAssembler::lookup_virtual_method(Register recv_klass,
+ RegisterOrConstant vtable_index,
+ Register method_result) {
+ assert_different_registers(recv_klass, method_result, vtable_index.register_or_noreg());
+ Register sethi_temp = method_result;
+ const int base = (instanceKlass::vtable_start_offset() * wordSize +
+ // method pointer offset within the vtable entry:
+ vtableEntry::method_offset_in_bytes());
+ RegisterOrConstant vtable_offset = vtable_index;
+ // Each of the following three lines potentially generates an instruction.
+ // But the total number of address formation instructions will always be
+ // at most two, and will often be zero. In any case, it will be optimal.
+ // If vtable_index is a register, we will have (sll_ptr N,x; inc_ptr B,x; ld_ptr k,x).
+ // If vtable_index is a constant, we will have at most (set B+X<<N,t; ld_ptr k,t).
+ vtable_offset = regcon_sll_ptr(vtable_index, exact_log2(vtableEntry::size() * wordSize), vtable_offset);
+ vtable_offset = regcon_inc_ptr(vtable_offset, base, vtable_offset, sethi_temp);
+ Address vtable_entry_addr(recv_klass, ensure_simm13_or_reg(vtable_offset, sethi_temp));
+ ld_ptr(vtable_entry_addr, method_result);
+}
+
+
void MacroAssembler::check_klass_subtype(Register sub_klass,
Register super_klass,
Register temp_reg,
Register temp2_reg,
Label& L_success) {
- Label L_failure, L_pop_to_failure;
- check_klass_subtype_fast_path(sub_klass, super_klass,
- temp_reg, temp2_reg,
- &L_success, &L_failure, NULL);
Register sub_2 = sub_klass;
Register sup_2 = super_klass;
if (!sub_2->is_global()) sub_2 = L0;
if (!sup_2->is_global()) sup_2 = L1;
-
- save_frame_and_mov(0, sub_klass, sub_2, super_klass, sup_2);
+ bool did_save = false;
+ if (temp_reg == noreg || temp2_reg == noreg) {
+ temp_reg = L2;
+ temp2_reg = L3;
+ save_frame_and_mov(0, sub_klass, sub_2, super_klass, sup_2);
+ sub_klass = sub_2;
+ super_klass = sup_2;
+ did_save = true;
+ }
+ Label L_failure, L_pop_to_failure, L_pop_to_success;
+ check_klass_subtype_fast_path(sub_klass, super_klass,
+ temp_reg, temp2_reg,
+ (did_save ? &L_pop_to_success : &L_success),
+ (did_save ? &L_pop_to_failure : &L_failure), NULL);
+
+ if (!did_save)
+ save_frame_and_mov(0, sub_klass, sub_2, super_klass, sup_2);
check_klass_subtype_slow_path(sub_2, sup_2,
L2, L3, L4, L5,
NULL, &L_pop_to_failure);
// on success:
+ bind(L_pop_to_success);
restore();
ba_short(L_success);
@@ -3234,54 +3275,6 @@
}
-void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type) {
- assert_different_registers(mtype_reg, mh_reg, temp_reg);
- // compare method type against that of the receiver
- RegisterOrConstant mhtype_offset = delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg);
- load_heap_oop(mh_reg, mhtype_offset, temp_reg);
- cmp_and_brx_short(temp_reg, mtype_reg, Assembler::notEqual, Assembler::pn, wrong_method_type);
-}
-
-
-// A method handle has a "vmslots" field which gives the size of its
-// argument list in JVM stack slots. This field is either located directly
-// in every method handle, or else is indirectly accessed through the
-// method handle's MethodType. This macro hides the distinction.
-void MacroAssembler::load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
- Register temp_reg) {
- assert_different_registers(vmslots_reg, mh_reg, temp_reg);
- // load mh.type.form.vmslots
- Register temp2_reg = vmslots_reg;
- load_heap_oop(Address(mh_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg)), temp2_reg);
- load_heap_oop(Address(temp2_reg, delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, temp_reg)), temp2_reg);
- ld( Address(temp2_reg, delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, temp_reg)), vmslots_reg);
-}
-
-
-void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg, bool emit_delayed_nop) {
- assert(mh_reg == G3_method_handle, "caller must put MH object in G3");
- assert_different_registers(mh_reg, temp_reg);
-
- // pick out the interpreted side of the handler
- // NOTE: vmentry is not an oop!
- ld_ptr(mh_reg, delayed_value(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes, temp_reg), temp_reg);
-
- // off we go...
- ld_ptr(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes(), temp_reg);
- jmp(temp_reg, 0);
-
- // for the various stubs which take control at this point,
- // see MethodHandles::generate_method_handle_stub
-
- // Some callers can fill the delay slot.
- if (emit_delayed_nop) {
- delayed()->nop();
- }
-}
-
-
RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot,
Register temp_reg,
int extra_slot_offset) {
@@ -3914,7 +3907,7 @@
ld_ptr(G2_thread, in_bytes(JavaThread::tlab_start_offset()), t2);
or3(t1, t2, t3);
cmp_and_br_short(t1, t2, Assembler::greaterEqual, Assembler::pn, next);
- stop("assert(top >= start)");
+ STOP("assert(top >= start)");
should_not_reach_here();
bind(next);
@@ -3922,13 +3915,13 @@
ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), t2);
or3(t3, t2, t3);
cmp_and_br_short(t1, t2, Assembler::lessEqual, Assembler::pn, next2);
- stop("assert(top <= end)");
+ STOP("assert(top <= end)");
should_not_reach_here();
bind(next2);
and3(t3, MinObjAlignmentInBytesMask, t3);
cmp_and_br_short(t3, 0, Assembler::lessEqual, Assembler::pn, ok);
- stop("assert(aligned)");
+ STOP("assert(aligned)");
should_not_reach_here();
bind(ok);
@@ -3976,7 +3969,7 @@
btst(MinObjAlignmentInBytesMask, obj);
br(Assembler::zero, false, Assembler::pt, L);
delayed()->nop();
- stop("eden top is not properly aligned");
+ STOP("eden top is not properly aligned");
bind(L);
}
#endif // ASSERT
@@ -4013,7 +4006,7 @@
btst(MinObjAlignmentInBytesMask, top_addr);
br(Assembler::zero, false, Assembler::pt, L);
delayed()->nop();
- stop("eden top is not properly aligned");
+ STOP("eden top is not properly aligned");
bind(L);
}
#endif // ASSERT
@@ -4066,7 +4059,7 @@
btst(MinObjAlignmentInBytesMask, free);
br(Assembler::zero, false, Assembler::pt, L);
delayed()->nop();
- stop("updated TLAB free is not properly aligned");
+ STOP("updated TLAB free is not properly aligned");
bind(L);
}
#endif // ASSERT
@@ -4164,7 +4157,7 @@
ld_ptr(G2_thread, in_bytes(JavaThread::tlab_size_offset()), t2);
sll_ptr(t2, LogHeapWordSize, t2);
cmp_and_br_short(t1, t2, Assembler::equal, Assembler::pt, ok);
- stop("assert(t1 == tlab_size)");
+ STOP("assert(t1 == tlab_size)");
should_not_reach_here();
bind(ok);
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -2538,6 +2538,11 @@
Register temp_reg, Register temp2_reg,
Label& no_such_interface);
+ // virtual method calling
+ void lookup_virtual_method(Register recv_klass,
+ RegisterOrConstant vtable_index,
+ Register method_result);
+
// Test sub_klass against super_klass, with fast and slow paths.
// The fast path produces a tri-state answer: yes / no / maybe-slow.
@@ -2577,12 +2582,6 @@
Label& L_success);
// method handles (JSR 292)
- void check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type);
- void load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
- Register temp_reg);
- void jump_to_method_handle_entry(Register mh_reg, Register temp_reg, bool emit_delayed_nop = true);
// offset relative to Gargs of argument at tos[arg_slot].
// (arg_slot == 0 means the last argument, not the first).
RegisterOrConstant argument_offset(RegisterOrConstant arg_slot,
@@ -2590,7 +2589,7 @@
int extra_slot_offset = 0);
// Address of Gargs and argument_offset.
Address argument_address(RegisterOrConstant arg_slot,
- Register temp_reg,
+ Register temp_reg = noreg,
int extra_slot_offset = 0);
// Stack overflow checking
--- a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -2956,6 +2956,7 @@
void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
ciMethod* method = op->profiled_method();
int bci = op->profiled_bci();
+ ciMethod* callee = op->profiled_callee();
// Update counter for all call types
ciMethodData* md = method->method_data_or_null();
@@ -2984,9 +2985,11 @@
Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias);
Bytecodes::Code bc = method->java_code_at_bci(bci);
+ const bool callee_is_static = callee->is_loaded() && callee->is_static();
// Perform additional virtual call profiling for invokevirtual and
// invokeinterface bytecodes
if ((bc == Bytecodes::_invokevirtual || bc == Bytecodes::_invokeinterface) &&
+ !callee_is_static && // required for optimized MH invokes
C1ProfileVirtualCalls) {
assert(op->recv()->is_single_cpu(), "recv must be allocated");
Register recv = op->recv()->as_register();
--- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -515,9 +515,9 @@
// Need to differentiate between igetfield, agetfield, bgetfield etc.
// because they are different sizes.
// Get the type from the constant pool cache
- __ srl(G1_scratch, ConstantPoolCacheEntry::tosBits, G1_scratch);
- // Make sure we don't need to mask G1_scratch for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(G1_scratch, ConstantPoolCacheEntry::tos_state_shift, G1_scratch);
+ // Make sure we don't need to mask G1_scratch after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmp(G1_scratch, atos );
__ br(Assembler::equal, true, Assembler::pt, xreturn_path);
__ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i);
--- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -514,7 +514,6 @@
// interpreted but its pc is in the code cache (for c1 -> osr_frame_return_id stub), so it must be
// explicitly recognized.
- if (is_ricochet_frame()) return sender_for_ricochet_frame(map);
bool frame_is_interpreted = is_interpreted_frame();
if (frame_is_interpreted) {
@@ -821,9 +820,7 @@
values.describe(frame_no, sp() + w, err_msg("register save area word %d", w), 1);
}
- if (is_ricochet_frame()) {
- MethodHandles::RicochetFrame::describe(this, values, frame_no);
- } else if (is_interpreted_frame()) {
+ if (is_interpreted_frame()) {
DESCRIBE_FP_OFFSET(interpreter_frame_d_scratch_fp);
DESCRIBE_FP_OFFSET(interpreter_frame_l_scratch_fp);
DESCRIBE_FP_OFFSET(interpreter_frame_padding);
--- a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -505,7 +505,7 @@
void InterpreterMacroAssembler::load_receiver(Register param_count,
Register recv) {
sll(param_count, Interpreter::logStackElementSize, param_count);
- ld_ptr(Lesp, param_count, recv); // gets receiver Oop
+ ld_ptr(Lesp, param_count, recv); // gets receiver oop
}
void InterpreterMacroAssembler::empty_expression_stack() {
@@ -767,8 +767,12 @@
get_cache_and_index_at_bcp(cache, temp, bcp_offset, index_size);
ld_ptr(cache, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset(), bytecode);
const int shift_count = (1 + byte_no) * BitsPerByte;
- srl( bytecode, shift_count, bytecode);
- and3(bytecode, 0xFF, bytecode);
+ assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) ||
+ (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift),
+ "correct shift count");
+ srl(bytecode, shift_count, bytecode);
+ assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask");
+ and3(bytecode, ConstantPoolCacheEntry::bytecode_1_mask, bytecode);
}
--- a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -32,7 +32,6 @@
address generate_normal_entry(bool synchronized);
address generate_native_entry(bool synchronized);
address generate_abstract_entry(void);
- address generate_method_handle_entry(void);
address generate_math_entry(AbstractInterpreter::MethodKind kind);
address generate_empty_entry(void);
address generate_accessor_entry(void);
--- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -255,17 +255,6 @@
}
-// Method handle invoker
-// Dispatch a method of the form java.lang.invoke.MethodHandles::invoke(...)
-address InterpreterGenerator::generate_method_handle_entry(void) {
- if (!EnableInvokeDynamic) {
- return generate_abstract_entry();
- }
-
- return MethodHandles::generate_method_handle_interpreter_entry(_masm);
-}
-
-
//----------------------------------------------------------------------------------------------------
// Entry points & stack frame layout
//
@@ -395,7 +384,7 @@
case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
- case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break;
+
case Interpreter::java_lang_math_sin : break;
case Interpreter::java_lang_math_cos : break;
case Interpreter::java_lang_math_tan : break;
@@ -407,7 +396,9 @@
case Interpreter::java_lang_math_exp : break;
case Interpreter::java_lang_ref_reference_get
: entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break;
- default : ShouldNotReachHere(); break;
+ default:
+ fatal(err_msg("unexpected method kind: %d", kind));
+ break;
}
if (entry_point) return entry_point;
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -31,452 +31,37 @@
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
+#define STOP(error) stop(error)
#else
#define BLOCK_COMMENT(str) __ block_comment(str)
+#define STOP(error) block_comment(error); __ stop(error)
#endif
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
-address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm,
- address interpreted_entry) {
- // Just before the actual machine code entry point, allocate space
- // for a MethodHandleEntry::Data record, so that we can manage everything
- // from one base pointer.
- __ align(wordSize);
- address target = __ pc() + sizeof(Data);
- while (__ pc() < target) {
- __ nop();
- __ align(wordSize);
- }
-
- MethodHandleEntry* me = (MethodHandleEntry*) __ pc();
- me->set_end_address(__ pc()); // set a temporary end_address
- me->set_from_interpreted_entry(interpreted_entry);
- me->set_type_checking_entry(NULL);
-
- return (address) me;
-}
-
-MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm,
- address start_addr) {
- MethodHandleEntry* me = (MethodHandleEntry*) start_addr;
- assert(me->end_address() == start_addr, "valid ME");
-
- // Fill in the real end_address:
- __ align(wordSize);
- me->set_end_address(__ pc());
-
- return me;
-}
-
-// stack walking support
-
-frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) {
- //RicochetFrame* f = RicochetFrame::from_frame(fr);
- // Cf. is_interpreted_frame path of frame::sender
- intptr_t* younger_sp = fr.sp();
- intptr_t* sp = fr.sender_sp();
- map->make_integer_regs_unsaved();
- map->shift_window(sp, younger_sp);
- bool this_frame_adjusted_stack = true; // I5_savedSP is live in this RF
- return frame(sp, younger_sp, this_frame_adjusted_stack);
-}
-
-void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map) {
- ResourceMark rm;
- 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 - 1;
- intptr_t* loc = &base[slot_num];
- //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");
- slot_num -= type2size[ptype];
- loc = &base[slot_num];
- 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");
-}
-
-// Ricochet Frames
-const Register MethodHandles::RicochetFrame::L1_continuation = L1;
-const Register MethodHandles::RicochetFrame::L2_saved_target = L2;
-const Register MethodHandles::RicochetFrame::L3_saved_args_layout = L3;
-const Register MethodHandles::RicochetFrame::L4_saved_args_base = L4; // cf. Gargs = G4
-const Register MethodHandles::RicochetFrame::L5_conversion = L5;
-#ifdef ASSERT
-const Register MethodHandles::RicochetFrame::L0_magic_number_1 = L0;
-#endif //ASSERT
-
-oop MethodHandles::RicochetFrame::compute_saved_args_layout(bool read_cache, bool write_cache) {
- if (read_cache) {
- oop 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);
- oop cookie = java_lang_invoke_MethodTypeForm::vmlayout(mtform);
- if (write_cache) {
- (*saved_args_layout_addr()) = cookie;
- }
- return cookie;
+// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
+static RegisterOrConstant constant(int value) {
+ return RegisterOrConstant(value);
}
-void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm,
- // output params:
- int* bounce_offset,
- int* exception_offset,
- int* frame_size_in_words) {
- (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize;
-
- address start = __ pc();
-
-#ifdef ASSERT
- __ illtrap(0); __ illtrap(0); __ illtrap(0);
- // here's a hint of something special:
- __ set(MAGIC_NUMBER_1, G0);
- __ set(MAGIC_NUMBER_2, G0);
-#endif //ASSERT
- __ illtrap(0); // not reached
-
- // Return values are in registers.
- // L1_continuation 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(L1_continuation, 0);
- __ delayed()->nop();
- __ illtrap(0);
-
- DEBUG_ONLY(__ set(MAGIC_NUMBER_2, G0));
-
- (*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:
- // Oexception (O0): exception
- // Oissuing_pc (O1): return address/pc that threw exception (ignored, always equal to bounce addr)
- __ verify_oop(Oexception);
-
- // Take down the frame.
-
- // Cf. InterpreterMacroAssembler::remove_activation.
- leave_ricochet_frame(_masm, /*recv_reg=*/ noreg, I5_savedSP, I7);
-
- // We are done with this activation frame; find out where to go next.
- // The continuation point will be an exception handler, which expects
- // the following registers set up:
- //
- // Oexception: exception
- // Oissuing_pc: the local call that threw exception
- // Other On: garbage
- // In/Ln: the contents of the caller's register window
- //
- // We do the required restore at the last possible moment, because we
- // need to preserve some state across a runtime call.
- // (Remember that the caller activation is unknown--it might not be
- // interpreted, so things like Lscratch are useless in the caller.)
- __ mov(Oexception, Oexception ->after_save()); // get exception in I0 so it will be on O0 after restore
- __ add(I7, frame::pc_return_offset, Oissuing_pc->after_save()); // likewise set I1 to a value local to the caller
- __ call_VM_leaf(L7_thread_cache,
- CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address),
- G2_thread, Oissuing_pc->after_save());
-
- // The caller's SP was adjusted upon method entry to accomodate
- // the callee's non-argument locals. Undo that adjustment.
- __ JMP(O0, 0); // return exception handler in caller
- __ delayed()->restore(I5_savedSP, G0, SP);
-
- // (same old exception object is already in Oexception; see above)
- // Note that an "issuing PC" is actually the next PC after the call
-}
-
-void MethodHandles::RicochetFrame::enter_ricochet_frame(MacroAssembler* _masm,
- Register recv_reg,
- Register argv_reg,
- address return_handler) {
- // does not include the __ save()
- assert(argv_reg == Gargs, "");
- Address G3_mh_vmtarget( recv_reg, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes());
- Address G3_amh_conversion(recv_reg, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes());
-
- // Create the RicochetFrame.
- // Unlike on x86 we can store all required information in local
- // registers.
- BLOCK_COMMENT("push RicochetFrame {");
- __ set(ExternalAddress(return_handler), L1_continuation);
- __ load_heap_oop(G3_mh_vmtarget, L2_saved_target);
- __ mov(G0, L3_saved_args_layout);
- __ mov(Gargs, L4_saved_args_base);
- __ lduw(G3_amh_conversion, L5_conversion); // 32-bit field
- // I5, I6, I7 are already set up
- DEBUG_ONLY(__ set((int32_t) MAGIC_NUMBER_1, L0_magic_number_1));
- BLOCK_COMMENT("} RicochetFrame");
-}
-
-void MethodHandles::RicochetFrame::leave_ricochet_frame(MacroAssembler* _masm,
- Register recv_reg,
- Register new_sp_reg,
- Register sender_pc_reg) {
- assert(new_sp_reg == I5_savedSP, "exact_sender_sp already in place");
- assert(sender_pc_reg == I7, "in a fixed place");
- // does not include the __ ret() & __ restore()
- assert_different_registers(recv_reg, new_sp_reg, sender_pc_reg);
- // Take down the frame.
- // Cf. InterpreterMacroAssembler::remove_activation.
- BLOCK_COMMENT("end_ricochet_frame {");
- if (recv_reg->is_valid())
- __ mov(L2_saved_target, recv_reg);
- BLOCK_COMMENT("} end_ricochet_frame");
-}
-
-// Emit code to verify that FP is pointing at a valid ricochet frame.
-#ifndef PRODUCT
-enum {
- ARG_LIMIT = 255, SLOP = 45,
- // 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
-};
-#endif
-
-#ifdef ASSERT
-void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) {
- // The stack should look like this:
- // ... keep1 | dest=42 | keep2 | magic | handler | magic | recursive args | [RF]
- // Check various invariants.
-
- Register O7_temp = O7, O5_temp = O5;
-
- Label L_ok_1, L_ok_2, L_ok_3, L_ok_4;
- BLOCK_COMMENT("verify_clean {");
- // Magic numbers must check out:
- __ set((int32_t) MAGIC_NUMBER_1, O7_temp);
- __ cmp_and_br_short(O7_temp, L0_magic_number_1, Assembler::equal, Assembler::pt, L_ok_1);
- __ stop("damaged ricochet frame: MAGIC_NUMBER_1 not found");
-
- __ BIND(L_ok_1);
-
- // Arguments pointer must look reasonable:
-#ifdef _LP64
- Register FP_temp = O5_temp;
- __ add(FP, STACK_BIAS, FP_temp);
-#else
- Register FP_temp = FP;
-#endif
- __ cmp_and_brx_short(L4_saved_args_base, FP_temp, Assembler::greaterEqualUnsigned, Assembler::pt, L_ok_2);
- __ stop("damaged ricochet frame: L4 < FP");
-
- __ BIND(L_ok_2);
- // Disable until we decide on it's fate
- // __ sub(L4_saved_args_base, UNREASONABLE_STACK_MOVE * Interpreter::stackElementSize, O7_temp);
- // __ cmp(O7_temp, FP_temp);
- // __ br(Assembler::lessEqualUnsigned, false, Assembler::pt, L_ok_3);
- // __ delayed()->nop();
- // __ stop("damaged ricochet frame: (L4 - UNREASONABLE_STACK_MOVE) > FP");
-
- __ BIND(L_ok_3);
- extract_conversion_dest_type(_masm, L5_conversion, O7_temp);
- __ cmp_and_br_short(O7_temp, T_VOID, Assembler::equal, Assembler::pt, L_ok_4);
- extract_conversion_vminfo(_masm, L5_conversion, O5_temp);
- __ ld_ptr(L4_saved_args_base, __ argument_offset(O5_temp, O5_temp), O7_temp);
- assert(Assembler::is_simm13(RETURN_VALUE_PLACEHOLDER), "must be simm13");
- __ cmp_and_brx_short(O7_temp, (int32_t) RETURN_VALUE_PLACEHOLDER, Assembler::equal, Assembler::pt, L_ok_4);
- __ stop("damaged ricochet frame: RETURN_VALUE_PLACEHOLDER not found");
- __ BIND(L_ok_4);
- BLOCK_COMMENT("} verify_clean");
-}
-#endif //ASSERT
-
void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg, Register temp_reg, Register temp2_reg) {
if (VerifyMethodHandles)
verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(), temp_reg, temp2_reg,
- "AMH argument is a Class");
+ "MH argument is a Class");
__ load_heap_oop(Address(klass_reg, java_lang_Class::klass_offset_in_bytes()), klass_reg);
}
-void MethodHandles::load_conversion_vminfo(MacroAssembler* _masm, Address conversion_field_addr, Register reg) {
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
- assert(CONV_VMINFO_MASK == right_n_bits(BitsPerByte), "else change type of following load");
- __ ldub(conversion_field_addr.plus_disp(BytesPerInt - 1), reg);
-}
-
-void MethodHandles::extract_conversion_vminfo(MacroAssembler* _masm, Register conversion_field_reg, Register reg) {
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
- __ and3(conversion_field_reg, CONV_VMINFO_MASK, reg);
-}
-
-void MethodHandles::extract_conversion_dest_type(MacroAssembler* _masm, Register conversion_field_reg, Register reg) {
- __ srl(conversion_field_reg, CONV_DEST_TYPE_SHIFT, reg);
- __ and3(reg, 0x0F, reg);
-}
-
-void MethodHandles::load_stack_move(MacroAssembler* _masm,
- Address G3_amh_conversion,
- Register stack_move_reg) {
- BLOCK_COMMENT("load_stack_move {");
- __ ldsw(G3_amh_conversion, stack_move_reg);
- __ sra(stack_move_reg, CONV_STACK_MOVE_SHIFT, stack_move_reg);
-#ifdef ASSERT
- if (VerifyMethodHandles) {
- Label L_ok, L_bad;
- int32_t stack_move_limit = 0x0800; // extra-large
- __ cmp_and_br_short(stack_move_reg, stack_move_limit, Assembler::greaterEqual, Assembler::pn, L_bad);
- __ cmp(stack_move_reg, -stack_move_limit);
- __ br(Assembler::greater, false, Assembler::pt, L_ok);
- __ delayed()->nop();
- __ BIND(L_bad);
- __ stop("load_stack_move of garbage value");
- __ BIND(L_ok);
- }
-#endif
- BLOCK_COMMENT("} load_stack_move");
-}
-
#ifdef ASSERT
-void MethodHandles::RicochetFrame::verify() const {
- assert(magic_number_1() == MAGIC_NUMBER_1, "");
- 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, "");
- }
+static int check_nonzero(const char* xname, int x) {
+ assert(x != 0, err_msg("%s should be nonzero", xname));
+ return x;
}
-
-void MethodHandles::verify_argslot(MacroAssembler* _masm, Register argslot_reg, Register temp_reg, const char* error_message) {
- // Verify that argslot lies within (Gargs, FP].
- Label L_ok, L_bad;
- BLOCK_COMMENT("verify_argslot {");
- __ cmp_and_brx_short(Gargs, argslot_reg, Assembler::greaterUnsigned, Assembler::pn, L_bad);
- __ add(FP, STACK_BIAS, temp_reg); // STACK_BIAS is zero on !_LP64
- __ cmp_and_brx_short(argslot_reg, temp_reg, Assembler::lessEqualUnsigned, Assembler::pt, L_ok);
- __ BIND(L_bad);
- __ stop(error_message);
- __ BIND(L_ok);
- BLOCK_COMMENT("} verify_argslot");
-}
+#define NONZERO(x) check_nonzero(#x, x)
+#else //ASSERT
+#define NONZERO(x) (x)
+#endif //ASSERT
-void MethodHandles::verify_argslots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register arg_slot_base_reg,
- Register temp_reg,
- Register temp2_reg,
- bool negate_argslots,
- const char* error_message) {
- // Verify that [argslot..argslot+size) lies within (Gargs, FP).
- Label L_ok, L_bad;
- BLOCK_COMMENT("verify_argslots {");
- if (negate_argslots) {
- if (arg_slots.is_constant()) {
- arg_slots = -1 * arg_slots.as_constant();
- } else {
- __ neg(arg_slots.as_register(), temp_reg);
- arg_slots = temp_reg;
- }
- }
- __ add(arg_slot_base_reg, __ argument_offset(arg_slots, temp_reg), temp_reg);
- __ add(FP, STACK_BIAS, temp2_reg); // STACK_BIAS is zero on !_LP64
- __ cmp_and_brx_short(temp_reg, temp2_reg, Assembler::greaterUnsigned, Assembler::pn, L_bad);
- // Gargs points to the first word so adjust by BytesPerWord
- __ add(arg_slot_base_reg, BytesPerWord, temp_reg);
- __ cmp_and_brx_short(Gargs, temp_reg, Assembler::lessEqualUnsigned, Assembler::pt, 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) {
- enum { UNREASONABLE_STACK_MOVE = 256 * 4 }; // limit of 255 arguments
- 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 {");
- // __ btst(-stack_move_unit() - 1, arg_slots.as_register()); // no need
- // __ br(Assembler::notZero, false, Assembler::pn, L_bad);
- // __ delayed()->nop();
- __ cmp(arg_slots.as_register(), (int32_t) NULL_WORD);
- if (direction > 0) {
- __ br(allow_zero ? Assembler::less : Assembler::lessEqual, false, Assembler::pn, L_bad);
- __ delayed()->nop();
- __ cmp(arg_slots.as_register(), (int32_t) UNREASONABLE_STACK_MOVE);
- __ br(Assembler::less, false, Assembler::pn, L_ok);
- __ delayed()->nop();
- } else {
- __ br(allow_zero ? Assembler::greater : Assembler::greaterEqual, false, Assembler::pn, L_bad);
- __ delayed()->nop();
- __ cmp(arg_slots.as_register(), (int32_t) -UNREASONABLE_STACK_MOVE);
- __ br(Assembler::greater, false, Assembler::pn, L_ok);
- __ delayed()->nop();
- }
- __ 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");
- }
-}
-
+#ifdef ASSERT
void MethodHandles::verify_klass(MacroAssembler* _masm,
Register obj_reg, KlassHandle klass,
Register temp_reg, Register temp2_reg,
@@ -485,6 +70,14 @@
assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() &&
klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(),
"must be one of the SystemDictionaryHandles");
+ bool did_save = false;
+ if (temp_reg == noreg || temp2_reg == noreg) {
+ temp_reg = L1;
+ temp2_reg = L2;
+ __ save_frame_and_mov(0, obj_reg, L0);
+ obj_reg = L0;
+ did_save = true;
+ }
Label L_ok, L_bad;
BLOCK_COMMENT("verify_klass {");
__ verify_oop(obj_reg);
@@ -499,537 +92,415 @@
__ ld_ptr(Address(temp2_reg, 0), temp2_reg);
__ cmp_and_brx_short(temp_reg, temp2_reg, Assembler::equal, Assembler::pt, L_ok);
__ BIND(L_bad);
- __ stop(error_message);
+ if (did_save) __ restore();
+ __ STOP(error_message);
__ BIND(L_ok);
+ if (did_save) __ restore();
BLOCK_COMMENT("} verify_klass");
}
+
+void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) {
+ Label L;
+ BLOCK_COMMENT("verify_ref_kind {");
+ __ lduw(Address(member_reg, NONZERO(java_lang_invoke_MemberName::flags_offset_in_bytes())), temp);
+ __ srl( temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT, temp);
+ __ and3(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK, temp);
+ __ cmp_and_br_short(temp, ref_kind, Assembler::equal, Assembler::pt, L);
+ { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal);
+ jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind);
+ if (ref_kind == JVM_REF_invokeVirtual ||
+ ref_kind == JVM_REF_invokeSpecial)
+ // could do this for all ref_kinds, but would explode assembly code size
+ trace_method_handle(_masm, buf);
+ __ STOP(buf);
+ }
+ BLOCK_COMMENT("} verify_ref_kind");
+ __ bind(L);
+}
+
#endif // ASSERT
-
-void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register target, Register temp) {
+void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register target, Register temp,
+ bool for_compiler_entry) {
assert(method == G5_method, "interpreter calling convention");
__ verify_oop(method);
- __ ld_ptr(G5_method, in_bytes(methodOopDesc::from_interpreted_offset()), target);
- if (JvmtiExport::can_post_interpreter_events()) {
+
+ if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) {
+ Label run_compiled_code;
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
// compiled code in threads for which the event is enabled. Check here for
// interp_only_mode if these events CAN be enabled.
__ verify_thread();
- Label skip_compiled_code;
-
const Address interp_only(G2_thread, JavaThread::interp_only_mode_offset());
__ ld(interp_only, temp);
- __ tst(temp);
- __ br(Assembler::notZero, true, Assembler::pn, skip_compiled_code);
- __ delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), target);
- __ bind(skip_compiled_code);
+ __ cmp_and_br_short(temp, 0, Assembler::zero, Assembler::pt, run_compiled_code);
+ __ ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), target);
+ __ jmp(target, 0);
+ __ delayed()->nop();
+ __ BIND(run_compiled_code);
+ // Note: we could fill some delay slots here, but
+ // it doesn't matter, since this is interpreter code.
}
+
+ const ByteSize entry_offset = for_compiler_entry ? methodOopDesc::from_compiled_offset() :
+ methodOopDesc::from_interpreted_offset();
+ __ ld_ptr(G5_method, in_bytes(entry_offset), target);
__ jmp(target, 0);
__ delayed()->nop();
}
+void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm,
+ Register recv, Register method_temp,
+ Register temp2, Register temp3,
+ bool for_compiler_entry) {
+ BLOCK_COMMENT("jump_to_lambda_form {");
+ // This is the initial entry point of a lazy method handle.
+ // After type checking, it picks up the invoker from the LambdaForm.
+ assert_different_registers(recv, method_temp, temp2, temp3);
+ assert(method_temp == G5_method, "required register for loading method");
+
+ //NOT_PRODUCT({ FlagSetting fs(TraceMethodHandles, true); trace_method_handle(_masm, "LZMH"); });
+
+ // Load the invoker, as MH -> MH.form -> LF.vmentry
+ __ verify_oop(recv);
+ __ load_heap_oop(Address(recv, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes())), method_temp);
+ __ verify_oop(method_temp);
+ __ load_heap_oop(Address(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes())), method_temp);
+ __ verify_oop(method_temp);
+ // the following assumes that a methodOop is normally compressed in the vmtarget field:
+ __ load_heap_oop(Address(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes())), method_temp);
+ __ verify_oop(method_temp);
+
+ if (VerifyMethodHandles && !for_compiler_entry) {
+ // make sure recv is already on stack
+ __ load_sized_value(Address(method_temp, methodOopDesc::size_of_parameters_offset()),
+ temp2,
+ sizeof(u2), /*is_signed*/ false);
+ // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), "");
+ Label L;
+ __ ld_ptr(__ argument_address(temp2, temp2, -1), temp2);
+ __ cmp_and_br_short(temp2, recv, Assembler::equal, Assembler::pt, L);
+ __ STOP("receiver not on stack");
+ __ BIND(L);
+ }
+
+ jump_from_method_handle(_masm, method_temp, temp2, temp3, for_compiler_entry);
+ BLOCK_COMMENT("} jump_to_lambda_form");
+}
+
// Code generation
-address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
- // I5_savedSP/O5_savedSP: sender SP (must preserve)
- // G4 (Gargs): incoming argument list (must preserve)
- // G5_method: invoke methodOop
- // G3_method_handle: receiver method handle (must load from sp[MethodTypeForm.vmslots])
- // O0, O1, O2, O3, O4: garbage temps, blown away
- Register O0_mtype = O0;
- Register O1_scratch = O1;
- Register O2_scratch = O2;
- Register O3_scratch = O3;
- Register O4_argslot = O4;
- Register O4_argbase = O4;
+address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm,
+ vmIntrinsics::ID iid) {
+ const bool not_for_compiler_entry = false; // this is the interpreter entry
+ assert(is_signature_polymorphic(iid), "expected invoke iid");
+ if (iid == vmIntrinsics::_invokeGeneric ||
+ iid == vmIntrinsics::_compiledLambdaForm) {
+ // Perhaps surprisingly, the symbolic references visible to Java are not directly used.
+ // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod.
+ // They all allow an appendix argument.
+ __ should_not_reach_here(); // empty stubs make SG sick
+ return NULL;
+ }
- // emit WrongMethodType path first, to enable back-branch from main path
- Label wrong_method_type;
- __ bind(wrong_method_type);
- Label invoke_generic_slow_path;
- assert(methodOopDesc::intrinsic_id_size_in_bytes() == sizeof(u1), "");;
- __ ldub(Address(G5_method, methodOopDesc::intrinsic_id_offset_in_bytes()), O1_scratch);
- __ cmp(O1_scratch, (int) vmIntrinsics::_invokeExact);
- __ brx(Assembler::notEqual, false, Assembler::pt, invoke_generic_slow_path);
- __ delayed()->nop();
- __ mov(O0_mtype, G5_method_type); // required by throw_WrongMethodType
- __ mov(G3_method_handle, G3_method_handle); // already in this register
- // O0 will be filled in with JavaThread in stub
- __ jump_to(AddressLiteral(StubRoutines::throw_WrongMethodTypeException_entry()), O3_scratch);
- __ delayed()->nop();
+ // I5_savedSP/O5_savedSP: sender SP (must preserve; see prepare_to_jump_from_interpreted)
+ // G5_method: methodOop
+ // G4 (Gargs): incoming argument list (must preserve)
+ // O0: used as temp to hold mh or receiver
+ // O1, O4: garbage temps, blown away
+ Register O1_scratch = O1;
+ Register O4_param_size = O4; // size of parameters
+
+ address code_start = __ pc();
// here's where control starts out:
__ align(CodeEntryAlignment);
address entry_point = __ pc();
- // fetch the MethodType from the method handle
- // 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 = G5_method;
- for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) {
- __ ld_ptr(Address(tem, *pchase), O0_mtype);
- tem = O0_mtype; // in case there is another indirection
+ if (VerifyMethodHandles) {
+ Label L;
+ BLOCK_COMMENT("verify_intrinsic_id {");
+ __ ldub(Address(G5_method, methodOopDesc::intrinsic_id_offset_in_bytes()), O1_scratch);
+ __ cmp_and_br_short(O1_scratch, (int) iid, Assembler::equal, Assembler::pt, L);
+ if (iid == vmIntrinsics::_linkToVirtual ||
+ iid == vmIntrinsics::_linkToSpecial) {
+ // could do this for all kinds, but would explode assembly code size
+ trace_method_handle(_masm, "bad methodOop::intrinsic_id");
}
+ __ STOP("bad methodOop::intrinsic_id");
+ __ bind(L);
+ BLOCK_COMMENT("} verify_intrinsic_id");
+ }
+
+ // First task: Find out how big the argument list is.
+ Address O4_first_arg_addr;
+ int ref_kind = signature_polymorphic_intrinsic_ref_kind(iid);
+ assert(ref_kind != 0 || iid == vmIntrinsics::_invokeBasic, "must be _invokeBasic or a linkTo intrinsic");
+ if (ref_kind == 0 || MethodHandles::ref_kind_has_receiver(ref_kind)) {
+ __ load_sized_value(Address(G5_method, methodOopDesc::size_of_parameters_offset()),
+ O4_param_size,
+ sizeof(u2), /*is_signed*/ false);
+ // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), "");
+ O4_first_arg_addr = __ argument_address(O4_param_size, O4_param_size, -1);
+ } else {
+ DEBUG_ONLY(O4_param_size = noreg);
+ }
+
+ Register O0_mh = noreg;
+ if (!is_signature_polymorphic_static(iid)) {
+ __ ld_ptr(O4_first_arg_addr, O0_mh = O0);
+ DEBUG_ONLY(O4_param_size = noreg);
}
- // given the MethodType, find out where the MH argument is buried
- __ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O4_argslot);
- __ ldsw( Address(O4_argslot, __ delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, O1_scratch)), O4_argslot);
- __ add(__ argument_address(O4_argslot, O4_argslot, 1), O4_argbase);
- // Note: argument_address uses its input as a scratch register!
- Address mh_receiver_slot_addr(O4_argbase, -Interpreter::stackElementSize);
- __ ld_ptr(mh_receiver_slot_addr, G3_method_handle);
+ // O4_first_arg_addr is live!
- trace_method_handle(_masm, "invokeExact");
-
- __ check_method_handle_type(O0_mtype, G3_method_handle, O1_scratch, wrong_method_type);
-
- // Nobody uses the MH receiver slot after this. Make sure.
- DEBUG_ONLY(__ set((int32_t) 0x999999, O1_scratch); __ st_ptr(O1_scratch, mh_receiver_slot_addr));
-
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
+ if (TraceMethodHandles) {
+ const char* name = vmIntrinsics::name_at(iid);
+ if (*name == '_') name += 1;
+ const size_t len = strlen(name) + 50;
+ char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ const char* suffix = "";
+ if (vmIntrinsics::method_for(iid) == NULL ||
+ !vmIntrinsics::method_for(iid)->access_flags().is_public()) {
+ if (is_signature_polymorphic_static(iid))
+ suffix = "/static";
+ else
+ suffix = "/private";
+ }
+ jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix);
+ if (O0_mh != noreg)
+ __ mov(O0_mh, G3_method_handle); // make stub happy
+ trace_method_handle(_masm, qname);
+ }
- // for invokeGeneric (only), apply argument and result conversions on the fly
- __ bind(invoke_generic_slow_path);
-#ifdef ASSERT
- if (VerifyMethodHandles) {
- Label L;
- __ ldub(Address(G5_method, methodOopDesc::intrinsic_id_offset_in_bytes()), O1_scratch);
- __ cmp(O1_scratch, (int) vmIntrinsics::_invokeGeneric);
- __ brx(Assembler::equal, false, Assembler::pt, L);
- __ delayed()->nop();
- __ stop("bad methodOop::intrinsic_id");
- __ bind(L);
- }
-#endif //ASSERT
+ if (iid == vmIntrinsics::_invokeBasic) {
+ generate_method_handle_dispatch(_masm, iid, O0_mh, noreg, not_for_compiler_entry);
- // make room on the stack for another pointer:
- insert_arg_slots(_masm, 2 * stack_move_unit(), O4_argbase, O1_scratch, O2_scratch, O3_scratch);
- // load up an adapter from the calling type (Java weaves this)
- 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);
- __ 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.
- // (Generic invokers are shared among form-families of method-type.)
- __ st_ptr(O0_mtype, Address(O4_argbase, 0 * Interpreter::stackElementSize));
- // FIXME: assert that O3_adapter is of the right method-type.
- __ mov(O3_adapter, G3_method_handle);
- trace_method_handle(_masm, "invokeGeneric");
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
+ } else {
+ // Adjust argument list by popping the trailing MemberName argument.
+ Register O0_recv = noreg;
+ if (MethodHandles::ref_kind_has_receiver(ref_kind)) {
+ // Load the receiver (not the MH; the actual MemberName's receiver) up from the interpreter stack.
+ __ ld_ptr(O4_first_arg_addr, O0_recv = O0);
+ DEBUG_ONLY(O4_param_size = noreg);
+ }
+ Register G5_member = G5_method; // MemberName ptr; incoming method ptr is dead now
+ __ ld_ptr(__ argument_address(constant(0)), G5_member);
+ __ add(Gargs, Interpreter::stackElementSize, Gargs);
+ generate_method_handle_dispatch(_masm, iid, O0_recv, G5_member, not_for_compiler_entry);
+ }
+
+ if (PrintMethodHandleStubs) {
+ address code_end = __ pc();
+ tty->print_cr("--------");
+ tty->print_cr("method handle interpreter entry for %s", vmIntrinsics::name_at(iid));
+ Disassembler::decode(code_start, code_end);
+ tty->cr();
+ }
return entry_point;
}
-// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
-static RegisterOrConstant constant(int value) {
- return RegisterOrConstant(value);
-}
+void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm,
+ vmIntrinsics::ID iid,
+ Register receiver_reg,
+ Register member_reg,
+ bool for_compiler_entry) {
+ assert(is_signature_polymorphic(iid), "expected invoke iid");
+ // temps used in this code are not used in *either* compiled or interpreted calling sequences
+ Register temp1 = (for_compiler_entry ? G1_scratch : O1);
+ Register temp2 = (for_compiler_entry ? G4_scratch : O4);
+ Register temp3 = G3_scratch;
+ Register temp4 = (for_compiler_entry ? noreg : O2);
+ if (for_compiler_entry) {
+ assert(receiver_reg == (iid == vmIntrinsics::_linkToStatic ? noreg : O0), "only valid assignment");
+ assert_different_registers(temp1, O0, O1, O2, O3, O4, O5);
+ assert_different_registers(temp2, O0, O1, O2, O3, O4, O5);
+ assert_different_registers(temp3, O0, O1, O2, O3, O4, O5);
+ assert_different_registers(temp4, O0, O1, O2, O3, O4, O5);
+ }
+ if (receiver_reg != noreg) assert_different_registers(temp1, temp2, temp3, temp4, receiver_reg);
+ if (member_reg != noreg) assert_different_registers(temp1, temp2, temp3, temp4, member_reg);
+ if (!for_compiler_entry) assert_different_registers(temp1, temp2, temp3, temp4, O5_savedSP); // don't trash lastSP
-static void load_vmargslot(MacroAssembler* _masm, Address vmargslot_addr, Register result) {
- __ ldsw(vmargslot_addr, result);
-}
+ if (iid == vmIntrinsics::_invokeBasic) {
+ // indirect through MH.form.vmentry.vmtarget
+ jump_to_lambda_form(_masm, receiver_reg, G5_method, temp2, temp3, for_compiler_entry);
+
+ } else {
+ // The method is a member invoker used by direct method handles.
+ if (VerifyMethodHandles) {
+ // make sure the trailing argument really is a MemberName (caller responsibility)
+ verify_klass(_masm, member_reg, SystemDictionaryHandles::MemberName_klass(),
+ temp1, temp2,
+ "MemberName required for invokeVirtual etc.");
+ }
+
+ Address member_clazz( member_reg, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()));
+ Address member_vmindex( member_reg, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes()));
+ Address member_vmtarget( member_reg, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()));
-static RegisterOrConstant adjust_SP_and_Gargs_down_by_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register temp_reg, Register temp2_reg) {
- // Keep the stack pointer 2*wordSize aligned.
- const int TwoWordAlignmentMask = right_n_bits(LogBytesPerWord + 1);
- if (arg_slots.is_constant()) {
- const int offset = arg_slots.as_constant() << LogBytesPerWord;
- const int masked_offset = round_to(offset, 2 * BytesPerWord);
- const int masked_offset2 = (offset + 1*BytesPerWord) & ~TwoWordAlignmentMask;
- assert(masked_offset == masked_offset2, "must agree");
- __ sub(Gargs, offset, Gargs);
- __ sub(SP, masked_offset, SP );
- return offset;
- } else {
-#ifdef ASSERT
+ Register temp1_recv_klass = temp1;
+ if (iid != vmIntrinsics::_linkToStatic) {
+ __ verify_oop(receiver_reg);
+ if (iid == vmIntrinsics::_linkToSpecial) {
+ // Don't actually load the klass; just null-check the receiver.
+ __ null_check(receiver_reg);
+ } else {
+ // load receiver klass itself
+ __ null_check(receiver_reg, oopDesc::klass_offset_in_bytes());
+ __ load_klass(receiver_reg, temp1_recv_klass);
+ __ verify_oop(temp1_recv_klass);
+ }
+ BLOCK_COMMENT("check_receiver {");
+ // The receiver for the MemberName must be in receiver_reg.
+ // Check the receiver against the MemberName.clazz
+ if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) {
+ // Did not load it above...
+ __ load_klass(receiver_reg, temp1_recv_klass);
+ __ verify_oop(temp1_recv_klass);
+ }
+ if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) {
+ Label L_ok;
+ Register temp2_defc = temp2;
+ __ load_heap_oop(member_clazz, temp2_defc);
+ load_klass_from_Class(_masm, temp2_defc, temp3, temp4);
+ __ verify_oop(temp2_defc);
+ __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, temp4, L_ok);
+ // If we get here, the type check failed!
+ __ STOP("receiver class disagrees with MemberName.clazz");
+ __ bind(L_ok);
+ }
+ BLOCK_COMMENT("} check_receiver");
+ }
+ if (iid == vmIntrinsics::_linkToSpecial ||
+ iid == vmIntrinsics::_linkToStatic) {
+ DEBUG_ONLY(temp1_recv_klass = noreg); // these guys didn't load the recv_klass
+ }
+
+ // Live registers at this point:
+ // member_reg - MemberName that was the trailing argument
+ // temp1_recv_klass - klass of stacked receiver, if needed
+ // O5_savedSP - interpreter linkage (if interpreted)
+ // O0..O7,G1,G4 - compiler arguments (if compiled)
+
+ bool method_is_live = false;
+ switch (iid) {
+ case vmIntrinsics::_linkToSpecial:
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp3);
+ }
+ __ load_heap_oop(member_vmtarget, G5_method);
+ method_is_live = true;
+ break;
+
+ case vmIntrinsics::_linkToStatic:
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp3);
+ }
+ __ load_heap_oop(member_vmtarget, G5_method);
+ method_is_live = true;
+ break;
+
+ case vmIntrinsics::_linkToVirtual:
{
- Label L_ok;
- __ cmp_and_br_short(arg_slots.as_register(), 0, Assembler::greaterEqual, Assembler::pt, L_ok);
- __ stop("negative arg_slots");
- __ bind(L_ok);
+ // same as TemplateTable::invokevirtual,
+ // minus the CP setup and profiling:
+
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeVirtual, member_reg, temp3);
+ }
+
+ // pick out the vtable index from the MemberName, and then we can discard it:
+ Register temp2_index = temp2;
+ __ ld_ptr(member_vmindex, temp2_index);
+
+ if (VerifyMethodHandles) {
+ Label L_index_ok;
+ __ cmp_and_br_short(temp2_index, (int) 0, Assembler::greaterEqual, Assembler::pn, L_index_ok);
+ __ STOP("no virtual index");
+ __ BIND(L_index_ok);
+ }
+
+ // Note: The verifier invariants allow us to ignore MemberName.clazz and vmtarget
+ // at this point. And VerifyMethodHandles has already checked clazz, if needed.
+
+ // get target methodOop & entry point
+ __ lookup_virtual_method(temp1_recv_klass, temp2_index, G5_method);
+ method_is_live = true;
+ break;
}
-#endif
- __ sll_ptr(arg_slots.as_register(), LogBytesPerWord, temp_reg);
- __ add( temp_reg, 1*BytesPerWord, temp2_reg);
- __ andn(temp2_reg, TwoWordAlignmentMask, temp2_reg);
- __ sub(Gargs, temp_reg, Gargs);
- __ sub(SP, temp2_reg, SP );
- return temp_reg;
- }
-}
+
+ case vmIntrinsics::_linkToInterface:
+ {
+ // same as TemplateTable::invokeinterface
+ // (minus the CP setup and profiling, with different argument motion)
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeInterface, member_reg, temp3);
+ }
+
+ Register temp3_intf = temp3;
+ __ load_heap_oop(member_clazz, temp3_intf);
+ load_klass_from_Class(_masm, temp3_intf, temp2, temp4);
+ __ verify_oop(temp3_intf);
-static RegisterOrConstant adjust_SP_and_Gargs_up_by_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register temp_reg, Register temp2_reg) {
- // Keep the stack pointer 2*wordSize aligned.
- const int TwoWordAlignmentMask = right_n_bits(LogBytesPerWord + 1);
- if (arg_slots.is_constant()) {
- const int offset = arg_slots.as_constant() << LogBytesPerWord;
- const int masked_offset = offset & ~TwoWordAlignmentMask;
- __ add(Gargs, offset, Gargs);
- __ add(SP, masked_offset, SP );
- return offset;
- } else {
- __ sll_ptr(arg_slots.as_register(), LogBytesPerWord, temp_reg);
- __ andn(temp_reg, TwoWordAlignmentMask, temp2_reg);
- __ add(Gargs, temp_reg, Gargs);
- __ add(SP, temp2_reg, SP );
- return temp_reg;
+ Register G5_index = G5_method;
+ __ ld_ptr(member_vmindex, G5_index);
+ if (VerifyMethodHandles) {
+ Label L;
+ __ cmp_and_br_short(G5_index, 0, Assembler::greaterEqual, Assembler::pt, L);
+ __ STOP("invalid vtable index for MH.invokeInterface");
+ __ bind(L);
+ }
+
+ // given intf, index, and recv klass, dispatch to the implementation method
+ Label L_no_such_interface;
+ Register no_sethi_temp = noreg;
+ __ lookup_interface_method(temp1_recv_klass, temp3_intf,
+ // note: next two args must be the same:
+ G5_index, G5_method,
+ temp2, no_sethi_temp,
+ L_no_such_interface);
+
+ __ verify_oop(G5_method);
+ jump_from_method_handle(_masm, G5_method, temp2, temp3, for_compiler_entry);
+
+ __ bind(L_no_such_interface);
+ AddressLiteral icce(StubRoutines::throw_IncompatibleClassChangeError_entry());
+ __ jump_to(icce, temp3);
+ __ delayed()->nop();
+ break;
+ }
+
+ default:
+ fatal(err_msg("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
+ break;
+ }
+
+ if (method_is_live) {
+ // live at this point: G5_method, O5_savedSP (if interpreted)
+
+ // After figuring out which concrete method to call, jump into it.
+ // Note that this works in the interpreter with no data motion.
+ // But the compiled version will require that rcx_recv be shifted out.
+ __ verify_oop(G5_method);
+ jump_from_method_handle(_masm, G5_method, temp1, temp3, for_compiler_entry);
+ }
}
}
-// Helper to insert argument slots into the stack.
-// arg_slots must be a multiple of stack_move_unit() and < 0
-// argslot_reg is decremented to point to the new (shifted) location of the argslot
-// But, temp_reg ends up holding the original value of argslot_reg.
-void MethodHandles::insert_arg_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register argslot_reg,
- Register temp_reg, Register temp2_reg, Register temp3_reg) {
- // allow constant zero
- if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
- return;
-
- assert_different_registers(argslot_reg, temp_reg, temp2_reg, temp3_reg,
- (!arg_slots.is_register() ? Gargs : arg_slots.as_register()));
-
- BLOCK_COMMENT("insert_arg_slots {");
- if (VerifyMethodHandles)
- verify_argslot(_masm, argslot_reg, temp_reg, "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 argslot_reg.
- // The stacked return address gets pulled down with everything else.
- // That is, copy [sp, argslot) downward by -size words. In pseudo-code:
- // sp -= size;
- // for (temp = sp + size; temp < argslot; temp++)
- // temp[-size] = temp[0]
- // argslot -= size;
-
- // offset is temp3_reg in case of arg_slots being a register.
- RegisterOrConstant offset = adjust_SP_and_Gargs_up_by_slots(_masm, arg_slots, temp3_reg, temp_reg);
- __ sub(Gargs, offset, temp_reg); // source pointer for copy
-
- {
- Label loop;
- __ BIND(loop);
- // pull one word down each time through the loop
- __ ld_ptr( Address(temp_reg, 0 ), temp2_reg);
- __ st_ptr(temp2_reg, Address(temp_reg, offset) );
- __ add(temp_reg, wordSize, temp_reg);
- __ cmp_and_brx_short(temp_reg, argslot_reg, Assembler::lessUnsigned, Assembler::pt, loop);
- }
-
- // Now move the argslot down, to point to the opened-up space.
- __ add(argslot_reg, offset, argslot_reg);
- 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
-void MethodHandles::remove_arg_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register argslot_reg,
- Register temp_reg, Register temp2_reg, Register temp3_reg) {
- // allow constant zero
- if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
- return;
- assert_different_registers(argslot_reg, temp_reg, temp2_reg, temp3_reg,
- (!arg_slots.is_register() ? Gargs : arg_slots.as_register()));
-
- BLOCK_COMMENT("remove_arg_slots {");
- if (VerifyMethodHandles)
- verify_argslots(_masm, arg_slots, argslot_reg, temp_reg, temp2_reg, false,
- "deleted argument(s) must fall within current frame");
- if (VerifyMethodHandles)
- verify_stack_move(_masm, arg_slots, +1);
-
- // Pull up everything shallower than argslot.
- // Then remove the excess space on the stack.
- // The stacked return address gets pulled up with everything else.
- // That is, copy [sp, argslot) upward by size words. In pseudo-code:
- // for (temp = argslot-1; temp >= sp; --temp)
- // temp[size] = temp[0]
- // argslot += size;
- // sp += size;
-
- RegisterOrConstant offset = __ regcon_sll_ptr(arg_slots, LogBytesPerWord, temp3_reg);
- __ sub(argslot_reg, wordSize, temp_reg); // source pointer for copy
-
- {
- Label L_loop;
- __ BIND(L_loop);
- // pull one word up each time through the loop
- __ ld_ptr( Address(temp_reg, 0 ), temp2_reg);
- __ st_ptr(temp2_reg, Address(temp_reg, offset) );
- __ sub(temp_reg, wordSize, temp_reg);
- __ cmp_and_brx_short(temp_reg, Gargs, Assembler::greaterEqualUnsigned, Assembler::pt, L_loop);
- }
-
- // And adjust the argslot address to point at the deletion point.
- __ add(argslot_reg, offset, argslot_reg);
-
- // We don't need the offset at this point anymore, just adjust SP and Gargs.
- (void) adjust_SP_and_Gargs_up_by_slots(_masm, arg_slots, temp3_reg, temp_reg);
-
- BLOCK_COMMENT("} remove_arg_slots");
-}
-
-// Helper to copy argument slots to the top of the stack.
-// The sequence starts with argslot_reg 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 argslot_reg.
-void MethodHandles::push_arg_slots(MacroAssembler* _masm,
- Register argslot_reg,
- RegisterOrConstant slot_count,
- Register temp_reg, Register temp2_reg) {
- // allow constant zero
- if (slot_count.is_constant() && slot_count.as_constant() == 0)
- return;
- assert_different_registers(argslot_reg, temp_reg, temp2_reg,
- (!slot_count.is_register() ? Gargs : slot_count.as_register()),
- SP);
- assert(Interpreter::stackElementSize == wordSize, "else change this code");
-
- BLOCK_COMMENT("push_arg_slots {");
- if (VerifyMethodHandles)
- verify_stack_move(_masm, slot_count, 0);
-
- RegisterOrConstant offset = adjust_SP_and_Gargs_down_by_slots(_masm, slot_count, temp2_reg, temp_reg);
-
- if (slot_count.is_constant()) {
- for (int i = slot_count.as_constant() - 1; i >= 0; i--) {
- __ ld_ptr( Address(argslot_reg, i * wordSize), temp_reg);
- __ st_ptr(temp_reg, Address(Gargs, i * wordSize));
- }
- } else {
- Label L_plural, L_loop, L_break;
- // Emit code to dynamically check for the common cases, zero and one slot.
- __ cmp(slot_count.as_register(), (int32_t) 1);
- __ br(Assembler::greater, false, Assembler::pn, L_plural);
- __ delayed()->nop();
- __ br(Assembler::less, false, Assembler::pn, L_break);
- __ delayed()->nop();
- __ ld_ptr( Address(argslot_reg, 0), temp_reg);
- __ st_ptr(temp_reg, Address(Gargs, 0));
- __ ba_short(L_break);
- __ BIND(L_plural);
-
- // Loop for 2 or more:
- // top = &argslot[slot_count]
- // while (top > argslot) *(--Gargs) = *(--top)
- Register top_reg = temp_reg;
- __ add(argslot_reg, offset, top_reg);
- __ add(Gargs, offset, Gargs ); // move back up again so we can go down
- __ BIND(L_loop);
- __ sub(top_reg, wordSize, top_reg);
- __ sub(Gargs, wordSize, Gargs );
- __ ld_ptr( Address(top_reg, 0), temp2_reg);
- __ st_ptr(temp2_reg, Address(Gargs, 0));
- __ cmp_and_brx_short(top_reg, argslot_reg, Assembler::greaterUnsigned, Assembler::pt, L_loop);
- __ BIND(L_break);
- }
- BLOCK_COMMENT("} push_arg_slots");
-}
-
-// in-place movement; no change to Gargs
-// blows temp_reg, temp2_reg
-void MethodHandles::move_arg_slots_up(MacroAssembler* _masm,
- Register bottom_reg, // invariant
- Address top_addr, // can use temp_reg
- RegisterOrConstant positive_distance_in_slots, // destroyed if register
- Register temp_reg, Register temp2_reg) {
- assert_different_registers(bottom_reg,
- temp_reg, temp2_reg,
- positive_distance_in_slots.register_or_noreg());
- BLOCK_COMMENT("move_arg_slots_up {");
- Label L_loop, L_break;
- Register top_reg = temp_reg;
- if (!top_addr.is_same_address(Address(top_reg, 0))) {
- __ add(top_addr, top_reg);
- }
- // 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()) {
- __ cmp(positive_distance_in_slots.as_register(), (int32_t) 0);
- __ br(Assembler::lessEqual, false, Assembler::pn, L_bad);
- __ delayed()->nop();
- }
- __ cmp_and_brx_short(bottom_reg, top_reg, Assembler::lessUnsigned, Assembler::pt, L_ok);
- __ BIND(L_bad);
- __ stop("valid bounds (copy up)");
- __ BIND(L_ok);
- }
-#endif
- __ cmp_and_brx_short(bottom_reg, top_reg, Assembler::greaterEqualUnsigned, Assembler::pn, L_break);
- // work top down to bottom, copying contiguous data upwards
- // In pseudo-code:
- // while (--top >= bottom) *(top + distance) = *(top + 0);
- RegisterOrConstant offset = __ argument_offset(positive_distance_in_slots, positive_distance_in_slots.register_or_noreg());
- __ BIND(L_loop);
- __ sub(top_reg, wordSize, top_reg);
- __ ld_ptr( Address(top_reg, 0 ), temp2_reg);
- __ st_ptr(temp2_reg, Address(top_reg, offset) );
- __ cmp_and_brx_short(top_reg, bottom_reg, Assembler::greaterUnsigned, Assembler::pt, 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 temp_reg, temp2_reg
-void MethodHandles::move_arg_slots_down(MacroAssembler* _masm,
- Address bottom_addr, // can use temp_reg
- Register top_reg, // invariant
- RegisterOrConstant negative_distance_in_slots, // destroyed if register
- Register temp_reg, Register temp2_reg) {
- assert_different_registers(top_reg,
- negative_distance_in_slots.register_or_noreg(),
- temp_reg, temp2_reg);
- BLOCK_COMMENT("move_arg_slots_down {");
- Label L_loop, L_break;
- Register bottom_reg = temp_reg;
- if (!bottom_addr.is_same_address(Address(bottom_reg, 0))) {
- __ add(bottom_addr, bottom_reg);
- }
- // 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()) {
- __ cmp(negative_distance_in_slots.as_register(), (int32_t) 0);
- __ br(Assembler::greaterEqual, false, Assembler::pn, L_bad);
- __ delayed()->nop();
- }
- __ cmp_and_brx_short(bottom_reg, top_reg, Assembler::lessUnsigned, Assembler::pt, L_ok);
- __ BIND(L_bad);
- __ stop("valid bounds (copy down)");
- __ BIND(L_ok);
- }
-#endif
- __ cmp_and_brx_short(bottom_reg, top_reg, Assembler::greaterEqualUnsigned, Assembler::pn, L_break);
- // work bottom up to top, copying contiguous data downwards
- // In pseudo-code:
- // while (bottom < top) *(bottom - distance) = *(bottom + 0), bottom++;
- RegisterOrConstant offset = __ argument_offset(negative_distance_in_slots, negative_distance_in_slots.register_or_noreg());
- __ BIND(L_loop);
- __ ld_ptr( Address(bottom_reg, 0 ), temp2_reg);
- __ st_ptr(temp2_reg, Address(bottom_reg, offset) );
- __ add(bottom_reg, wordSize, bottom_reg);
- __ cmp_and_brx_short(bottom_reg, top_reg, Assembler::lessUnsigned, Assembler::pt, 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 value_src, Address slot_dest,
- Register temp_reg) {
- assert(!slot_dest.uses(temp_reg), "must be different register");
- BLOCK_COMMENT(!is_element ? "move_typed_arg {" : "move_typed_arg { (array element)");
- if (type == T_OBJECT || type == T_ARRAY) {
- __ load_heap_oop(value_src, temp_reg);
- __ verify_oop(temp_reg);
- __ st_ptr(temp_reg, slot_dest);
- } else if (type != T_VOID) {
- int arg_size = type2aelembytes(type);
- bool arg_is_signed = is_signed_subword_type(type);
- int slot_size = is_subword_type(type) ? type2aelembytes(T_INT) : arg_size; // store int sub-words as int
- __ load_sized_value( value_src, temp_reg, arg_size, arg_is_signed);
- __ store_sized_value(temp_reg, slot_dest, slot_size );
- }
- BLOCK_COMMENT("} move_typed_arg");
-}
-
-// Cf. TemplateInterpreterGenerator::generate_return_entry_for and
-// InterpreterMacroAssembler::save_return_value
-void MethodHandles::move_return_value(MacroAssembler* _masm, BasicType type,
- Address return_slot) {
- BLOCK_COMMENT("move_return_value {");
- // 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) {
- __ verify_oop(O0);
- __ st_ptr(O0, return_slot);
- } else if (type == T_INT || is_subword_type(type)) {
- int type_size = type2aelembytes(T_INT);
- __ store_sized_value(O0, return_slot, type_size);
- } else if (type == T_LONG) {
- // store the value by parts
- // Note: We assume longs are continguous (if misaligned) on the interpreter stack.
-#if !defined(_LP64) && defined(COMPILER2)
- __ stx(G1, return_slot);
-#else
- #ifdef _LP64
- __ stx(O0, return_slot);
- #else
- if (return_slot.has_disp()) {
- // The displacement is a constant
- __ st(O0, return_slot);
- __ st(O1, return_slot.plus_disp(Interpreter::stackElementSize));
- } else {
- __ std(O0, return_slot);
- }
- #endif
-#endif
- } else if (type == T_FLOAT) {
- __ stf(FloatRegisterImpl::S, Ftos_f, return_slot);
- } else if (type == T_DOUBLE) {
- __ stf(FloatRegisterImpl::D, Ftos_f, return_slot);
- } else {
- ShouldNotReachHere();
- }
- BLOCK_COMMENT("} move_return_value");
-}
-
#ifndef PRODUCT
-void MethodHandles::RicochetFrame::describe(const frame* fr, FrameValues& values, int frame_no) {
- RicochetFrame* rf = new RicochetFrame(*fr);
-
- // ricochet slots (kept in registers for sparc)
- values.describe(frame_no, rf->register_addr(I5_savedSP), err_msg("exact_sender_sp reg for #%d", frame_no));
- values.describe(frame_no, rf->register_addr(L5_conversion), err_msg("conversion reg for #%d", frame_no));
- values.describe(frame_no, rf->register_addr(L4_saved_args_base), err_msg("saved_args_base reg for #%d", frame_no));
- values.describe(frame_no, rf->register_addr(L3_saved_args_layout), err_msg("saved_args_layout reg for #%d", frame_no));
- values.describe(frame_no, rf->register_addr(L2_saved_target), err_msg("saved_target reg for #%d", frame_no));
- values.describe(frame_no, rf->register_addr(L1_continuation), err_msg("continuation reg for #%d", frame_no));
-
- // relevant ricochet targets (in caller frame)
- values.describe(-1, rf->saved_args_base(), err_msg("*saved_args_base for #%d", frame_no));
- values.describe(-1, (intptr_t *)(STACK_BIAS+(uintptr_t)rf->exact_sender_sp()), err_msg("*exact_sender_sp+STACK_BIAS for #%d", frame_no));
-}
-#endif // ASSERT
-
-#ifndef PRODUCT
-extern "C" void print_method_handle(oop mh);
void trace_method_handle_stub(const char* adaptername,
oopDesc* mh,
intptr_t* saved_sp,
intptr_t* args,
intptr_t* tracing_fp) {
- bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have mh
-
- tty->print_cr("MH %s mh="INTPTR_FORMAT " saved_sp=" INTPTR_FORMAT " args=" INTPTR_FORMAT, adaptername, (intptr_t) mh, saved_sp, args);
+ bool has_mh = (strstr(adaptername, "/static") == NULL &&
+ strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH
+ const char* mh_reg_name = has_mh ? "G3_mh" : "G3";
+ tty->print_cr("MH %s %s="INTPTR_FORMAT " saved_sp=" INTPTR_FORMAT " args=" INTPTR_FORMAT,
+ adaptername, mh_reg_name,
+ (intptr_t) mh, saved_sp, args);
if (Verbose) {
// dumping last frame with frame::describe
@@ -1090,6 +561,7 @@
// mark saved_sp, if seems valid (may not be valid for some adapters)
intptr_t *unbiased_sp = (intptr_t *)(STACK_BIAS+(uintptr_t)saved_sp);
+ const int ARG_LIMIT = 255, SLOP = 45, UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP);
if ((unbiased_sp >= dump_sp - UNREASONABLE_STACK_MOVE) && (unbiased_sp < dump_fp)) {
values.describe(-1, unbiased_sp, "*saved_sp+STACK_BIAS");
}
@@ -1097,10 +569,13 @@
// Note: the unextended_sp may not be correct
tty->print_cr(" stack layout:");
values.print(p);
- }
-
- if (has_mh) {
- print_method_handle(mh);
+ if (has_mh && mh->is_oop()) {
+ mh->print();
+ if (java_lang_invoke_MethodHandle::is_instance(mh)) {
+ if (java_lang_invoke_MethodHandle::form_offset_in_bytes() != 0)
+ java_lang_invoke_MethodHandle::form(mh)->print();
+ }
+ }
}
}
@@ -1143,1260 +618,3 @@
BLOCK_COMMENT("} trace_method_handle");
}
#endif // PRODUCT
-
-// which conversion op types are implemented here?
-int MethodHandles::adapter_conversion_ops_supported_mask() {
- return ((1<<java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY)
- |(1<<java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW)
- |(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)
- // OP_COLLECT_ARGS is below...
- |(1<<java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS)
- |(
- 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)
- )
- )
- );
-}
-
-//------------------------------------------------------------------------------
-// MethodHandles::generate_method_handle_stub
-//
-// 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():
- // - G5: garbage temp (was MethodHandle.invoke methodOop, unused)
- // - G3: receiver method handle
- // - O5_savedSP: sender SP (must preserve)
-
- const Register O0_scratch = O0;
- const Register O1_scratch = O1;
- const Register O2_scratch = O2;
- const Register O3_scratch = O3;
- const Register O4_scratch = O4;
- const Register G5_scratch = G5;
-
- // Often used names:
- const Register O0_argslot = O0;
-
- // Argument registers for _raise_exception:
- const Register O0_code = O0;
- const Register O1_actual = O1;
- const Register O2_required = O2;
-
- guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
-
- // Some handy addresses:
- Address G3_mh_vmtarget( G3_method_handle, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes());
-
- Address G3_dmh_vmindex( G3_method_handle, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes());
-
- Address G3_bmh_vmargslot( G3_method_handle, java_lang_invoke_BoundMethodHandle::vmargslot_offset_in_bytes());
- Address G3_bmh_argument( G3_method_handle, java_lang_invoke_BoundMethodHandle::argument_offset_in_bytes());
-
- Address G3_amh_vmargslot( G3_method_handle, java_lang_invoke_AdapterMethodHandle::vmargslot_offset_in_bytes());
- Address G3_amh_argument ( G3_method_handle, java_lang_invoke_AdapterMethodHandle::argument_offset_in_bytes());
- Address G3_amh_conversion(G3_method_handle, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes());
-
- const int java_mirror_offset = in_bytes(Klass::java_mirror_offset());
-
- if (have_entry(ek)) {
- __ nop(); // empty stubs make SG sick
- return;
- }
-
- address interp_entry = __ pc();
-
- trace_method_handle(_masm, entry_name(ek));
-
- BLOCK_COMMENT(err_msg("Entry %s {", entry_name(ek)));
-
- switch ((int) ek) {
- case _raise_exception:
- {
- // Not a real MH entry, but rather shared code for raising an
- // exception. For sharing purposes the arguments are passed into registers
- // and then placed in the intepreter calling convention here.
- assert(raise_exception_method(), "must be set");
- assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
-
- __ set(AddressLiteral((address) &_raise_exception_method), G5_method);
- __ ld_ptr(Address(G5_method, 0), G5_method);
-
- const int jobject_oop_offset = 0;
- __ ld_ptr(Address(G5_method, jobject_oop_offset), G5_method);
-
- adjust_SP_and_Gargs_down_by_slots(_masm, 3, noreg, noreg);
-
- __ st (O0_code, __ argument_address(constant(2), noreg, 0));
- __ st_ptr(O1_actual, __ argument_address(constant(1), noreg, 0));
- __ st_ptr(O2_required, __ argument_address(constant(0), noreg, 0));
- jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
- }
- break;
-
- case _invokestatic_mh:
- case _invokespecial_mh:
- {
- __ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
- // Same as TemplateTable::invokestatic or invokespecial,
- // minus the CP setup and profiling:
- if (ek == _invokespecial_mh) {
- // Must load & check the first argument before entering the target method.
- __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch);
- __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle);
- __ null_check(G3_method_handle);
- __ verify_oop(G3_method_handle);
- }
- jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
- }
- break;
-
- case _invokevirtual_mh:
- {
- // Same as TemplateTable::invokevirtual,
- // minus the CP setup and profiling:
-
- // Pick out the vtable index and receiver offset from the MH,
- // and then we can discard it:
- Register O2_index = O2_scratch;
- __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch);
- __ ldsw(G3_dmh_vmindex, O2_index);
- // Note: The verifier allows us to ignore G3_mh_vmtarget.
- __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle);
- __ null_check(G3_method_handle, oopDesc::klass_offset_in_bytes());
-
- // Get receiver klass:
- Register O0_klass = O0_argslot;
- __ load_klass(G3_method_handle, O0_klass);
- __ verify_oop(O0_klass);
-
- // Get target methodOop & entry point:
- const int base = instanceKlass::vtable_start_offset() * wordSize;
- assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below");
-
- __ sll_ptr(O2_index, LogBytesPerWord, O2_index);
- __ add(O0_klass, O2_index, O0_klass);
- Address vtable_entry_addr(O0_klass, base + vtableEntry::method_offset_in_bytes());
- __ ld_ptr(vtable_entry_addr, G5_method);
-
- jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
- }
- break;
-
- case _invokeinterface_mh:
- {
- // Same as TemplateTable::invokeinterface,
- // minus the CP setup and profiling:
- __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch);
- Register O1_intf = O1_scratch;
- Register G5_index = G5_scratch;
- __ load_heap_oop(G3_mh_vmtarget, O1_intf);
- __ ldsw(G3_dmh_vmindex, G5_index);
- __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle);
- __ null_check(G3_method_handle, oopDesc::klass_offset_in_bytes());
-
- // Get receiver klass:
- Register O0_klass = O0_argslot;
- __ load_klass(G3_method_handle, O0_klass);
- __ verify_oop(O0_klass);
-
- // Get interface:
- Label no_such_interface;
- __ verify_oop(O1_intf);
- __ lookup_interface_method(O0_klass, O1_intf,
- // Note: next two args must be the same:
- G5_index, G5_method,
- O2_scratch,
- O3_scratch,
- no_such_interface);
-
- jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
-
- __ bind(no_such_interface);
- // Throw an exception.
- // For historical reasons, it will be IncompatibleClassChangeError.
- __ unimplemented("not tested yet");
- __ ld_ptr(Address(O1_intf, java_mirror_offset), O2_required); // required interface
- __ mov( O0_klass, O1_actual); // bad receiver
- __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
- __ delayed()->mov(Bytecodes::_invokeinterface, O0_code); // who is complaining?
- }
- break;
-
- case _bound_ref_mh:
- case _bound_int_mh:
- case _bound_long_mh:
- case _bound_ref_direct_mh:
- case _bound_int_direct_mh:
- case _bound_long_direct_mh:
- {
- const bool direct_to_method = (ek >= _bound_ref_direct_mh);
- BasicType arg_type = ek_bound_mh_arg_type(ek);
- int arg_slots = type2size[arg_type];
-
- // Make room for the new argument:
- load_vmargslot(_masm, G3_bmh_vmargslot, O0_argslot);
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
-
- insert_arg_slots(_masm, arg_slots * stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch);
-
- // Store bound argument into the new stack slot:
- __ load_heap_oop(G3_bmh_argument, O1_scratch);
- if (arg_type == T_OBJECT) {
- __ st_ptr(O1_scratch, Address(O0_argslot, 0));
- } else {
- Address prim_value_addr(O1_scratch, java_lang_boxing_object::value_offset_in_bytes(arg_type));
- move_typed_arg(_masm, arg_type, false,
- prim_value_addr,
- Address(O0_argslot, 0),
- O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3)
- }
-
- if (direct_to_method) {
- __ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
- jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
- } else {
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); // target is a methodOop
- __ verify_oop(G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- }
- break;
-
- case _adapter_opt_profiling:
- if (java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes() != 0) {
- Address G3_mh_vmcount(G3_method_handle, java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes());
- __ ld(G3_mh_vmcount, O1_scratch);
- __ add(O1_scratch, 1, O1_scratch);
- __ st(O1_scratch, G3_mh_vmcount);
- }
- // fall through
-
- case _adapter_retype_only:
- case _adapter_retype_raw:
- // Immediately jump to the next MH layer:
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ verify_oop(G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- // This is OK when all parameter types widen.
- // It is also OK when a return type narrows.
- break;
-
- case _adapter_check_cast:
- {
- // Check a reference argument before jumping to the next layer of MH:
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- Address vmarg = __ argument_address(O0_argslot, O0_argslot);
-
- // What class are we casting to?
- Register O1_klass = O1_scratch; // Interesting AMH data.
- __ load_heap_oop(G3_amh_argument, O1_klass); // This is a Class object!
- load_klass_from_Class(_masm, O1_klass, O2_scratch, O3_scratch);
-
- Label L_done;
- __ ld_ptr(vmarg, O2_scratch);
- __ br_null_short(O2_scratch, Assembler::pn, L_done); // No cast if null.
- __ load_klass(O2_scratch, O2_scratch);
-
- // Live at this point:
- // - O0_argslot : argslot index in vmarg; may be required in the failing path
- // - O1_klass : klass required by the target method
- // - O2_scratch : argument klass to test
- // - G3_method_handle: adapter method handle
- __ check_klass_subtype(O2_scratch, O1_klass, O3_scratch, O4_scratch, L_done);
-
- // If we get here, the type check failed!
- __ load_heap_oop(G3_amh_argument, O2_required); // required class
- __ ld_ptr( vmarg, O1_actual); // bad object
- __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
- __ delayed()->mov(Bytecodes::_checkcast, O0_code); // who is complaining?
-
- __ BIND(L_done);
- // Get the new MH:
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_prim_to_prim:
- case _adapter_ref_to_prim:
- // Handled completely by optimized cases.
- __ stop("init_AdapterMethodHandle should not issue this");
- break;
-
- case _adapter_opt_i2i: // optimized subcase of adapt_prim_to_prim
-//case _adapter_opt_f2i: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_l2i: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_unboxi: // optimized subcase of adapt_ref_to_prim
- {
- // Perform an in-place conversion to int or an int subword.
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- Address value;
- Address vmarg;
- bool value_left_justified = false;
-
- switch (ek) {
- case _adapter_opt_i2i:
- value = vmarg = __ argument_address(O0_argslot, O0_argslot);
- break;
- case _adapter_opt_l2i:
- {
- // just delete the extra slot
-#ifdef _LP64
- // In V9, longs are given 2 64-bit slots in the interpreter, but the
- // data is passed in only 1 slot.
- // Keep the second slot.
- __ add(__ argument_address(O0_argslot, O0_argslot, -1), O0_argslot);
- remove_arg_slots(_masm, -stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch);
- value = Address(O0_argslot, 4); // Get least-significant 32-bit of 64-bit value.
- vmarg = Address(O0_argslot, Interpreter::stackElementSize);
-#else
- // Keep the first slot.
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
- remove_arg_slots(_masm, -stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch);
- value = Address(O0_argslot, 0);
- vmarg = value;
-#endif
- }
- break;
- case _adapter_opt_unboxi:
- {
- vmarg = __ argument_address(O0_argslot, O0_argslot);
- // Load the value up from the heap.
- __ ld_ptr(vmarg, O1_scratch);
- int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT);
-#ifdef ASSERT
- for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
- if (is_subword_type(BasicType(bt)))
- assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(BasicType(bt)), "");
- }
-#endif
- __ null_check(O1_scratch, value_offset);
- value = Address(O1_scratch, value_offset);
-#ifdef _BIG_ENDIAN
- // Values stored in objects are packed.
- value_left_justified = true;
-#endif
- }
- break;
- default:
- ShouldNotReachHere();
- }
-
- // This check is required on _BIG_ENDIAN
- Register G5_vminfo = G5_scratch;
- __ ldsw(G3_amh_conversion, G5_vminfo);
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
-
- // Original 32-bit vmdata word must be of this form:
- // | MBZ:6 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 |
- __ lduw(value, O1_scratch);
- if (!value_left_justified)
- __ sll(O1_scratch, G5_vminfo, O1_scratch);
- Label zero_extend, done;
- __ btst(CONV_VMINFO_SIGN_FLAG, G5_vminfo);
- __ br(Assembler::zero, false, Assembler::pn, zero_extend);
- __ delayed()->nop();
-
- // this path is taken for int->byte, int->short
- __ sra(O1_scratch, G5_vminfo, O1_scratch);
- __ ba_short(done);
-
- __ bind(zero_extend);
- // this is taken for int->char
- __ srl(O1_scratch, G5_vminfo, O1_scratch);
-
- __ bind(done);
- __ st(O1_scratch, vmarg);
-
- // Get the new MH:
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_opt_i2l: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_unboxl: // optimized subcase of adapt_ref_to_prim
- {
- // Perform an in-place int-to-long or ref-to-long conversion.
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
-
- // On big-endian machine we duplicate the slot and store the MSW
- // in the first slot.
- __ add(__ argument_address(O0_argslot, O0_argslot, 1), O0_argslot);
-
- insert_arg_slots(_masm, stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch);
-
- Address arg_lsw(O0_argslot, 0);
- Address arg_msw(O0_argslot, -Interpreter::stackElementSize);
-
- switch (ek) {
- case _adapter_opt_i2l:
- {
-#ifdef _LP64
- __ ldsw(arg_lsw, O2_scratch); // Load LSW sign-extended
-#else
- __ ldsw(arg_lsw, O3_scratch); // Load LSW sign-extended
- __ srlx(O3_scratch, BitsPerInt, O2_scratch); // Move MSW value to lower 32-bits for std
-#endif
- __ st_long(O2_scratch, arg_msw); // Uses O2/O3 on !_LP64
- }
- break;
- case _adapter_opt_unboxl:
- {
- // Load the value up from the heap.
- __ ld_ptr(arg_lsw, O1_scratch);
- int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_LONG);
- assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(T_DOUBLE), "");
- __ null_check(O1_scratch, value_offset);
- __ ld_long(Address(O1_scratch, value_offset), O2_scratch); // Uses O2/O3 on !_LP64
- __ st_long(O2_scratch, arg_msw);
- }
- break;
- default:
- ShouldNotReachHere();
- }
-
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_opt_f2d: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_d2f: // optimized subcase of adapt_prim_to_prim
- {
- // perform an in-place floating primitive conversion
- __ unimplemented(entry_name(ek));
- }
- break;
-
- case _adapter_prim_to_ref:
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
- break;
-
- case _adapter_swap_args:
- case _adapter_rot_args:
- // handled completely by optimized cases
- __ stop("init_AdapterMethodHandle should not issue this");
- break;
-
- case _adapter_opt_swap_1:
- case _adapter_opt_swap_2:
- case _adapter_opt_rot_1_up:
- case _adapter_opt_rot_1_down:
- case _adapter_opt_rot_2_up:
- case _adapter_opt_rot_2_down:
- {
- int 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.
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
- if (VerifyMethodHandles)
- verify_argslot(_masm, O0_argslot, O2_scratch, "swap point must fall within current frame");
-
- // 'vminfo' is the second.
- Register O1_destslot = O1_scratch;
- load_conversion_vminfo(_masm, G3_amh_conversion, O1_destslot);
- __ add(__ argument_address(O1_destslot, O1_destslot), O1_destslot);
- if (VerifyMethodHandles)
- verify_argslot(_masm, O1_destslot, O2_scratch, "swap point must fall within current frame");
-
- assert(Interpreter::stackElementSize == wordSize, "else rethink use of wordSize here");
- if (!rotate) {
- // simple swap
- for (int i = 0; i < swap_slots; i++) {
- __ ld_ptr( Address(O0_argslot, i * wordSize), O2_scratch);
- __ ld_ptr( Address(O1_destslot, i * wordSize), O3_scratch);
- __ st_ptr(O3_scratch, Address(O0_argslot, i * wordSize));
- __ st_ptr(O2_scratch, Address(O1_destslot, i * wordSize));
- }
- } else {
- // 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
- switch (swap_slots) {
- case 2 : __ ld_ptr(Address(O0_argslot, 1 * wordSize), O4_scratch); // fall-thru
- case 1 : __ ld_ptr(Address(O0_argslot, 0 * wordSize), O3_scratch); break;
- default: ShouldNotReachHere();
- }
- if (rotate > 0) {
- // 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:
- // argslot = src_addr - swap_bytes
- // destslot = dest_addr
- // while (argslot >= destslot) *(argslot + swap_bytes) = *(argslot + 0), argslot--;
- move_arg_slots_up(_masm,
- O1_destslot,
- Address(O0_argslot, 0),
- swap_slots,
- O0_argslot, O2_scratch);
- } else {
- // 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:
- // argslot = src_addr + swap_bytes
- // destslot = dest_addr
- // while (argslot <= destslot) *(argslot - swap_bytes) = *(argslot + 0), argslot++;
- // dest_slot denotes an exclusive upper limit
- int limit_bias = OP_ROT_ARGS_DOWN_LIMIT_BIAS;
- if (limit_bias != 0)
- __ add(O1_destslot, - limit_bias * wordSize, O1_destslot);
- move_arg_slots_down(_masm,
- Address(O0_argslot, swap_slots * wordSize),
- O1_destslot,
- -swap_slots,
- O0_argslot, O2_scratch);
-
- __ sub(O1_destslot, swap_slots * wordSize, O1_destslot);
- }
- // pop the original first chunk into the destination slot, now free
- switch (swap_slots) {
- case 2 : __ st_ptr(O4_scratch, Address(O1_destslot, 1 * wordSize)); // fall-thru
- case 1 : __ st_ptr(O3_scratch, Address(O1_destslot, 0 * wordSize)); break;
- default: ShouldNotReachHere();
- }
- }
-
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_dup_args:
- {
- // 'argslot' is the position of the first argument to duplicate.
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
-
- // 'stack_move' is negative number of words to duplicate.
- Register O1_stack_move = O1_scratch;
- load_stack_move(_masm, G3_amh_conversion, O1_stack_move);
-
- if (VerifyMethodHandles) {
- verify_argslots(_masm, O1_stack_move, O0_argslot, O2_scratch, O3_scratch, true,
- "copied argument(s) must fall within current frame");
- }
-
- // insert location is always the bottom of the argument list:
- __ neg(O1_stack_move);
- push_arg_slots(_masm, O0_argslot, O1_stack_move, O2_scratch, O3_scratch);
-
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_drop_args:
- {
- // 'argslot' is the position of the first argument to nuke.
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
-
- // 'stack_move' is number of words to drop.
- Register O1_stack_move = O1_scratch;
- load_stack_move(_masm, G3_amh_conversion, O1_stack_move);
-
- remove_arg_slots(_masm, O1_stack_move, O0_argslot, O2_scratch, O3_scratch, O4_scratch);
-
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- }
- break;
-
- case _adapter_collect_args:
- 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 FP 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 |
-
- // 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
- }
- if (extra_slots != 0) {
- __ sub(SP, round_to(extra_slots, 2) * Interpreter::stackElementSize, SP);
- }
- }
-
- // Set up Ricochet Frame.
- __ mov(SP, O5_savedSP); // record SP for the callee
-
- // One extra (empty) slot for outgoing target MH (see Gargs computation below).
- __ save_frame(2); // Note: we need to add 2 slots since frame::memory_parameter_word_sp_offset is 23.
-
- // Note: Gargs is live throughout the following, until we make our recursive call.
- // And the RF saves a copy in L4_saved_args_base.
-
- RicochetFrame::enter_ricochet_frame(_masm, G3_method_handle, Gargs,
- entry(ek_ret)->from_interpreted_entry());
-
- // Compute argument base:
- // Set up Gargs for current frame, extra (empty) slot is for outgoing target MH (space reserved by save_frame above).
- __ add(FP, STACK_BIAS - (1 * Interpreter::stackElementSize), Gargs);
-
- // Now pushed: ... keep1 | collect | keep2 | extra | [RF]
-
-#ifdef ASSERT
- if (VerifyMethodHandles && dest != T_CONFLICT) {
- BLOCK_COMMENT("verify AMH.conv.dest {");
- extract_conversion_dest_type(_masm, RicochetFrame::L5_conversion, O1_scratch);
- Label L_dest_ok;
- __ cmp(O1_scratch, (int) dest);
- __ br(Assembler::equal, false, Assembler::pt, L_dest_ok);
- __ delayed()->nop();
- if (dest == T_INT) {
- for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
- if (is_subword_type(BasicType(bt))) {
- __ cmp(O1_scratch, (int) bt);
- __ br(Assembler::equal, false, Assembler::pt, L_dest_ok);
- __ delayed()->nop();
- }
- }
- }
- __ stop("bad dest in AMH.conv");
- __ BIND(L_dest_ok);
- BLOCK_COMMENT("} verify AMH.conv.dest");
- }
-#endif //ASSERT
-
- // Find out where the original copy of the recursive argument sequence begins.
- Register O0_coll = O0_scratch;
- {
- RegisterOrConstant collect_slot = collect_slot_constant;
- if (collect_slot_constant == -1) {
- load_vmargslot(_masm, G3_amh_vmargslot, O1_scratch);
- collect_slot = O1_scratch;
- }
- // collect_slot might be 0, but we need the move anyway.
- __ add(RicochetFrame::L4_saved_args_base, __ argument_offset(collect_slot, collect_slot.register_or_noreg()), O0_coll);
- // O0_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(G3_amh_argument, G3_method_handle);
- if (VerifyMethodHandles) verify_method_handle(_masm, G3_method_handle, O1_scratch, O2_scratch);
-
- // Calculate |collect|, the number of arguments we are collecting.
- Register O1_collect_count = O1_scratch;
- RegisterOrConstant collect_count;
- if (collect_count_constant < 0) {
- __ load_method_handle_vmslots(O1_collect_count, G3_method_handle, O2_scratch);
- collect_count = O1_collect_count;
- } else {
- collect_count = collect_count_constant;
-#ifdef ASSERT
- if (VerifyMethodHandles) {
- BLOCK_COMMENT("verify collect_count_constant {");
- __ load_method_handle_vmslots(O3_scratch, G3_method_handle, O2_scratch);
- Label L_count_ok;
- __ cmp_and_br_short(O3_scratch, collect_count_constant, Assembler::equal, Assembler::pt, L_count_ok);
- __ stop("bad vminfo in AMH.conv");
- __ BIND(L_count_ok);
- BLOCK_COMMENT("} verify collect_count_constant");
- }
-#endif //ASSERT
- }
-
- // copy |collect| slots directly to TOS:
- push_arg_slots(_masm, O0_coll, collect_count, O2_scratch, O3_scratch);
- // Now pushed: ... keep1 | collect | keep2 | RF... | collect |
- // O0_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 original 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 O1_close_count = O1_collect_count;
- if (retain_original_args) {
- close_count = constant(0);
- } else if (collect_count_constant == -1) {
- close_count = O1_collect_count;
- }
-
- // How many slots need moving? This is simply dest_slot (0 => no |keep3|).
- RegisterOrConstant keep3_count;
- Register O2_keep3_count = O2_scratch;
- if (dest_slot_constant < 0) {
- extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O2_keep3_count);
- keep3_count = O2_keep3_count;
- } else {
- keep3_count = dest_slot_constant;
-#ifdef ASSERT
- if (VerifyMethodHandles && dest_slot_constant < 0) {
- BLOCK_COMMENT("verify dest_slot_constant {");
- extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O3_scratch);
- Label L_vminfo_ok;
- __ cmp_and_br_short(O3_scratch, dest_slot_constant, Assembler::equal, Assembler::pt, L_vminfo_ok);
- __ stop("bad vminfo in AMH.conv");
- __ BIND(L_vminfo_ok);
- BLOCK_COMMENT("} verify dest_slot_constant");
- }
-#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());
-
- // 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 to have everything in one register:
- if (close_count.is_register()) {
- // Deduct open_count from close_count register to get a clean +/- value.
- __ sub(close_count.as_register(), open_count, close_count.as_register());
- } else {
- close_count = close_count.as_constant() - open_count;
- }
- open_count = 0;
- }
- Register L4_old_argv = RicochetFrame::L4_saved_args_base;
- Register O3_new_argv = O3_scratch;
- if (fix_arg_base) {
- __ add(L4_old_argv, __ argument_offset(close_count, O4_scratch), O3_new_argv,
- -(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_register()) {
- __ cmp_and_br_short(keep3_count.as_register(), 0, Assembler::equal, Assembler::pn, L_done);
- }
- if (close_count.is_register()) {
- __ cmp_and_br_short(close_count.as_register(), open_count, Assembler::equal, Assembler::pn, 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) {
- __ cmp(close_count.as_register(), open_count);
- __ br(Assembler::greater, false, Assembler::pn, L_move_up);
- __ delayed()->nop();
- }
-
- 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++) {
- __ ld_ptr( Address(L4_old_argv, i * Interpreter::stackElementSize), O4_scratch);
- __ st_ptr(O4_scratch, Address(O3_new_argv, i * Interpreter::stackElementSize) );
- }
- } else {
- // Live: O1_close_count, O2_keep3_count, O3_new_argv
- Register argv_top = O0_scratch;
- __ add(L4_old_argv, __ argument_offset(keep3_count, O4_scratch), argv_top);
- move_arg_slots_down(_masm,
- Address(L4_old_argv, 0), // beginning of old argv
- argv_top, // end of old argv
- close_count, // distance to move down (must be negative)
- O4_scratch, G5_scratch);
- }
- }
-
- if (emit_guard) {
- __ ba_short(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--) {
- __ ld_ptr( Address(L4_old_argv, i * Interpreter::stackElementSize), O4_scratch);
- __ st_ptr(O4_scratch, Address(O3_new_argv, i * Interpreter::stackElementSize) );
- }
- } else {
- Address argv_top(L4_old_argv, __ argument_offset(keep3_count, O4_scratch));
- // Live: O1_close_count, O2_keep3_count, O3_new_argv
- move_arg_slots_up(_masm,
- L4_old_argv, // beginning of old argv
- argv_top, // end of old argv
- close_count, // distance to move up (must be positive)
- O4_scratch, G5_scratch);
- }
- }
- }
- __ BIND(L_done);
-
- if (fix_arg_base) {
- // adjust RF.saved_args_base
- __ mov(O3_new_argv, RicochetFrame::L4_saved_args_base);
- }
-
- if (stomp_dest) {
- // Stomp the return slot, so it doesn't hold garbage.
- // This isn't strictly necessary, but it may help detect bugs.
- __ set(RicochetFrame::RETURN_VALUE_PLACEHOLDER, O4_scratch);
- __ st_ptr(O4_scratch, Address(RicochetFrame::L4_saved_args_base,
- __ argument_offset(keep3_count, keep3_count.register_or_noreg()))); // uses O2_keep3_count
- }
- BLOCK_COMMENT("} adjust trailing arguments");
-
- BLOCK_COMMENT("do_recursive_call");
- __ mov(SP, O5_savedSP); // record SP for the callee
- __ set(ExternalAddress(SharedRuntime::ricochet_blob()->bounce_addr() - frame::pc_return_offset), O7);
- // 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(G3_method_handle, O1_scratch);
- }
- 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;
- }
- }
- }
- }
-
- // On entry to this continuation handler, make Gargs live again.
- __ mov(RicochetFrame::L4_saved_args_base, Gargs);
-
- Register O7_temp = O7;
- Register O5_vminfo = O5;
-
- RegisterOrConstant dest_slot = dest_slot_constant;
- if (dest_slot_constant == -1) {
- extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O5_vminfo);
- dest_slot = O5_vminfo;
- }
- // 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, __ argument_address(dest_slot, O7_temp));
-
- RicochetFrame::leave_ricochet_frame(_masm, G3_method_handle, I5_savedSP, I7);
-
- // Load the final target and go.
- if (VerifyMethodHandles) verify_method_handle(_masm, G3_method_handle, O0_scratch, O1_scratch);
- __ restore(I5_savedSP, G0, SP);
- __ jump_to_method_handle_entry(G3_method_handle, O0_scratch);
- __ illtrap(0);
- }
- break;
-
- case _adapter_opt_return_any:
- {
- Register O7_temp = O7;
- Register O5_dest_type = O5;
-
- if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
- extract_conversion_dest_type(_masm, RicochetFrame::L5_conversion, O5_dest_type);
- __ set(ExternalAddress((address) &_adapter_return_handlers[0]), O7_temp);
- __ sll_ptr(O5_dest_type, LogBytesPerWord, O5_dest_type);
- __ ld_ptr(O7_temp, O5_dest_type, O7_temp);
-
-#ifdef ASSERT
- { Label L_ok;
- __ br_notnull_short(O7_temp, Assembler::pt, L_ok);
- __ stop("bad method handle return");
- __ BIND(L_ok);
- }
-#endif //ASSERT
- __ JMP(O7_temp, 0);
- __ delayed()->nop();
- }
- break;
-
- case _adapter_opt_spread_0:
- 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 = 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
- load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot);
- __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot);
-
- // O0_argslot points both to the array and to the first output arg
- Address vmarg = Address(O0_argslot, 0);
-
- // Get the array value.
- Register O1_array = O1_scratch;
- Register O2_array_klass = O2_scratch;
- 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);
- __ ld_ptr(vmarg, O1_array);
-
- 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, G3_amh_conversion, O3_scratch);
- __ cmp_zero_and_br(Assembler::notZero, O3_scratch, L_skip);
- __ delayed()->nop(); // to avoid back-to-back cbcond instructions
- }
- __ br_null_short(O1_array, Assembler::pn, L_array_is_empty);
- __ BIND(L_skip);
- }
- __ null_check(O1_array, oopDesc::klass_offset_in_bytes());
- __ load_klass(O1_array, O2_array_klass);
-
- // Check the array type.
- Register O3_klass = O3_scratch;
- __ load_heap_oop(G3_amh_argument, O3_klass); // this is a Class object!
- load_klass_from_Class(_masm, O3_klass, O4_scratch, G5_scratch);
-
- Label L_ok_array_klass, L_bad_array_klass, L_bad_array_length;
- __ check_klass_subtype(O2_array_klass, O3_klass, O4_scratch, G5_scratch, L_ok_array_klass);
- // If we get here, the type check failed!
- __ ba_short(L_bad_array_klass);
- __ BIND(L_ok_array_klass);
-
- // Check length.
- if (length_constant >= 0) {
- __ ldsw(Address(O1_array, length_offset), O4_scratch);
- __ cmp(O4_scratch, length_constant);
- } else {
- Register O3_vminfo = O3_scratch;
- load_conversion_vminfo(_masm, G3_amh_conversion, O3_vminfo);
- __ ldsw(Address(O1_array, length_offset), O4_scratch);
- __ cmp(O3_vminfo, O4_scratch);
- }
- __ br(Assembler::notEqual, false, Assembler::pn, L_bad_array_length);
- __ delayed()->nop();
-
- Register O2_argslot_limit = O2_scratch;
-
- // Array length checks out. Now insert any required stack slots.
- if (length_constant == -1) {
- // Form a pointer to the end of the affected region.
- __ add(O0_argslot, Interpreter::stackElementSize, O2_argslot_limit);
- // 'stack_move' is negative number of words to insert
- // This number already accounts for elem_slots.
- Register O3_stack_move = O3_scratch;
- load_stack_move(_masm, G3_amh_conversion, O3_stack_move);
- __ cmp(O3_stack_move, 0);
- assert(stack_move_unit() < 0, "else change this comparison");
- __ br(Assembler::less, false, Assembler::pn, L_insert_arg_space);
- __ delayed()->nop();
- __ br(Assembler::equal, false, Assembler::pn, L_copy_args);
- __ delayed()->nop();
- // single argument case, with no array movement
- __ BIND(L_array_is_empty);
- remove_arg_slots(_masm, -stack_move_unit() * array_slots,
- O0_argslot, O1_scratch, O2_scratch, O3_scratch);
- __ ba_short(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)
- // Live: O1_array, O2_argslot_limit, O3_stack_move
- insert_arg_slots(_masm, O3_stack_move,
- O0_argslot, O4_scratch, G5_scratch, O1_scratch);
- // reload from rdx_argslot_limit since rax_argslot is now decremented
- __ ld_ptr(Address(O2_argslot_limit, -Interpreter::stackElementSize), O1_array);
- } else if (length_constant >= 1) {
- int new_slots = (length_constant * elem_slots) - array_slots;
- insert_arg_slots(_masm, new_slots * stack_move_unit(),
- O0_argslot, O2_scratch, O3_scratch, O4_scratch);
- } else if (length_constant == 0) {
- __ BIND(L_array_is_empty);
- remove_arg_slots(_masm, -stack_move_unit() * array_slots,
- O0_argslot, O1_scratch, O2_scratch, O3_scratch);
- } else {
- ShouldNotReachHere();
- }
-
- // Copy from the array to the new slots.
- // Note: Stack change code preserves integrity of O0_argslot pointer.
- // So even after slot insertions, O0_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) {
- // [O0_argslot, O2_argslot_limit) is the area we are inserting into.
- // Array element [0] goes at O0_argslot_limit[-wordSize].
- Register O1_source = O1_array;
- __ add(Address(O1_array, elem0_offset), O1_source);
- Register O4_fill_ptr = O4_scratch;
- __ mov(O2_argslot_limit, O4_fill_ptr);
- Label L_loop;
- __ BIND(L_loop);
- __ add(O4_fill_ptr, -Interpreter::stackElementSize * elem_slots, O4_fill_ptr);
- move_typed_arg(_masm, elem_type, true,
- Address(O1_source, 0), Address(O4_fill_ptr, 0),
- O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3)
- __ add(O1_source, type2aelembytes(elem_type), O1_source);
- __ cmp_and_brx_short(O4_fill_ptr, O0_argslot, Assembler::greaterUnsigned, Assembler::pt, L_loop);
- } else if (length_constant == 0) {
- // nothing to copy
- } else {
- int elem_offset = elem0_offset;
- int slot_offset = length_constant * Interpreter::stackElementSize;
- for (int index = 0; index < length_constant; index++) {
- slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward
- move_typed_arg(_masm, elem_type, true,
- Address(O1_array, elem_offset), Address(O0_argslot, slot_offset),
- O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3)
- elem_offset += type2aelembytes(elem_type);
- }
- }
- __ BIND(L_args_done);
-
- // Arguments are spread. Move to next method handle.
- __ load_heap_oop(G3_mh_vmtarget, G3_method_handle);
- __ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
-
- __ BIND(L_bad_array_klass);
- assert(!vmarg.uses(O2_required), "must be different registers");
- __ load_heap_oop(Address(O2_array_klass, java_mirror_offset), O2_required); // required class
- __ ld_ptr( vmarg, O1_actual); // bad object
- __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
- __ delayed()->mov(Bytecodes::_aaload, O0_code); // who is complaining?
-
- __ bind(L_bad_array_length);
- assert(!vmarg.uses(O2_required), "must be different registers");
- __ mov( G3_method_handle, O2_required); // required class
- __ ld_ptr(vmarg, O1_actual); // bad object
- __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
- __ delayed()->mov(Bytecodes::_arraylength, O0_code); // who is complaining?
- }
- break;
-
- default:
- DEBUG_ONLY(tty->print_cr("bad ek=%d (%s)", (int)ek, entry_name(ek)));
- ShouldNotReachHere();
- }
- BLOCK_COMMENT(err_msg("} Entry %s", entry_name(ek)));
-
- address me_cookie = MethodHandleEntry::start_compiled_entry(_masm, interp_entry);
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
-
- init_entry(ek, MethodHandleEntry::finish_compiled_entry(_masm, me_cookie));
-}
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -30,186 +30,9 @@
adapter_code_size = NOT_LP64(23000 DEBUG_ONLY(+ 40000)) LP64_ONLY(35000 DEBUG_ONLY(+ 50000))
};
-public:
-
-class RicochetFrame : public ResourceObj {
- friend class MethodHandles;
-
- private:
- /*
- RF field x86 SPARC
- sender_pc *(rsp+0) I7-0x8
- sender_link rbp I6+BIAS
- exact_sender_sp rsi/r13 I5_savedSP
- conversion *(rcx+&amh_conv) L5_conv
- saved_args_base rax L4_sab (cf. Gargs = G4)
- saved_args_layout #NULL L3_sal
- saved_target *(rcx+&mh_vmtgt) L2_stgt
- continuation #STUB_CON L1_cont
- */
- static const Register L1_continuation ; // what to do when control gets back here
- static const Register L2_saved_target ; // target method handle to invoke on saved_args
- static const Register L3_saved_args_layout; // caching point for MethodTypeForm.vmlayout cookie
- static const Register L4_saved_args_base ; // base of pushed arguments (slot 0, arg N) (-3)
- static const Register L5_conversion ; // misc. information from original AdapterMethodHandle (-2)
-
- frame _fr;
-
- RicochetFrame(const frame& fr) : _fr(fr) { }
-
- intptr_t* register_addr(Register reg) const {
- assert((_fr.sp() + reg->sp_offset_in_saved_window()) == _fr.register_addr(reg), "must agree");
- return _fr.register_addr(reg);
- }
- intptr_t register_value(Register reg) const { return *register_addr(reg); }
-
- public:
- intptr_t* continuation() const { return (intptr_t*) register_value(L1_continuation); }
- oop saved_target() const { return (oop) register_value(L2_saved_target); }
- oop saved_args_layout() const { return (oop) register_value(L3_saved_args_layout); }
- intptr_t* saved_args_base() const { return (intptr_t*) register_value(L4_saved_args_base); }
- intptr_t conversion() const { return register_value(L5_conversion); }
- intptr_t* exact_sender_sp() const { return (intptr_t*) register_value(I5_savedSP); }
- intptr_t* sender_link() const { return _fr.sender_sp(); } // XXX
- address sender_pc() const { return _fr.sender_pc(); }
-
- // This value is not used for much, but it apparently must be nonzero.
- static int frame_size_in_bytes() { return wordSize * 4; }
-
- 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*)register_addr(L2_saved_target); }
- oop* saved_args_layout_addr() { return (oop*)register_addr(L3_saved_args_layout); }
-
- oop compute_saved_args_layout(bool read_cache, bool write_cache);
-
-#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 const Register L0_magic_number_1 ; // cookie for debugging, at start of RSA
- static Address magic_number_2_addr() { return Address(L4_saved_args_base, -wordSize); }
- intptr_t magic_number_1() const { return register_value(L0_magic_number_1); }
- intptr_t magic_number_2() const { return saved_args_base()[-1]; }
-#endif //ASSERT
-
- public:
- enum { RETURN_VALUE_PLACEHOLDER = (NOT_DEBUG(0) DEBUG_ONLY(42)) };
-
- void verify() const NOT_DEBUG_RETURN; // check for MAGIC_NUMBER, etc.
-
- static void generate_ricochet_blob(MacroAssembler* _masm,
- // output params:
- int* bounce_offset,
- int* exception_offset,
- int* frame_size_in_words);
-
- static void enter_ricochet_frame(MacroAssembler* _masm,
- Register recv_reg,
- Register argv_reg,
- address return_handler);
-
- static void leave_ricochet_frame(MacroAssembler* _masm,
- Register recv_reg,
- Register new_sp_reg,
- Register sender_pc_reg);
-
- static RicochetFrame* from_frame(const frame& fr) {
- RicochetFrame* rf = new RicochetFrame(fr);
- rf->verify();
- return rf;
- }
-
- static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN;
-
- static void describe(const frame* fr, FrameValues& values, int frame_no) PRODUCT_RETURN;
-};
-
// Additional helper methods for MethodHandles code generation:
public:
static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg, Register temp_reg, Register temp2_reg);
- static void load_conversion_vminfo(MacroAssembler* _masm, Address conversion_field_addr, Register reg);
- static void extract_conversion_vminfo(MacroAssembler* _masm, Register conversion_field_reg, Register reg);
- static void extract_conversion_dest_type(MacroAssembler* _masm, Register conversion_field_reg, Register reg);
-
- static void load_stack_move(MacroAssembler* _masm,
- Address G3_amh_conversion,
- Register G5_stack_move);
-
- static void insert_arg_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register argslot_reg,
- Register temp_reg, Register temp2_reg, Register temp3_reg);
-
- static void remove_arg_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register argslot_reg,
- Register temp_reg, Register temp2_reg, Register temp3_reg);
-
- static void push_arg_slots(MacroAssembler* _masm,
- Register argslot_reg,
- RegisterOrConstant slot_count,
- Register temp_reg, Register temp2_reg);
-
- static void move_arg_slots_up(MacroAssembler* _masm,
- Register bottom_reg, // invariant
- Address top_addr, // can use temp_reg
- RegisterOrConstant positive_distance_in_slots,
- Register temp_reg, Register temp2_reg);
-
- static void move_arg_slots_down(MacroAssembler* _masm,
- Address bottom_addr, // can use temp_reg
- Register top_reg, // invariant
- RegisterOrConstant negative_distance_in_slots,
- Register temp_reg, Register temp2_reg);
-
- static void move_typed_arg(MacroAssembler* _masm,
- BasicType type, bool is_element,
- Address value_src, Address slot_dest,
- Register temp_reg);
-
- static void move_return_value(MacroAssembler* _masm, BasicType type,
- Address return_slot);
-
- static void verify_argslot(MacroAssembler* _masm, Register argslot_reg,
- Register temp_reg,
- const char* error_message) NOT_DEBUG_RETURN;
-
- static void verify_argslots(MacroAssembler* _masm,
- RegisterOrConstant argslot_count,
- Register argslot_reg,
- Register temp_reg,
- Register temp2_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_reg, KlassHandle klass,
@@ -223,8 +46,17 @@
"reference is a MH");
}
+ static void verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) NOT_DEBUG_RETURN;
+
// Similar to InterpreterMacroAssembler::jump_from_interpreted.
// Takes care of special dispatch from single stepping too.
- static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, Register temp2);
+ static void jump_from_method_handle(MacroAssembler* _masm, Register method,
+ Register temp, Register temp2,
+ bool for_compiler_entry);
+
+ static void jump_to_lambda_form(MacroAssembler* _masm,
+ Register recv, Register method_temp,
+ Register temp2, Register temp3,
+ bool for_compiler_entry);
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
--- a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -400,13 +400,13 @@
case T_LONG: // LP64, longs compete with int args
assert(sig_bt[i+1] == T_VOID, "");
#ifdef _LP64
- if (int_reg_cnt < int_reg_max) int_reg_cnt++;
+ if (int_reg_cnt < int_reg_max) int_reg_cnt++;
#endif
break;
case T_OBJECT:
case T_ARRAY:
case T_ADDRESS: // Used, e.g., in slow-path locking for the lock's stack address
- if (int_reg_cnt < int_reg_max) int_reg_cnt++;
+ if (int_reg_cnt < int_reg_max) int_reg_cnt++;
#ifndef _LP64
else stk_reg_pairs++;
#endif
@@ -416,11 +416,11 @@
case T_CHAR:
case T_BYTE:
case T_BOOLEAN:
- if (int_reg_cnt < int_reg_max) int_reg_cnt++;
+ if (int_reg_cnt < int_reg_max) int_reg_cnt++;
else stk_reg_pairs++;
break;
case T_FLOAT:
- if (flt_reg_cnt < flt_reg_max) flt_reg_cnt++;
+ if (flt_reg_cnt < flt_reg_max) flt_reg_cnt++;
else stk_reg_pairs++;
break;
case T_DOUBLE:
@@ -436,7 +436,6 @@
// This is where the longs/doubles start on the stack.
stk_reg_pairs = (stk_reg_pairs+1) & ~1; // Round
- int int_reg_pairs = (int_reg_cnt+1) & ~1; // 32-bit 2-reg longs only
int flt_reg_pairs = (flt_reg_cnt+1) & ~1;
// int stk_reg = frame::register_save_words*(wordSize>>2);
@@ -517,24 +516,15 @@
stk_reg_pairs += 2;
}
#else // COMPILER2
- if (int_reg_pairs + 1 < int_reg_max) {
- if (is_outgoing) {
- regs[i].set_pair(as_oRegister(int_reg_pairs + 1)->as_VMReg(), as_oRegister(int_reg_pairs)->as_VMReg());
- } else {
- regs[i].set_pair(as_iRegister(int_reg_pairs + 1)->as_VMReg(), as_iRegister(int_reg_pairs)->as_VMReg());
- }
- int_reg_pairs += 2;
- } else {
regs[i].set2(VMRegImpl::stack2reg(stk_reg_pairs));
stk_reg_pairs += 2;
- }
#endif // COMPILER2
#endif // _LP64
break;
case T_FLOAT:
if (flt_reg < flt_reg_max) regs[i].set1(as_FloatRegister(flt_reg++)->as_VMReg());
- else regs[i].set1( VMRegImpl::stack2reg(stk_reg++));
+ else regs[i].set1(VMRegImpl::stack2reg(stk_reg++));
break;
case T_DOUBLE:
assert(sig_bt[i+1] == T_VOID, "expecting half");
@@ -886,6 +876,20 @@
__ delayed()->add(SP, G1, Gargs);
}
+static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg, Register temp2_reg,
+ address code_start, address code_end,
+ Label& L_ok) {
+ Label L_fail;
+ __ set(ExternalAddress(code_start), temp_reg);
+ __ set(pointer_delta(code_end, code_start, 1), temp2_reg);
+ __ cmp(pc_reg, temp_reg);
+ __ brx(Assembler::lessEqualUnsigned, false, Assembler::pn, L_fail);
+ __ delayed()->add(temp_reg, temp2_reg, temp_reg);
+ __ cmp(pc_reg, temp_reg);
+ __ cmp_and_brx_short(pc_reg, temp_reg, Assembler::lessUnsigned, Assembler::pt, L_ok);
+ __ bind(L_fail);
+}
+
void AdapterGenerator::gen_i2c_adapter(
int total_args_passed,
// VMReg max_arg,
@@ -907,6 +911,51 @@
// This removes all sorts of headaches on the x86 side and also eliminates
// the possibility of having c2i -> i2c -> c2i -> ... endless transitions.
+ // More detail:
+ // Adapters can be frameless because they do not require the caller
+ // to perform additional cleanup work, such as correcting the stack pointer.
+ // An i2c adapter is frameless because the *caller* frame, which is interpreted,
+ // routinely repairs its own stack pointer (from interpreter_frame_last_sp),
+ // even if a callee has modified the stack pointer.
+ // A c2i adapter is frameless because the *callee* frame, which is interpreted,
+ // routinely repairs its caller's stack pointer (from sender_sp, which is set
+ // up via the senderSP register).
+ // In other words, if *either* the caller or callee is interpreted, we can
+ // get the stack pointer repaired after a call.
+ // This is why c2i and i2c adapters cannot be indefinitely composed.
+ // In particular, if a c2i adapter were to somehow call an i2c adapter,
+ // both caller and callee would be compiled methods, and neither would
+ // clean up the stack pointer changes performed by the two adapters.
+ // If this happens, control eventually transfers back to the compiled
+ // caller, but with an uncorrected stack, causing delayed havoc.
+
+ if (VerifyAdapterCalls &&
+ (Interpreter::code() != NULL || StubRoutines::code1() != NULL)) {
+ // So, let's test for cascading c2i/i2c adapters right now.
+ // assert(Interpreter::contains($return_addr) ||
+ // StubRoutines::contains($return_addr),
+ // "i2c adapter must return to an interpreter frame");
+ __ block_comment("verify_i2c { ");
+ Label L_ok;
+ if (Interpreter::code() != NULL)
+ range_check(masm, O7, O0, O1,
+ Interpreter::code()->code_start(), Interpreter::code()->code_end(),
+ L_ok);
+ if (StubRoutines::code1() != NULL)
+ range_check(masm, O7, O0, O1,
+ StubRoutines::code1()->code_begin(), StubRoutines::code1()->code_end(),
+ L_ok);
+ if (StubRoutines::code2() != NULL)
+ range_check(masm, O7, O0, O1,
+ StubRoutines::code2()->code_begin(), StubRoutines::code2()->code_end(),
+ L_ok);
+ const char* msg = "i2c adapter must return to an interpreter frame";
+ __ block_comment(msg);
+ __ stop(msg);
+ __ bind(L_ok);
+ __ block_comment("} verify_i2ce ");
+ }
+
// As you can see from the list of inputs & outputs there are not a lot
// of temp registers to work with: mostly G1, G3 & G4.
@@ -1937,20 +1986,156 @@
__ bind(done);
}
+static void verify_oop_args(MacroAssembler* masm,
+ int total_args_passed,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ Register temp_reg = G5_method; // not part of any compiled calling seq
+ if (VerifyOops) {
+ for (int i = 0; i < total_args_passed; i++) {
+ if (sig_bt[i] == T_OBJECT ||
+ sig_bt[i] == T_ARRAY) {
+ VMReg r = regs[i].first();
+ assert(r->is_valid(), "bad oop arg");
+ if (r->is_stack()) {
+ RegisterOrConstant ld_off = reg2offset(r) + STACK_BIAS;
+ ld_off = __ ensure_simm13_or_reg(ld_off, temp_reg);
+ __ ld_ptr(SP, ld_off, temp_reg);
+ __ verify_oop(temp_reg);
+ } else {
+ __ verify_oop(r->as_Register());
+ }
+ }
+ }
+ }
+}
+
+static void gen_special_dispatch(MacroAssembler* masm,
+ int total_args_passed,
+ int comp_args_on_stack,
+ vmIntrinsics::ID special_dispatch,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ verify_oop_args(masm, total_args_passed, sig_bt, regs);
+
+ // Now write the args into the outgoing interpreter space
+ bool has_receiver = false;
+ Register receiver_reg = noreg;
+ int member_arg_pos = -1;
+ Register member_reg = noreg;
+ int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(special_dispatch);
+ if (ref_kind != 0) {
+ member_arg_pos = total_args_passed - 1; // trailing MemberName argument
+ member_reg = G5_method; // known to be free at this point
+ has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind);
+ } else if (special_dispatch == vmIntrinsics::_invokeBasic) {
+ has_receiver = true;
+ } else {
+ fatal(err_msg("special_dispatch=%d", special_dispatch));
+ }
+
+ if (member_reg != noreg) {
+ // Load the member_arg into register, if necessary.
+ assert(member_arg_pos >= 0 && member_arg_pos < total_args_passed, "oob");
+ assert(sig_bt[member_arg_pos] == T_OBJECT, "dispatch argument must be an object");
+ VMReg r = regs[member_arg_pos].first();
+ assert(r->is_valid(), "bad member arg");
+ if (r->is_stack()) {
+ RegisterOrConstant ld_off = reg2offset(r) + STACK_BIAS;
+ ld_off = __ ensure_simm13_or_reg(ld_off, member_reg);
+ __ ld_ptr(SP, ld_off, member_reg);
+ } else {
+ // no data motion is needed
+ member_reg = r->as_Register();
+ }
+ }
+
+ if (has_receiver) {
+ // Make sure the receiver is loaded into a register.
+ assert(total_args_passed > 0, "oob");
+ assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object");
+ VMReg r = regs[0].first();
+ assert(r->is_valid(), "bad receiver arg");
+ if (r->is_stack()) {
+ // Porting note: This assumes that compiled calling conventions always
+ // pass the receiver oop in a register. If this is not true on some
+ // platform, pick a temp and load the receiver from stack.
+ assert(false, "receiver always in a register");
+ receiver_reg = G3_scratch; // known to be free at this point
+ RegisterOrConstant ld_off = reg2offset(r) + STACK_BIAS;
+ ld_off = __ ensure_simm13_or_reg(ld_off, member_reg);
+ __ ld_ptr(SP, ld_off, receiver_reg);
+ } else {
+ // no data motion is needed
+ receiver_reg = r->as_Register();
+ }
+ }
+
+ // Figure out which address we are really jumping to:
+ MethodHandles::generate_method_handle_dispatch(masm, special_dispatch,
+ receiver_reg, member_reg, /*for_compiler_entry:*/ true);
+}
+
// ---------------------------------------------------------------------------
// Generate a native wrapper for a given method. The method takes arguments
// in the Java compiled code convention, marshals them to the native
// convention (handlizes oops, etc), transitions to native, makes the call,
// returns to java state (possibly blocking), unhandlizes any result and
// returns.
+//
+// Critical native functions are a shorthand for the use of
+// GetPrimtiveArrayCritical and disallow the use of any other JNI
+// functions. The wrapper is expected to unpack the arguments before
+// passing them to the callee and perform checks before and after the
+// native call to ensure that they GC_locker
+// lock_critical/unlock_critical semantics are followed. Some other
+// parts of JNI setup are skipped like the tear down of the JNI handle
+// block and the check for pending exceptions it's impossible for them
+// to be thrown.
+//
+// They are roughly structured like this:
+// if (GC_locker::needs_gc())
+// SharedRuntime::block_for_jni_critical();
+// tranistion to thread_in_native
+// unpack arrray arguments and call native entry point
+// check for safepoint in progress
+// check if any thread suspend flags are set
+// call into JVM and possible unlock the JNI critical
+// if a GC was suppressed while in the critical native.
+// transition back to thread_in_Java
+// return to caller
+//
nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
methodHandle method,
int compile_id,
int total_in_args,
int comp_args_on_stack, // in VMRegStackSlots
- BasicType *in_sig_bt,
- VMRegPair *in_regs,
+ BasicType* in_sig_bt,
+ VMRegPair* in_regs,
BasicType ret_type) {
+ if (method->is_method_handle_intrinsic()) {
+ vmIntrinsics::ID iid = method->intrinsic_id();
+ intptr_t start = (intptr_t)__ pc();
+ int vep_offset = ((intptr_t)__ pc()) - start;
+ gen_special_dispatch(masm,
+ total_in_args,
+ comp_args_on_stack,
+ method->intrinsic_id(),
+ in_sig_bt,
+ in_regs);
+ int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period
+ __ flush();
+ int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually
+ return nmethod::new_native_nmethod(method,
+ compile_id,
+ masm->code(),
+ vep_offset,
+ frame_complete,
+ stack_slots / VMRegImpl::slots_per_word,
+ in_ByteSize(-1),
+ in_ByteSize(-1),
+ (OopMapSet*)NULL);
+ }
bool is_critical_native = true;
address native_func = method->critical_native_function();
if (native_func == NULL) {
--- a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3404,14 +3404,6 @@
StubRoutines::_atomic_add_ptr_entry = StubRoutines::_atomic_add_entry;
#endif // COMPILER2 !=> _LP64
- // Build this early so it's available for the interpreter. The
- // stub expects the required and actual type to already be in O1
- // and O2 respectively.
- StubRoutines::_throw_WrongMethodTypeException_entry =
- generate_throw_exception("WrongMethodTypeException throw_exception",
- CAST_FROM_FN_PTR(address, SharedRuntime::throw_WrongMethodTypeException),
- G5_method_type, G3_method_handle);
-
// Build this early so it's available for the interpreter.
StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError));
}
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -694,9 +694,9 @@
// Need to differentiate between igetfield, agetfield, bgetfield etc.
// because they are different sizes.
// Get the type from the constant pool cache
- __ srl(G1_scratch, ConstantPoolCacheEntry::tosBits, G1_scratch);
- // Make sure we don't need to mask G1_scratch for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(G1_scratch, ConstantPoolCacheEntry::tos_state_shift, G1_scratch);
+ // Make sure we don't need to mask G1_scratch after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmp(G1_scratch, atos );
__ br(Assembler::equal, true, Assembler::pt, xreturn_path);
__ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i);
@@ -1662,7 +1662,7 @@
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_compiled_frame() || caller->is_entry_frame() || caller->is_ricochet_frame(), "only possible cases");
+ 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.
//
--- a/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -378,7 +378,7 @@
Register Rcache = G3_scratch;
Register Rscratch = G4_scratch;
- resolve_cache_and_index(f1_oop, Otos_i, Rcache, Rscratch, wide ? sizeof(u2) : sizeof(u1));
+ resolve_cache_and_index(f12_oop, Otos_i, Rcache, Rscratch, wide ? sizeof(u2) : sizeof(u1));
__ verify_oop(Otos_i);
@@ -2093,10 +2093,12 @@
// Depends on cpCacheOop layout!
Label resolved;
- if (byte_no == f1_oop) {
- // We are resolved if the f1 field contains a non-null object (CallSite, etc.)
- // This kind of CP cache entry does not need to match the flags byte, because
+ if (byte_no == f12_oop) {
+ // We are resolved if the f1 field contains a non-null object (CallSite, MethodType, etc.)
+ // This kind of CP cache entry does not need to match bytecode_1 or bytecode_2, because
// there is a 1-1 relation between bytecode type and CP entry type.
+ // The caller will also load a methodOop from f2.
+ assert(result != noreg, "");
assert_different_registers(result, Rcache);
__ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
__ ld_ptr(Rcache, constantPoolCacheOopDesc::base_offset() +
@@ -2123,10 +2125,13 @@
case Bytecodes::_invokespecial : // fall through
case Bytecodes::_invokestatic : // fall through
case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break;
+ case Bytecodes::_invokehandle : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); break;
case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break;
case Bytecodes::_fast_aldc : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
case Bytecodes::_fast_aldc_w : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
- default : ShouldNotReachHere(); break;
+ default:
+ fatal(err_msg("unexpected bytecode: %s", Bytecodes::name(bytecode())));
+ break;
}
// first time invocation - must resolve first
__ call_VM(noreg, entry, O1);
@@ -2139,48 +2144,54 @@
}
void TemplateTable::load_invoke_cp_cache_entry(int byte_no,
- Register Rmethod,
- Register Ritable_index,
- Register Rflags,
+ Register method,
+ Register itable_index,
+ Register flags,
bool is_invokevirtual,
bool is_invokevfinal,
bool is_invokedynamic) {
// Uses both G3_scratch and G4_scratch
- Register Rcache = G3_scratch;
- Register Rscratch = G4_scratch;
- assert_different_registers(Rcache, Rmethod, Ritable_index);
-
- ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset();
+ Register cache = G3_scratch;
+ Register index = G4_scratch;
+ assert_different_registers(cache, method, itable_index);
// determine constant pool cache field offsets
+ assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
const int method_offset = in_bytes(
- cp_base_offset +
- (is_invokevirtual
+ constantPoolCacheOopDesc::base_offset() +
+ ((byte_no == f2_byte)
? ConstantPoolCacheEntry::f2_offset()
: ConstantPoolCacheEntry::f1_offset()
)
);
- const int flags_offset = in_bytes(cp_base_offset +
+ const int flags_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
ConstantPoolCacheEntry::flags_offset());
// access constant pool cache fields
- const int index_offset = in_bytes(cp_base_offset +
+ const int index_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
ConstantPoolCacheEntry::f2_offset());
if (is_invokevfinal) {
- __ get_cache_and_index_at_bcp(Rcache, Rscratch, 1);
- __ ld_ptr(Rcache, method_offset, Rmethod);
- } else if (byte_no == f1_oop) {
- // Resolved f1_oop goes directly into 'method' register.
- resolve_cache_and_index(byte_no, Rmethod, Rcache, Rscratch, sizeof(u4));
+ __ get_cache_and_index_at_bcp(cache, index, 1);
+ __ ld_ptr(Address(cache, method_offset), method);
+ } else if (byte_no == f12_oop) {
+ // Resolved f1_oop (CallSite, MethodType, etc.) goes into 'itable_index'.
+ // Resolved f2_oop (methodOop invoker) will go into 'method' (at index_offset).
+ // See ConstantPoolCacheEntry::set_dynamic_call and set_method_handle.
+ size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
+ resolve_cache_and_index(byte_no, itable_index, cache, index, index_size);
+ __ ld_ptr(Address(cache, index_offset), method);
+ itable_index = noreg; // hack to disable load below
} else {
- resolve_cache_and_index(byte_no, noreg, Rcache, Rscratch, sizeof(u2));
- __ ld_ptr(Rcache, method_offset, Rmethod);
+ resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
+ __ ld_ptr(Address(cache, method_offset), method);
}
- if (Ritable_index != noreg) {
- __ ld_ptr(Rcache, index_offset, Ritable_index);
+ if (itable_index != noreg) {
+ // pick up itable index from f2 also:
+ assert(byte_no == f1_byte, "already picked up f1");
+ __ ld_ptr(Address(cache, index_offset), itable_index);
}
- __ ld_ptr(Rcache, flags_offset, Rflags);
+ __ ld_ptr(Address(cache, flags_offset), flags);
}
// The Rcache register must be set before call
@@ -2272,7 +2283,7 @@
if (__ membar_has_effect(membar_bits)) {
// Get volatile flag
- __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_volatile_shift), Lscratch);
__ and3(Rflags, Lscratch, Lscratch);
}
@@ -2280,9 +2291,9 @@
// compute field type
Label notByte, notInt, notShort, notChar, notLong, notFloat, notObj;
- __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags);
- // Make sure we don't need to mask Rflags for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(Rflags, ConstantPoolCacheEntry::tos_state_shift, Rflags);
+ // Make sure we don't need to mask Rflags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
// Check atos before itos for getstatic, more likely (in Queens at least)
__ cmp(Rflags, atos);
@@ -2445,7 +2456,7 @@
if (__ membar_has_effect(membar_bits)) {
// Get volatile flag
__ ld_ptr(Rcache, cp_base_offset + ConstantPoolCacheEntry::f2_offset(), Rflags);
- __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_volatile_shift), Lscratch);
}
switch (bytecode()) {
@@ -2569,9 +2580,9 @@
Label two_word, valsizeknown;
__ ld_ptr(G1_scratch, cp_base_offset + ConstantPoolCacheEntry::flags_offset(), Rflags);
__ mov(Lesp, G4_scratch);
- __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags);
- // Make sure we don't need to mask Rflags for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(Rflags, ConstantPoolCacheEntry::tos_state_shift, Rflags);
+ // Make sure we don't need to mask Rflags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmp(Rflags, ltos);
__ br(Assembler::equal, false, Assembler::pt, two_word);
__ delayed()->cmp(Rflags, dtos);
@@ -2625,7 +2636,7 @@
Label notVolatile, checkVolatile, exit;
if (__ membar_has_effect(read_bits) || __ membar_has_effect(write_bits)) {
- __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_volatile_shift), Lscratch);
__ and3(Rflags, Lscratch, Lscratch);
if (__ membar_has_effect(read_bits)) {
@@ -2635,9 +2646,9 @@
}
}
- __ srl(Rflags, ConstantPoolCacheEntry::tosBits, Rflags);
- // Make sure we don't need to mask Rflags for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(Rflags, ConstantPoolCacheEntry::tos_state_shift, Rflags);
+ // Make sure we don't need to mask Rflags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
// compute field type
Label notInt, notShort, notChar, notObj, notByte, notLong, notFloat;
@@ -2833,7 +2844,7 @@
Label notVolatile, checkVolatile, exit;
if (__ membar_has_effect(read_bits) || __ membar_has_effect(write_bits)) {
__ ld_ptr(Rcache, cp_base_offset + ConstantPoolCacheEntry::flags_offset(), Rflags);
- __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_volatile_shift), Lscratch);
__ and3(Rflags, Lscratch, Lscratch);
if (__ membar_has_effect(read_bits)) {
__ cmp_and_br_short(Lscratch, 0, Assembler::equal, Assembler::pt, notVolatile);
@@ -2916,7 +2927,7 @@
// Test volatile
Label notVolatile;
- __ set((1 << ConstantPoolCacheEntry::volatileField), Lscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_volatile_shift), Lscratch);
__ btst(Rflags, Lscratch);
__ br(Assembler::zero, false, Assembler::pt, notVolatile);
__ delayed()->nop();
@@ -2936,27 +2947,82 @@
ShouldNotReachHere();
}
+
+void TemplateTable::prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register ra, // return address
+ Register index, // itable index, MethodType, etc.
+ Register recv, // if caller wants to see it
+ Register flags // if caller wants to test it
+ ) {
+ // determine flags
+ const Bytecodes::Code code = bytecode();
+ const bool is_invokeinterface = code == Bytecodes::_invokeinterface;
+ const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
+ const bool is_invokehandle = code == Bytecodes::_invokehandle;
+ const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
+ const bool is_invokespecial = code == Bytecodes::_invokespecial;
+ const bool load_receiver = (recv != noreg);
+ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), "");
+ assert(recv == noreg || recv == O0, "");
+ assert(flags == noreg || flags == O1, "");
+
+ // setup registers & access constant pool cache
+ if (recv == noreg) recv = O0;
+ if (flags == noreg) flags = O1;
+ const Register temp = O2;
+ assert_different_registers(method, ra, index, recv, flags, temp);
+
+ load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
+
+ __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
+
+ // maybe push appendix to arguments
+ if (is_invokedynamic || is_invokehandle) {
+ Label L_no_push;
+ __ verify_oop(index);
+ __ set((1 << ConstantPoolCacheEntry::has_appendix_shift), temp);
+ __ btst(flags, temp);
+ __ br(Assembler::zero, false, Assembler::pt, L_no_push);
+ __ delayed()->nop();
+ // Push the appendix as a trailing parameter.
+ // This must be done before we get the receiver,
+ // since the parameter_size includes it.
+ __ push_ptr(index); // push appendix (MethodType, CallSite, etc.)
+ __ bind(L_no_push);
+ }
+
+ // load receiver if needed (after appendix is pushed so parameter size is correct)
+ if (load_receiver) {
+ __ and3(flags, ConstantPoolCacheEntry::parameter_size_mask, temp); // get parameter size
+ __ load_receiver(temp, recv); // __ argument_address uses Gargs but we need Lesp
+ __ verify_oop(recv);
+ }
+
+ // compute return type
+ __ srl(flags, ConstantPoolCacheEntry::tos_state_shift, ra);
+ // Make sure we don't need to mask flags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
+ // load return address
+ {
+ const address table_addr = (is_invokeinterface || is_invokedynamic) ?
+ (address)Interpreter::return_5_addrs_by_index_table() :
+ (address)Interpreter::return_3_addrs_by_index_table();
+ AddressLiteral table(table_addr);
+ __ set(table, temp);
+ __ sll(ra, LogBytesPerWord, ra);
+ __ ld_ptr(Address(temp, ra), ra);
+ }
+}
+
+
void TemplateTable::generate_vtable_call(Register Rrecv, Register Rindex, Register Rret) {
Register Rtemp = G4_scratch;
Register Rcall = Rindex;
assert_different_registers(Rcall, G5_method, Gargs, Rret);
// get target methodOop & entry point
- const int base = instanceKlass::vtable_start_offset() * wordSize;
- if (vtableEntry::size() % 3 == 0) {
- // scale the vtable index by 12:
- int one_third = vtableEntry::size() / 3;
- __ sll(Rindex, exact_log2(one_third * 1 * wordSize), Rtemp);
- __ sll(Rindex, exact_log2(one_third * 2 * wordSize), Rindex);
- __ add(Rindex, Rtemp, Rindex);
- } else {
- // scale the vtable index by 8:
- __ sll(Rindex, exact_log2(vtableEntry::size() * wordSize), Rindex);
- }
-
- __ add(Rrecv, Rindex, Rrecv);
- __ ld_ptr(Rrecv, base + vtableEntry::method_offset_in_bytes(), G5_method);
-
+ __ lookup_virtual_method(Rrecv, Rindex, G5_method);
__ call_from_interpreter(Rcall, Gargs, Rret);
}
@@ -2965,16 +3031,16 @@
assert(byte_no == f2_byte, "use this argument");
Register Rscratch = G3_scratch;
- Register Rtemp = G4_scratch;
- Register Rret = Lscratch;
- Register Rrecv = G5_method;
+ Register Rtemp = G4_scratch;
+ Register Rret = Lscratch;
+ Register O0_recv = O0;
Label notFinal;
load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, true, false, false);
__ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
// Check for vfinal
- __ set((1 << ConstantPoolCacheEntry::vfinalMethod), G4_scratch);
+ __ set((1 << ConstantPoolCacheEntry::is_vfinal_shift), G4_scratch);
__ btst(Rret, G4_scratch);
__ br(Assembler::zero, false, Assembler::pt, notFinal);
__ delayed()->and3(Rret, 0xFF, G4_scratch); // gets number of parameters
@@ -2986,27 +3052,27 @@
__ bind(notFinal);
__ mov(G5_method, Rscratch); // better scratch register
- __ load_receiver(G4_scratch, O0); // gets receiverOop
- // receiver is in O0
- __ verify_oop(O0);
+ __ load_receiver(G4_scratch, O0_recv); // gets receiverOop
+ // receiver is in O0_recv
+ __ verify_oop(O0_recv);
// get return address
AddressLiteral table(Interpreter::return_3_addrs_by_index_table());
__ set(table, Rtemp);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(Rret, ConstantPoolCacheEntry::tos_state_shift, Rret); // get return type
+ // Make sure we don't need to mask Rret after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ sll(Rret, LogBytesPerWord, Rret);
__ ld_ptr(Rtemp, Rret, Rret); // get return address
// get receiver klass
- __ null_check(O0, oopDesc::klass_offset_in_bytes());
- __ load_klass(O0, Rrecv);
- __ verify_oop(Rrecv);
-
- __ profile_virtual_call(Rrecv, O4);
-
- generate_vtable_call(Rrecv, Rscratch, Rret);
+ __ null_check(O0_recv, oopDesc::klass_offset_in_bytes());
+ __ load_klass(O0_recv, O0_recv);
+ __ verify_oop(O0_recv);
+
+ __ profile_virtual_call(O0_recv, O4);
+
+ generate_vtable_call(O0_recv, Rscratch, Rret);
}
void TemplateTable::fast_invokevfinal(int byte_no) {
@@ -3036,9 +3102,9 @@
// get return address
AddressLiteral table(Interpreter::return_3_addrs_by_index_table());
__ set(table, Rtemp);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ srl(Rret, ConstantPoolCacheEntry::tos_state_shift, Rret); // get return type
+ // Make sure we don't need to mask Rret after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ sll(Rret, LogBytesPerWord, Rret);
__ ld_ptr(Rtemp, Rret, Rret); // get return address
@@ -3047,65 +3113,37 @@
__ call_from_interpreter(Rscratch, Gargs, Rret);
}
+
void TemplateTable::invokespecial(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- Register Rscratch = G3_scratch;
- Register Rtemp = G4_scratch;
- Register Rret = Lscratch;
-
- load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, /*virtual*/ false, false, false);
- __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
-
- __ verify_oop(G5_method);
-
- __ lduh(G5_method, in_bytes(methodOopDesc::size_of_parameters_offset()), G4_scratch);
- __ load_receiver(G4_scratch, O0);
-
- // receiver NULL check
- __ null_check(O0);
-
- __ profile_call(O4);
-
- // get return address
- AddressLiteral table(Interpreter::return_3_addrs_by_index_table());
- __ set(table, Rtemp);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
- __ sll(Rret, LogBytesPerWord, Rret);
- __ ld_ptr(Rtemp, Rret, Rret); // get return address
+ const Register Rret = Lscratch;
+ const Register O0_recv = O0;
+ const Register Rscratch = G3_scratch;
+
+ prepare_invoke(byte_no, G5_method, Rret, noreg, O0_recv); // get receiver also for null check
+ __ null_check(O0_recv);
// do the call
+ __ verify_oop(G5_method);
+ __ profile_call(O4);
__ call_from_interpreter(Rscratch, Gargs, Rret);
}
+
void TemplateTable::invokestatic(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- Register Rscratch = G3_scratch;
- Register Rtemp = G4_scratch;
- Register Rret = Lscratch;
-
- load_invoke_cp_cache_entry(byte_no, G5_method, noreg, Rret, /*virtual*/ false, false, false);
- __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
-
- __ verify_oop(G5_method);
-
- __ profile_call(O4);
-
- // get return address
- AddressLiteral table(Interpreter::return_3_addrs_by_index_table());
- __ set(table, Rtemp);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
- __ sll(Rret, LogBytesPerWord, Rret);
- __ ld_ptr(Rtemp, Rret, Rret); // get return address
+ const Register Rret = Lscratch;
+ const Register Rscratch = G3_scratch;
+
+ prepare_invoke(byte_no, G5_method, Rret); // get f1 methodOop
// do the call
+ __ verify_oop(G5_method);
+ __ profile_call(O4);
__ call_from_interpreter(Rscratch, Gargs, Rret);
}
@@ -3122,7 +3160,7 @@
Label notFinal;
// Check for vfinal
- __ set((1 << ConstantPoolCacheEntry::vfinalMethod), Rscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_vfinal_shift), Rscratch);
__ btst(Rflags, Rscratch);
__ br(Assembler::zero, false, Assembler::pt, notFinal);
__ delayed()->nop();
@@ -3144,53 +3182,37 @@
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- Register Rscratch = G4_scratch;
- Register Rret = G3_scratch;
- Register Rindex = Lscratch;
- Register Rinterface = G1_scratch;
- Register RklassOop = G5_method;
- Register Rflags = O1;
+ const Register Rinterface = G1_scratch;
+ const Register Rret = G3_scratch;
+ const Register Rindex = Lscratch;
+ const Register O0_recv = O0;
+ const Register O1_flags = O1;
+ const Register O2_klassOop = O2;
+ const Register Rscratch = G4_scratch;
assert_different_registers(Rscratch, G5_method);
- load_invoke_cp_cache_entry(byte_no, Rinterface, Rindex, Rflags, /*virtual*/ false, false, false);
- __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
-
- // get receiver
- __ and3(Rflags, 0xFF, Rscratch); // gets number of parameters
- __ load_receiver(Rscratch, O0);
- __ verify_oop(O0);
-
- __ mov(Rflags, Rret);
-
- // get return address
- AddressLiteral table(Interpreter::return_5_addrs_by_index_table());
- __ set(table, Rscratch);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
- __ sll(Rret, LogBytesPerWord, Rret);
- __ ld_ptr(Rscratch, Rret, Rret); // get return address
+ prepare_invoke(byte_no, Rinterface, Rret, Rindex, O0_recv, O1_flags);
// get receiver klass
- __ null_check(O0, oopDesc::klass_offset_in_bytes());
- __ load_klass(O0, RklassOop);
- __ verify_oop(RklassOop);
+ __ null_check(O0_recv, oopDesc::klass_offset_in_bytes());
+ __ load_klass(O0_recv, O2_klassOop);
+ __ verify_oop(O2_klassOop);
// Special case of invokeinterface called for virtual method of
// java.lang.Object. See cpCacheOop.cpp for details.
// This code isn't produced by javac, but could be produced by
// another compliant java compiler.
Label notMethod;
- __ set((1 << ConstantPoolCacheEntry::methodInterface), Rscratch);
- __ btst(Rflags, Rscratch);
+ __ set((1 << ConstantPoolCacheEntry::is_forced_virtual_shift), Rscratch);
+ __ btst(O1_flags, Rscratch);
__ br(Assembler::zero, false, Assembler::pt, notMethod);
__ delayed()->nop();
- invokeinterface_object_method(RklassOop, Rinterface, Rret, Rflags);
+ invokeinterface_object_method(O2_klassOop, Rinterface, Rret, O1_flags);
__ bind(notMethod);
- __ profile_virtual_call(RklassOop, O4);
+ __ profile_virtual_call(O2_klassOop, O4);
//
// find entry point to call
@@ -3199,9 +3221,9 @@
// compute start of first itableOffsetEntry (which is at end of vtable)
const int base = instanceKlass::vtable_start_offset() * wordSize;
Label search;
- Register Rtemp = Rflags;
-
- __ ld(RklassOop, instanceKlass::vtable_length_offset() * wordSize, Rtemp);
+ Register Rtemp = O1_flags;
+
+ __ ld(O2_klassOop, instanceKlass::vtable_length_offset() * wordSize, Rtemp);
if (align_object_offset(1) > 1) {
__ round_to(Rtemp, align_object_offset(1));
}
@@ -3212,7 +3234,7 @@
__ set(base, Rscratch);
__ add(Rscratch, Rtemp, Rtemp);
}
- __ add(RklassOop, Rtemp, Rscratch);
+ __ add(O2_klassOop, Rtemp, Rscratch);
__ bind(search);
@@ -3244,7 +3266,7 @@
assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust instruction below");
__ sll(Rindex, exact_log2(itableMethodEntry::size() * wordSize), Rindex); // Rindex *= 8;
__ add(Rscratch, Rindex, Rscratch);
- __ ld_ptr(RklassOop, Rscratch, G5_method);
+ __ ld_ptr(O2_klassOop, Rscratch, G5_method);
// Check for abstract method error.
{
@@ -3260,13 +3282,42 @@
__ verify_oop(G5_method);
__ call_from_interpreter(Rcall, Gargs, Rret);
-
+}
+
+
+void TemplateTable::invokehandle(int byte_no) {
+ transition(vtos, vtos);
+ assert(byte_no == f12_oop, "use this argument");
+
+ if (!EnableInvokeDynamic) {
+ // rewriter does not generate this bytecode
+ __ should_not_reach_here();
+ return;
+ }
+
+ const Register Rret = Lscratch;
+ const Register G4_mtype = G4_scratch; // f1
+ const Register O0_recv = O0;
+ const Register Rscratch = G3_scratch;
+
+ prepare_invoke(byte_no, G5_method, Rret, G4_mtype, O0_recv);
+ __ null_check(O0_recv);
+
+ // G4: MethodType object (from f1)
+ // G5: MH.linkToCallSite method (from f2)
+
+ // Note: G4_mtype is already pushed (if necessary) by prepare_invoke
+
+ // do the call
+ __ verify_oop(G5_method);
+ __ profile_final_call(O4); // FIXME: profile the LambdaForm also
+ __ call_from_interpreter(Rscratch, Gargs, Rret);
}
void TemplateTable::invokedynamic(int byte_no) {
transition(vtos, vtos);
- assert(byte_no == f1_oop, "use this argument");
+ assert(byte_no == f12_oop, "use this argument");
if (!EnableInvokeDynamic) {
// We should not encounter this bytecode if !EnableInvokeDynamic.
@@ -3279,42 +3330,24 @@
return;
}
- // G5: CallSite object (f1)
- // XX: unused (f2)
- // XX: flags (unused)
-
- Register G5_callsite = G5_method;
- Register Rscratch = G3_scratch;
- Register Rtemp = G1_scratch;
- Register Rret = Lscratch;
-
- load_invoke_cp_cache_entry(byte_no, G5_callsite, noreg, Rret,
- /*virtual*/ false, /*vfinal*/ false, /*indy*/ true);
- __ mov(SP, O5_savedSP); // record SP that we wanted the callee to restore
-
+ const Register Rret = Lscratch;
+ const Register G4_callsite = G4_scratch;
+ const Register Rscratch = G3_scratch;
+
+ prepare_invoke(byte_no, G5_method, Rret, G4_callsite);
+
+ // G4: CallSite object (from f1)
+ // G5: MH.linkToCallSite method (from f2)
+
+ // Note: G4_callsite is already pushed by prepare_invoke
+
+ // %%% should make a type profile for any invokedynamic that takes a ref argument
// profile this call
__ profile_call(O4);
- // get return address
- AddressLiteral table(Interpreter::return_5_addrs_by_index_table());
- __ set(table, Rtemp);
- __ srl(Rret, ConstantPoolCacheEntry::tosBits, Rret); // get return type
- // Make sure we don't need to mask Rret for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
- __ sll(Rret, LogBytesPerWord, Rret);
- __ ld_ptr(Rtemp, Rret, Rret); // get return address
-
- __ verify_oop(G5_callsite);
- __ load_heap_oop(G5_callsite, __ delayed_value(java_lang_invoke_CallSite::target_offset_in_bytes, Rscratch), G3_method_handle);
- __ null_check(G3_method_handle);
- __ verify_oop(G3_method_handle);
-
- // Adjust Rret first so Llast_SP can be same as Rret
- __ add(Rret, -frame::pc_return_offset, O7);
- __ add(Lesp, BytesPerWord, Gargs); // setup parameter pointer
- __ jump_to_method_handle_entry(G3_method_handle, Rtemp, /* emit_delayed_nop */ false);
- // Record SP so we can remove any stack space allocated by adapter transition
- __ delayed()->mov(SP, Llast_SP);
+ // do the call
+ __ verify_oop(G5_method);
+ __ call_from_interpreter(Rscratch, Gargs, Rret);
}
--- a/hotspot/src/cpu/sparc/vm/templateTable_sparc.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -25,6 +25,13 @@
#ifndef CPU_SPARC_VM_TEMPLATETABLE_SPARC_HPP
#define CPU_SPARC_VM_TEMPLATETABLE_SPARC_HPP
+ static void prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register ra, // return address
+ Register index = noreg, // itable index, MethodType, etc.
+ Register recv = noreg, // if caller wants to see it
+ Register flags = noreg // if caller wants to test it
+ );
// helper function
static void invokevfinal_helper(Register Rcache, Register Rret);
static void invokeinterface_object_method(Register RklassOop, Register Rcall,
--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -44,10 +44,11 @@
fmaf_instructions = 10,
fmau_instructions = 11,
vis3_instructions = 12,
- sparc64_family = 13,
- T_family = 14,
- T1_model = 15,
- cbcond_instructions = 16
+ cbcond_instructions = 13,
+ sparc64_family = 14,
+ M_family = 15,
+ T_family = 16,
+ T1_model = 17
};
enum Feature_Flag_Set {
@@ -67,10 +68,11 @@
fmaf_instructions_m = 1 << fmaf_instructions,
fmau_instructions_m = 1 << fmau_instructions,
vis3_instructions_m = 1 << vis3_instructions,
+ cbcond_instructions_m = 1 << cbcond_instructions,
sparc64_family_m = 1 << sparc64_family,
+ M_family_m = 1 << M_family,
T_family_m = 1 << T_family,
T1_model_m = 1 << T1_model,
- cbcond_instructions_m = 1 << cbcond_instructions,
generic_v8_m = v8_instructions_m | hardware_mul32_m | hardware_div32_m | hardware_fsmuld_m,
generic_v9_m = generic_v8_m | v9_instructions_m,
@@ -89,6 +91,7 @@
static int platform_features(int features);
// Returns true if the platform is in the niagara line (T series)
+ static bool is_M_family(int features) { return (features & M_family_m) != 0; }
static bool is_T_family(int features) { return (features & T_family_m) != 0; }
static bool is_niagara() { return is_T_family(_features); }
DEBUG_ONLY( static bool is_niagara(int features) { return (features & sun4v_m) != 0; } )
--- a/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -70,7 +70,6 @@
__ load_klass(O0, G3_scratch);
// set methodOop (in case of interpreted method), and destination address
- int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size();
#ifndef PRODUCT
if (DebugVtables) {
Label L;
@@ -82,13 +81,8 @@
__ bind(L);
}
#endif
- int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes();
- if (Assembler::is_simm13(v_off)) {
- __ ld_ptr(G3, v_off, G5_method);
- } else {
- __ set(v_off,G5);
- __ ld_ptr(G3, G5, G5_method);
- }
+
+ __ lookup_virtual_method(G3_scratch, vtable_index, G5_method);
#ifndef PRODUCT
if (DebugVtables) {
--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -41,6 +41,15 @@
#include "gc_implementation/g1/heapRegion.hpp"
#endif
+#ifdef PRODUCT
+#define BLOCK_COMMENT(str) /* nothing */
+#define STOP(error) stop(error)
+#else
+#define BLOCK_COMMENT(str) block_comment(str)
+#define STOP(error) block_comment(error); stop(error)
+#endif
+
+#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
// Implementation of AddressLiteral
AddressLiteral::AddressLiteral(address target, relocInfo::relocType rtype) {
@@ -5508,23 +5517,7 @@
// To see where a verify_oop failed, get $ebx+40/X for this frame.
// This is the value of eip which points to where verify_oop will return.
if (os::message_box(msg, "Execution stopped, print registers?")) {
- ttyLocker ttyl;
- tty->print_cr("eip = 0x%08x", eip);
-#ifndef PRODUCT
- if ((WizardMode || Verbose) && PrintMiscellaneous) {
- tty->cr();
- findpc(eip);
- tty->cr();
- }
-#endif
- tty->print_cr("rax = 0x%08x", rax);
- tty->print_cr("rbx = 0x%08x", rbx);
- tty->print_cr("rcx = 0x%08x", rcx);
- tty->print_cr("rdx = 0x%08x", rdx);
- tty->print_cr("rdi = 0x%08x", rdi);
- tty->print_cr("rsi = 0x%08x", rsi);
- tty->print_cr("rbp = 0x%08x", rbp);
- tty->print_cr("rsp = 0x%08x", rsp);
+ print_state32(rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax, eip);
BREAKPOINT;
assert(false, "start up GDB");
}
@@ -5536,12 +5529,53 @@
ThreadStateTransition::transition(thread, _thread_in_vm, saved_state);
}
+void MacroAssembler::print_state32(int rdi, int rsi, int rbp, int rsp, int rbx, int rdx, int rcx, int rax, int eip) {
+ ttyLocker ttyl;
+ FlagSetting fs(Debugging, true);
+ tty->print_cr("eip = 0x%08x", eip);
+#ifndef PRODUCT
+ if ((WizardMode || Verbose) && PrintMiscellaneous) {
+ tty->cr();
+ findpc(eip);
+ tty->cr();
+ }
+#endif
+#define PRINT_REG(rax) \
+ { tty->print("%s = ", #rax); os::print_location(tty, rax); }
+ PRINT_REG(rax);
+ PRINT_REG(rbx);
+ PRINT_REG(rcx);
+ PRINT_REG(rdx);
+ PRINT_REG(rdi);
+ PRINT_REG(rsi);
+ PRINT_REG(rbp);
+ PRINT_REG(rsp);
+#undef PRINT_REG
+ // Print some words near top of staack.
+ int* dump_sp = (int*) rsp;
+ for (int col1 = 0; col1 < 8; col1++) {
+ tty->print("(rsp+0x%03x) 0x%08x: ", (int)((intptr_t)dump_sp - (intptr_t)rsp), (intptr_t)dump_sp);
+ os::print_location(tty, *dump_sp++);
+ }
+ for (int row = 0; row < 16; row++) {
+ tty->print("(rsp+0x%03x) 0x%08x: ", (int)((intptr_t)dump_sp - (intptr_t)rsp), (intptr_t)dump_sp);
+ for (int col = 0; col < 8; col++) {
+ tty->print(" 0x%08x", *dump_sp++);
+ }
+ tty->cr();
+ }
+ // Print some instructions around pc:
+ Disassembler::decode((address)eip-64, (address)eip);
+ tty->print_cr("--------");
+ Disassembler::decode((address)eip, (address)eip+32);
+}
+
void MacroAssembler::stop(const char* msg) {
ExternalAddress message((address)msg);
// push address of message
pushptr(message.addr());
{ Label L; call(L, relocInfo::none); bind(L); } // push eip
- pusha(); // push registers
+ pusha(); // push registers
call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug32)));
hlt();
}
@@ -5558,6 +5592,18 @@
pop_CPU_state();
}
+void MacroAssembler::print_state() {
+ { Label L; call(L, relocInfo::none); bind(L); } // push eip
+ pusha(); // push registers
+
+ push_CPU_state();
+ call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::print_state32)));
+ pop_CPU_state();
+
+ popa();
+ addl(rsp, wordSize);
+}
+
#else // _LP64
// 64 bit versions
@@ -6023,14 +6069,33 @@
}
void MacroAssembler::warn(const char* msg) {
- push(rsp);
+ push(rbp);
+ movq(rbp, rsp);
andq(rsp, -16); // align stack as required by push_CPU_state and call
-
push_CPU_state(); // keeps alignment at 16 bytes
lea(c_rarg0, ExternalAddress((address) msg));
call_VM_leaf(CAST_FROM_FN_PTR(address, warning), c_rarg0);
pop_CPU_state();
- pop(rsp);
+ mov(rsp, rbp);
+ pop(rbp);
+}
+
+void MacroAssembler::print_state() {
+ address rip = pc();
+ pusha(); // get regs on stack
+ push(rbp);
+ movq(rbp, rsp);
+ andq(rsp, -16); // align stack as required by push_CPU_state and call
+ push_CPU_state(); // keeps alignment at 16 bytes
+
+ lea(c_rarg0, InternalAddress(rip));
+ lea(c_rarg1, Address(rbp, wordSize)); // pass pointer to regs array
+ call_VM_leaf(CAST_FROM_FN_PTR(address, MacroAssembler::print_state64), c_rarg0, c_rarg1);
+
+ pop_CPU_state();
+ mov(rsp, rbp);
+ pop(rbp);
+ popa();
}
#ifndef PRODUCT
@@ -6039,7 +6104,7 @@
void MacroAssembler::debug64(char* msg, int64_t pc, int64_t regs[]) {
// In order to get locks to work, we need to fake a in_VM state
- if (ShowMessageBoxOnError ) {
+ if (ShowMessageBoxOnError) {
JavaThread* thread = JavaThread::current();
JavaThreadState saved_state = thread->thread_state();
thread->set_thread_state(_thread_in_vm);
@@ -6053,30 +6118,9 @@
// XXX correct this offset for amd64
// This is the value of eip which points to where verify_oop will return.
if (os::message_box(msg, "Execution stopped, print registers?")) {
- ttyLocker ttyl;
- tty->print_cr("rip = 0x%016lx", pc);
-#ifndef PRODUCT
- tty->cr();
- findpc(pc);
- tty->cr();
-#endif
- tty->print_cr("rax = 0x%016lx", regs[15]);
- tty->print_cr("rbx = 0x%016lx", regs[12]);
- tty->print_cr("rcx = 0x%016lx", regs[14]);
- tty->print_cr("rdx = 0x%016lx", regs[13]);
- tty->print_cr("rdi = 0x%016lx", regs[8]);
- tty->print_cr("rsi = 0x%016lx", regs[9]);
- tty->print_cr("rbp = 0x%016lx", regs[10]);
- tty->print_cr("rsp = 0x%016lx", regs[11]);
- tty->print_cr("r8 = 0x%016lx", regs[7]);
- tty->print_cr("r9 = 0x%016lx", regs[6]);
- tty->print_cr("r10 = 0x%016lx", regs[5]);
- tty->print_cr("r11 = 0x%016lx", regs[4]);
- tty->print_cr("r12 = 0x%016lx", regs[3]);
- tty->print_cr("r13 = 0x%016lx", regs[2]);
- tty->print_cr("r14 = 0x%016lx", regs[1]);
- tty->print_cr("r15 = 0x%016lx", regs[0]);
+ print_state64(pc, regs);
BREAKPOINT;
+ assert(false, "start up GDB");
}
ThreadStateTransition::transition(thread, _thread_in_vm, saved_state);
} else {
@@ -6087,6 +6131,54 @@
}
}
+void MacroAssembler::print_state64(int64_t pc, int64_t regs[]) {
+ ttyLocker ttyl;
+ FlagSetting fs(Debugging, true);
+ tty->print_cr("rip = 0x%016lx", pc);
+#ifndef PRODUCT
+ tty->cr();
+ findpc(pc);
+ tty->cr();
+#endif
+#define PRINT_REG(rax, value) \
+ { tty->print("%s = ", #rax); os::print_location(tty, value); }
+ PRINT_REG(rax, regs[15]);
+ PRINT_REG(rbx, regs[12]);
+ PRINT_REG(rcx, regs[14]);
+ PRINT_REG(rdx, regs[13]);
+ PRINT_REG(rdi, regs[8]);
+ PRINT_REG(rsi, regs[9]);
+ PRINT_REG(rbp, regs[10]);
+ PRINT_REG(rsp, regs[11]);
+ PRINT_REG(r8 , regs[7]);
+ PRINT_REG(r9 , regs[6]);
+ PRINT_REG(r10, regs[5]);
+ PRINT_REG(r11, regs[4]);
+ PRINT_REG(r12, regs[3]);
+ PRINT_REG(r13, regs[2]);
+ PRINT_REG(r14, regs[1]);
+ PRINT_REG(r15, regs[0]);
+#undef PRINT_REG
+ // Print some words near top of staack.
+ int64_t* rsp = (int64_t*) regs[11];
+ int64_t* dump_sp = rsp;
+ for (int col1 = 0; col1 < 8; col1++) {
+ tty->print("(rsp+0x%03x) 0x%016lx: ", (int)((intptr_t)dump_sp - (intptr_t)rsp), (int64_t)dump_sp);
+ os::print_location(tty, *dump_sp++);
+ }
+ for (int row = 0; row < 25; row++) {
+ tty->print("(rsp+0x%03x) 0x%016lx: ", (int)((intptr_t)dump_sp - (intptr_t)rsp), (int64_t)dump_sp);
+ for (int col = 0; col < 4; col++) {
+ tty->print(" 0x%016lx", *dump_sp++);
+ }
+ tty->cr();
+ }
+ // Print some instructions around pc:
+ Disassembler::decode((address)pc-64, (address)pc);
+ tty->print_cr("--------");
+ Disassembler::decode((address)pc, (address)pc+32);
+}
+
#endif // _LP64
// Now versions that are common to 32/64 bit
@@ -6456,7 +6548,7 @@
get_thread(rax);
cmpptr(java_thread, rax);
jcc(Assembler::equal, L);
- stop("MacroAssembler::call_VM_base: rdi not callee saved?");
+ STOP("MacroAssembler::call_VM_base: rdi not callee saved?");
bind(L);
}
pop(rax);
@@ -7196,7 +7288,7 @@
jcc(Assembler::notZero, integer);
cmpl(tmp3, 0x80000000);
jcc(Assembler::notZero, integer);
- stop("integer indefinite value shouldn't be seen here");
+ STOP("integer indefinite value shouldn't be seen here");
bind(integer);
}
#else
@@ -7206,7 +7298,7 @@
shlq(tmp3, 1);
jcc(Assembler::carryClear, integer);
jcc(Assembler::notZero, integer);
- stop("integer indefinite value shouldn't be seen here");
+ STOP("integer indefinite value shouldn't be seen here");
bind(integer);
}
#endif
@@ -8388,7 +8480,7 @@
shlptr(tsize, LogHeapWordSize);
cmpptr(t1, tsize);
jcc(Assembler::equal, ok);
- stop("assert(t1 != tlab size)");
+ STOP("assert(t1 != tlab size)");
should_not_reach_here();
bind(ok);
@@ -8727,6 +8819,19 @@
}
+// virtual method calling
+void MacroAssembler::lookup_virtual_method(Register recv_klass,
+ RegisterOrConstant vtable_index,
+ Register method_result) {
+ const int base = instanceKlass::vtable_start_offset() * wordSize;
+ assert(vtableEntry::size() * wordSize == wordSize, "else adjust the scaling in the code below");
+ Address vtable_entry_addr(recv_klass,
+ vtable_index, Address::times_ptr,
+ base + vtableEntry::method_offset_in_bytes());
+ movptr(method_result, vtable_entry_addr);
+}
+
+
void MacroAssembler::check_klass_subtype(Register sub_klass,
Register super_klass,
Register temp_reg,
@@ -8976,6 +9081,7 @@
// Pass register number to verify_oop_subroutine
char* b = new char[strlen(s) + 50];
sprintf(b, "verify_oop: %s: %s", reg->name(), s);
+ BLOCK_COMMENT("verify_oop {");
#ifdef _LP64
push(rscratch1); // save r10, trashed by movptr()
#endif
@@ -8990,6 +9096,7 @@
movptr(rax, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address()));
call(rax);
// Caller pops the arguments (oop, message) and restores rax, r10
+ BLOCK_COMMENT("} verify_oop");
}
@@ -9010,7 +9117,7 @@
jcc(Assembler::notZero, L);
char* buf = new char[40];
sprintf(buf, "DelayedValue="INTPTR_FORMAT, delayed_value_addr[1]);
- stop(buf);
+ STOP(buf);
} else {
jccb(Assembler::notZero, L);
hlt();
@@ -9026,60 +9133,6 @@
}
-// registers on entry:
-// - rax ('check' register): required MethodType
-// - rcx: method handle
-// - rdx, rsi, or ?: killable temp
-void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type) {
- Address type_addr(mh_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg));
- // compare method type against that of the receiver
- if (UseCompressedOops) {
- load_heap_oop(temp_reg, type_addr);
- cmpptr(mtype_reg, temp_reg);
- } else {
- cmpptr(mtype_reg, type_addr);
- }
- jcc(Assembler::notEqual, wrong_method_type);
-}
-
-
-// A method handle has a "vmslots" field which gives the size of its
-// argument list in JVM stack slots. This field is either located directly
-// in every method handle, or else is indirectly accessed through the
-// method handle's MethodType. This macro hides the distinction.
-void MacroAssembler::load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
- Register temp_reg) {
- assert_different_registers(vmslots_reg, mh_reg, temp_reg);
- // load mh.type.form.vmslots
- Register temp2_reg = vmslots_reg;
- load_heap_oop(temp2_reg, Address(mh_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg)));
- load_heap_oop(temp2_reg, Address(temp2_reg, delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, temp_reg)));
- movl(vmslots_reg, Address(temp2_reg, delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, temp_reg)));
-}
-
-
-// registers on entry:
-// - rcx: method handle
-// - rdx: killable temp (interpreted only)
-// - rax: killable temp (compiled only)
-void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg) {
- assert(mh_reg == rcx, "caller must put MH object in rcx");
- assert_different_registers(mh_reg, temp_reg);
-
- // pick out the interpreted side of the handler
- // NOTE: vmentry is not an oop!
- movptr(temp_reg, Address(mh_reg, delayed_value(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes, temp_reg)));
-
- // off we go...
- jmp(Address(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes()));
-
- // for the various stubs which take control at this point,
- // see MethodHandles::generate_method_handle_stub
-}
-
-
Address MacroAssembler::argument_address(RegisterOrConstant arg_slot,
int extra_slot_offset) {
// cf. TemplateTable::prepare_invoke(), if (load_receiver).
@@ -9152,14 +9205,14 @@
movptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset())));
cmpptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_start_offset())));
jcc(Assembler::aboveEqual, next);
- stop("assert(top >= start)");
+ STOP("assert(top >= start)");
should_not_reach_here();
bind(next);
movptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_end_offset())));
cmpptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset())));
jcc(Assembler::aboveEqual, ok);
- stop("assert(top <= end)");
+ STOP("assert(top <= end)");
should_not_reach_here();
bind(ok);
@@ -9592,6 +9645,25 @@
movptr(dst, src);
}
+void MacroAssembler::cmp_heap_oop(Register src1, Address src2, Register tmp) {
+ assert_different_registers(src1, tmp);
+#ifdef _LP64
+ if (UseCompressedOops) {
+ bool did_push = false;
+ if (tmp == noreg) {
+ tmp = rax;
+ push(tmp);
+ did_push = true;
+ assert(!src2.uses(rsp), "can't push");
+ }
+ load_heap_oop(tmp, src2);
+ cmpptr(src1, tmp);
+ if (did_push) pop(tmp);
+ } else
+#endif
+ cmpptr(src1, src2);
+}
+
// Used for storing NULLs.
void MacroAssembler::store_heap_oop_null(Address dst) {
#ifdef _LP64
@@ -9622,7 +9694,7 @@
push(rscratch1); // cmpptr trashes rscratch1
cmpptr(r12_heapbase, ExternalAddress((address)Universe::narrow_oop_base_addr()));
jcc(Assembler::equal, ok);
- stop(msg);
+ STOP(msg);
bind(ok);
pop(rscratch1);
}
@@ -9655,7 +9727,7 @@
Label ok;
testq(r, r);
jcc(Assembler::notEqual, ok);
- stop("null oop passed to encode_heap_oop_not_null");
+ STOP("null oop passed to encode_heap_oop_not_null");
bind(ok);
}
#endif
@@ -9676,7 +9748,7 @@
Label ok;
testq(src, src);
jcc(Assembler::notEqual, ok);
- stop("null oop passed to encode_heap_oop_not_null2");
+ STOP("null oop passed to encode_heap_oop_not_null2");
bind(ok);
}
#endif
@@ -9867,7 +9939,7 @@
cmpptr(rax, StackAlignmentInBytes-wordSize);
pop(rax);
jcc(Assembler::equal, L);
- stop("Stack is not properly aligned!");
+ STOP("Stack is not properly aligned!");
bind(L);
}
#endif
@@ -10541,13 +10613,6 @@
bind(DONE);
}
-#ifdef PRODUCT
-#define BLOCK_COMMENT(str) /* nothing */
-#else
-#define BLOCK_COMMENT(str) block_comment(str)
-#endif
-
-#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
void MacroAssembler::generate_fill(BasicType t, bool aligned,
Register to, Register value, Register count,
Register rtmp, XMMRegister xtmp) {
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -1940,6 +1940,7 @@
void load_heap_oop(Register dst, Address src);
void load_heap_oop_not_null(Register dst, Address src);
void store_heap_oop(Address dst, Register src);
+ void cmp_heap_oop(Register src1, Address src2, Register tmp = noreg);
// Used for storing NULL. All other oop constants should be
// stored using routines that take a jobject.
@@ -2117,6 +2118,11 @@
Register scan_temp,
Label& no_such_interface);
+ // virtual method calling
+ void lookup_virtual_method(Register recv_klass,
+ RegisterOrConstant vtable_index,
+ Register method_result);
+
// Test sub_klass against super_klass, with fast and slow paths.
// The fast path produces a tri-state answer: yes / no / maybe-slow.
@@ -2152,15 +2158,8 @@
Label& L_success);
// method handles (JSR 292)
- void check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type);
- void load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
- Register temp_reg);
- void jump_to_method_handle_entry(Register mh_reg, Register temp_reg);
Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0);
-
//----
void set_word_if_not_zero(Register reg); // sets reg to 1 if not zero, otherwise 0
@@ -2179,8 +2178,13 @@
// prints msg and continues
void warn(const char* msg);
+ // dumps registers and other state
+ void print_state();
+
static void debug32(int rdi, int rsi, int rbp, int rsp, int rbx, int rdx, int rcx, int rax, int eip, char* msg);
static void debug64(char* msg, int64_t pc, int64_t regs[]);
+ static void print_state32(int rdi, int rsi, int rbp, int rsp, int rbx, int rdx, int rcx, int rax, int eip);
+ static void print_state64(int64_t pc, int64_t regs[]);
void os_breakpoint();
--- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3508,6 +3508,7 @@
void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
ciMethod* method = op->profiled_method();
int bci = op->profiled_bci();
+ ciMethod* callee = op->profiled_callee();
// Update counter for all call types
ciMethodData* md = method->method_data_or_null();
@@ -3519,9 +3520,11 @@
__ movoop(mdo, md->constant_encoding());
Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
Bytecodes::Code bc = method->java_code_at_bci(bci);
+ const bool callee_is_static = callee->is_loaded() && callee->is_static();
// Perform additional virtual call profiling for invokevirtual and
// invokeinterface bytecodes
if ((bc == Bytecodes::_invokevirtual || bc == Bytecodes::_invokeinterface) &&
+ !callee_is_static && // required for optimized MH invokes
C1ProfileVirtualCalls) {
assert(op->recv()->is_single_cpu(), "recv must be allocated");
Register recv = op->recv()->as_register();
--- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -871,9 +871,9 @@
// Need to differentiate between igetfield, agetfield, bgetfield etc.
// because they are different sizes.
// Use the type from the constant pool cache
- __ shrl(rdx, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask rdx for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask rdx after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
#ifdef _LP64
Label notObj;
__ cmpl(rdx, atos);
--- a/hotspot/src/cpu/x86/vm/frame_x86.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -439,7 +439,6 @@
// 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");
@@ -483,7 +482,6 @@
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);
@@ -658,9 +656,7 @@
values.describe(frame_no, fp() + frame::name##_offset, #name)
void frame::describe_pd(FrameValues& values, int frame_no) {
- if (is_ricochet_frame()) {
- MethodHandles::RicochetFrame::describe(this, values, frame_no);
- } else if (is_interpreted_frame()) {
+ if (is_interpreted_frame()) {
DESCRIBE_FP_OFFSET(interpreter_frame_sender_sp);
DESCRIBE_FP_OFFSET(interpreter_frame_last_sp);
DESCRIBE_FP_OFFSET(interpreter_frame_method);
@@ -682,12 +678,7 @@
if (_cb != NULL) {
// use the frame size if valid
int size = _cb->frame_size();
- if ((size > 0) &&
- (! is_ricochet_frame())) {
- // Work-around: ricochet explicitly excluded because frame size is not
- // constant for the ricochet blob but its frame_size could not, for
- // some reasons, be declared as <= 0. This potentially confusing
- // size declaration should be fixed as another CR.
+ if (size > 0) {
return unextended_sp() + size;
}
}
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -253,8 +253,12 @@
get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size);
movptr(bytecode, Address(cache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()));
const int shift_count = (1 + byte_no) * BitsPerByte;
+ assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) ||
+ (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift),
+ "correct shift count");
shrptr(bytecode, shift_count);
- andptr(bytecode, 0xFF);
+ assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask");
+ andptr(bytecode, ConstantPoolCacheEntry::bytecode_1_mask);
}
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -256,8 +256,12 @@
// little-endian machines allow us that.
movl(bytecode, Address(cache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()));
const int shift_count = (1 + byte_no) * BitsPerByte;
+ assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) ||
+ (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift),
+ "correct shift count");
shrl(bytecode, shift_count);
- andl(bytecode, 0xFF);
+ assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask");
+ andl(bytecode, ConstantPoolCacheEntry::bytecode_1_mask);
}
--- a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -35,7 +35,6 @@
address generate_normal_entry(bool synchronized);
address generate_native_entry(bool synchronized);
address generate_abstract_entry(void);
- address generate_method_handle_entry(void);
address generate_math_entry(AbstractInterpreter::MethodKind kind);
address generate_empty_entry(void);
address generate_accessor_entry(void);
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -243,18 +243,6 @@
}
-// Method handle invoker
-// Dispatch a method of the form java.lang.invoke.MethodHandles::invoke(...)
-address InterpreterGenerator::generate_method_handle_entry(void) {
- if (!EnableInvokeDynamic) {
- return generate_abstract_entry();
- }
-
- address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm);
-
- return entry_point;
-}
-
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 Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -325,19 +325,6 @@
}
-// Method handle invoker
-// Dispatch a method of the form java.lang.invoke.MethodHandles::invoke(...)
-address InterpreterGenerator::generate_method_handle_entry(void) {
- if (!EnableInvokeDynamic) {
- return generate_abstract_entry();
- }
-
- address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm);
-
- return entry_point;
-}
-
-
// Empty method, generate a very fast return.
address InterpreterGenerator::generate_empty_entry(void) {
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -32,8 +32,10 @@
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
+#define STOP(error) stop(error)
#else
#define BLOCK_COMMENT(str) __ block_comment(str)
+#define STOP(error) block_comment(error); __ stop(error)
#endif
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
@@ -43,483 +45,24 @@
return RegisterOrConstant(value);
}
-address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm,
- address interpreted_entry) {
- // Just before the actual machine code entry point, allocate space
- // for a MethodHandleEntry::Data record, so that we can manage everything
- // from one base pointer.
- __ align(wordSize);
- address target = __ pc() + sizeof(Data);
- while (__ pc() < target) {
- __ nop();
- __ align(wordSize);
- }
-
- MethodHandleEntry* me = (MethodHandleEntry*) __ pc();
- me->set_end_address(__ pc()); // set a temporary end_address
- me->set_from_interpreted_entry(interpreted_entry);
- me->set_type_checking_entry(NULL);
-
- return (address) me;
-}
-
-MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm,
- address start_addr) {
- MethodHandleEntry* me = (MethodHandleEntry*) start_addr;
- assert(me->end_address() == start_addr, "valid ME");
-
- // Fill in the real end_address:
- __ align(wordSize);
- me->set_end_address(__ pc());
-
- return me;
-}
-
-// 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* bounce_offset,
- int* exception_offset,
- int* frame_size_in_words) {
- (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize;
-
- address start = __ pc();
-
-#ifdef ASSERT
- __ 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.
-#ifndef PRODUCT
-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
-};
-#endif
-
-#ifdef ASSERT
-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");
+ "MH 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
-#ifdef ASSERT
- 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);
- }
-#endif
- BLOCK_COMMENT("} load_stack_move");
-}
-
#ifdef ASSERT
-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(), "");
+static int check_nonzero(const char* xname, int x) {
+ assert(x != 0, err_msg("%s should be nonzero", xname));
+ return x;
}
-
-void MethodHandles::RicochetFrame::verify() const {
- verify_offsets();
- assert(magic_number_1() == MAGIC_NUMBER_1, err_msg(PTR_FORMAT " == " PTR_FORMAT, magic_number_1(), MAGIC_NUMBER_1));
- assert(magic_number_2() == MAGIC_NUMBER_2, err_msg(PTR_FORMAT " == " PTR_FORMAT, 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
+#define NONZERO(x) check_nonzero(#x, x)
+#else //ASSERT
+#define NONZERO(x) (x)
+#endif //ASSERT
#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 {");
- __ 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);
- BLOCK_COMMENT("} verify_argslot");
-}
-
-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) {
@@ -528,12 +71,15 @@
klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(),
"must be one of the SystemDictionaryHandles");
Register temp = rdi;
+ Register temp2 = noreg;
+ LP64_ONLY(temp2 = rscratch1); // used by MacroAssembler::cmpptr
Label L_ok, L_bad;
BLOCK_COMMENT("verify_klass {");
__ verify_oop(obj);
__ testptr(obj, obj);
__ jcc(Assembler::zero, L_bad);
- __ push(temp);
+ __ push(temp); if (temp2 != noreg) __ push(temp2);
+#define UNPUSH { if (temp2 != noreg) __ pop(temp2); __ pop(temp); }
__ load_klass(temp, obj);
__ cmpptr(temp, ExternalAddress((address) klass_addr));
__ jcc(Assembler::equal, L_ok);
@@ -541,17 +87,42 @@
__ movptr(temp, Address(temp, super_check_offset));
__ cmpptr(temp, ExternalAddress((address) klass_addr));
__ jcc(Assembler::equal, L_ok);
- __ pop(temp);
+ UNPUSH;
__ bind(L_bad);
- __ stop(error_message);
+ __ STOP(error_message);
__ BIND(L_ok);
- __ pop(temp);
+ UNPUSH;
BLOCK_COMMENT("} verify_klass");
}
+
+void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) {
+ Label L;
+ BLOCK_COMMENT("verify_ref_kind {");
+ __ movl(temp, Address(member_reg, NONZERO(java_lang_invoke_MemberName::flags_offset_in_bytes())));
+ __ shrl(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT);
+ __ andl(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK);
+ __ cmpl(temp, ref_kind);
+ __ jcc(Assembler::equal, L);
+ { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal);
+ jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind);
+ if (ref_kind == JVM_REF_invokeVirtual ||
+ ref_kind == JVM_REF_invokeSpecial)
+ // could do this for all ref_kinds, but would explode assembly code size
+ trace_method_handle(_masm, buf);
+ __ STOP(buf);
+ }
+ BLOCK_COMMENT("} verify_ref_kind");
+ __ bind(L);
+}
+
#endif //ASSERT
-void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp) {
- if (JvmtiExport::can_post_interpreter_events()) {
+void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp,
+ bool for_compiler_entry) {
+ assert(method == rbx, "interpreter calling convention");
+ __ verify_oop(method);
+
+ if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
// compiled code in threads for which the event is enabled. Check here for
@@ -567,462 +138,380 @@
__ cmpb(Address(rthread, JavaThread::interp_only_mode_offset()), 0);
__ jccb(Assembler::zero, run_compiled_code);
__ jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
- __ bind(run_compiled_code);
+ __ BIND(run_compiled_code);
}
- __ jmp(Address(method, methodOopDesc::from_interpreted_offset()));
+
+ const ByteSize entry_offset = for_compiler_entry ? methodOopDesc::from_compiled_offset() :
+ methodOopDesc::from_interpreted_offset();
+ __ jmp(Address(method, entry_offset));
}
+void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm,
+ Register recv, Register method_temp,
+ Register temp2,
+ bool for_compiler_entry) {
+ BLOCK_COMMENT("jump_to_lambda_form {");
+ // This is the initial entry point of a lazy method handle.
+ // After type checking, it picks up the invoker from the LambdaForm.
+ assert_different_registers(recv, method_temp, temp2);
+ assert(recv != noreg, "required register");
+ assert(method_temp == rbx, "required register for loading method");
+
+ //NOT_PRODUCT({ FlagSetting fs(TraceMethodHandles, true); trace_method_handle(_masm, "LZMH"); });
+
+ // Load the invoker, as MH -> MH.form -> LF.vmentry
+ __ verify_oop(recv);
+ __ load_heap_oop(method_temp, Address(recv, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes())));
+ __ verify_oop(method_temp);
+ __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes())));
+ __ verify_oop(method_temp);
+ // the following assumes that a methodOop is normally compressed in the vmtarget field:
+ __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes())));
+ __ verify_oop(method_temp);
+
+ if (VerifyMethodHandles && !for_compiler_entry) {
+ // make sure recv is already on stack
+ __ load_sized_value(temp2,
+ Address(method_temp, methodOopDesc::size_of_parameters_offset()),
+ sizeof(u2), /*is_signed*/ false);
+ // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), "");
+ Label L;
+ __ cmpptr(recv, __ argument_address(temp2, -1));
+ __ jcc(Assembler::equal, L);
+ __ movptr(rax, __ argument_address(temp2, -1));
+ __ STOP("receiver not on stack");
+ __ BIND(L);
+ }
+
+ jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry);
+ BLOCK_COMMENT("} jump_to_lambda_form");
+}
+
+
// Code generation
-address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
- // rbx: methodOop
- // rcx: receiver method handle (must load from sp[MethodTypeForm.vmslots])
- // rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
- // rdx, rdi: garbage temp, blown away
+address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm,
+ vmIntrinsics::ID iid) {
+ const bool not_for_compiler_entry = false; // this is the interpreter entry
+ assert(is_signature_polymorphic(iid), "expected invoke iid");
+ if (iid == vmIntrinsics::_invokeGeneric ||
+ iid == vmIntrinsics::_compiledLambdaForm) {
+ // Perhaps surprisingly, the symbolic references visible to Java are not directly used.
+ // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod.
+ // They all allow an appendix argument.
+ __ hlt(); // empty stubs make SG sick
+ return NULL;
+ }
- Register rbx_method = rbx;
- Register rcx_recv = rcx;
- Register rax_mtype = rax;
- Register rdx_temp = rdx;
- Register rdi_temp = rdi;
+ // rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
+ // rbx: methodOop
+ // rdx: argument locator (parameter slot count, added to rsp)
+ // rcx: used as temp to hold mh or receiver
+ // rax, rdi: garbage temps, blown away
+ Register rdx_argp = rdx; // argument list ptr, live on error paths
+ Register rax_temp = rax;
+ Register rcx_mh = rcx; // MH receiver; dies quickly and is recycled
+ Register rbx_method = rbx; // eventual target of this invocation
- // emit WrongMethodType path first, to enable jccb back-branch from main path
- Label wrong_method_type;
- __ bind(wrong_method_type);
- Label invoke_generic_slow_path, invoke_exact_error_path;
- assert(methodOopDesc::intrinsic_id_size_in_bytes() == sizeof(u1), "");;
- __ cmpb(Address(rbx_method, methodOopDesc::intrinsic_id_offset_in_bytes()), (int) vmIntrinsics::_invokeExact);
- __ jcc(Assembler::notEqual, invoke_generic_slow_path);
- __ jmp(invoke_exact_error_path);
+ address code_start = __ pc();
// here's where control starts out:
__ align(CodeEntryAlignment);
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++) {
- __ movptr(rax_mtype, Address(tem, *pchase));
- tem = rax_mtype; // in case there is another indirection
+ if (VerifyMethodHandles) {
+ Label L;
+ BLOCK_COMMENT("verify_intrinsic_id {");
+ __ cmpb(Address(rbx_method, methodOopDesc::intrinsic_id_offset_in_bytes()), (int) iid);
+ __ jcc(Assembler::equal, L);
+ if (iid == vmIntrinsics::_linkToVirtual ||
+ iid == vmIntrinsics::_linkToSpecial) {
+ // could do this for all kinds, but would explode assembly code size
+ trace_method_handle(_masm, "bad methodOop::intrinsic_id");
}
+ __ STOP("bad methodOop::intrinsic_id");
+ __ bind(L);
+ BLOCK_COMMENT("} verify_intrinsic_id");
+ }
+
+ // First task: Find out how big the argument list is.
+ Address rdx_first_arg_addr;
+ int ref_kind = signature_polymorphic_intrinsic_ref_kind(iid);
+ assert(ref_kind != 0 || iid == vmIntrinsics::_invokeBasic, "must be _invokeBasic or a linkTo intrinsic");
+ if (ref_kind == 0 || MethodHandles::ref_kind_has_receiver(ref_kind)) {
+ __ load_sized_value(rdx_argp,
+ Address(rbx_method, methodOopDesc::size_of_parameters_offset()),
+ sizeof(u2), /*is_signed*/ false);
+ // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), "");
+ rdx_first_arg_addr = __ argument_address(rdx_argp, -1);
+ } else {
+ DEBUG_ONLY(rdx_argp = noreg);
+ }
+
+ if (!is_signature_polymorphic_static(iid)) {
+ __ movptr(rcx_mh, rdx_first_arg_addr);
+ DEBUG_ONLY(rdx_argp = noreg);
}
- // given the MethodType, find out where the MH argument is buried
- __ 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)));
- 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);
+ // rdx_first_arg_addr is live!
- // 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);
+ if (TraceMethodHandles) {
+ const char* name = vmIntrinsics::name_at(iid);
+ if (*name == '_') name += 1;
+ const size_t len = strlen(name) + 50;
+ char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ const char* suffix = "";
+ if (vmIntrinsics::method_for(iid) == NULL ||
+ !vmIntrinsics::method_for(iid)->access_flags().is_public()) {
+ if (is_signature_polymorphic_static(iid))
+ suffix = "/static";
+ else
+ suffix = "/private";
+ }
+ jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix);
+ // note: stub look for mh in rcx
+ trace_method_handle(_masm, qname);
+ }
- // error path for invokeExact (only)
- __ bind(invoke_exact_error_path);
- // ensure that the top of stack is properly aligned.
- __ mov(rdi, rsp);
- __ andptr(rsp, -StackAlignmentInBytes); // Align the stack for the ABI
- __ pushptr(Address(rdi, 0)); // Pick up the return address
-
- // Stub wants expected type in rax and the actual type in rcx
- __ jump(ExternalAddress(StubRoutines::throw_WrongMethodTypeException_entry()));
+ if (iid == vmIntrinsics::_invokeBasic) {
+ generate_method_handle_dispatch(_masm, iid, rcx_mh, noreg, not_for_compiler_entry);
- // for invokeGeneric (only), apply argument and result conversions on the fly
- __ bind(invoke_generic_slow_path);
-#ifdef ASSERT
- 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");
- __ bind(L);
+ } else {
+ // Adjust argument list by popping the trailing MemberName argument.
+ Register rcx_recv = noreg;
+ if (MethodHandles::ref_kind_has_receiver(ref_kind)) {
+ // Load the receiver (not the MH; the actual MemberName's receiver) up from the interpreter stack.
+ __ movptr(rcx_recv = rcx, rdx_first_arg_addr);
+ }
+ DEBUG_ONLY(rdx_argp = noreg);
+ Register rbx_member = rbx_method; // MemberName ptr; incoming method ptr is dead now
+ __ pop(rax_temp); // return address
+ __ pop(rbx_member); // extract last argument
+ __ push(rax_temp); // re-push return address
+ generate_method_handle_dispatch(_masm, iid, rcx_recv, rbx_member, not_for_compiler_entry);
}
-#endif //ASSERT
- Register rbx_temp = rbx_method; // don't need it now
- // 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(),
- rcx_argslot, rbx_temp, rdx_temp);
-
- // load up an adapter from the calling type (Java weaves this)
- Register rdx_adapter = rdx_temp;
- __ 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.
- // (Generic invokers are shared among form-families of method-type.)
- __ movptr(Address(rcx_argslot, 0 * Interpreter::stackElementSize), rax_mtype);
- // FIXME: assert that rdx_adapter is of the right method-type.
- __ mov(rcx, rdx_adapter);
- trace_method_handle(_masm, "invokeGeneric");
- __ jump_to_method_handle_entry(rcx, rdi_temp);
+ if (PrintMethodHandleStubs) {
+ address code_end = __ pc();
+ tty->print_cr("--------");
+ tty->print_cr("method handle interpreter entry for %s", vmIntrinsics::name_at(iid));
+ Disassembler::decode(code_start, code_end);
+ tty->cr();
+ }
return entry_point;
}
-// Helper to insert argument slots into the stack.
-// 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,
- 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()));
- 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.
- // The stacked return address gets pulled down with everything else.
- // That is, copy [rsp, argslot) downward by -size words. In pseudo-code:
- // rsp -= size;
- // for (rdx = rsp + size; rdx < argslot; rdx++)
- // rdx[-size] = rdx[0]
- // argslot -= size;
- BLOCK_COMMENT("insert_arg_slots {");
- __ mov(rdx_temp, rsp); // source pointer for copy
- __ 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, Interpreter::stackElementScale()), rbx_temp);
- __ addptr(rdx_temp, wordSize);
- __ cmpptr(rdx_temp, rax_argslot);
- __ jcc(Assembler::below, loop);
- }
-
- // Now move the argslot down, to point to the opened-up space.
- __ 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
-void MethodHandles::remove_arg_slots(MacroAssembler* _masm,
- 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()));
- 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.
- // Then remove the excess space on the stack.
- // The stacked return address gets pulled up with everything else.
- // That is, copy [rsp, argslot) upward by size words. In pseudo-code:
- // for (rdx = argslot-1; rdx >= rsp; --rdx)
- // rdx[size] = rdx[0]
- // argslot += size;
- // rsp += size;
- __ lea(rdx_temp, Address(rax_argslot, -wordSize)); // source pointer for copy
- {
- Label loop;
- __ BIND(loop);
- // pull one word up each time through the loop
- __ movptr(rbx_temp, Address(rdx_temp, 0));
- __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp);
- __ addptr(rdx_temp, -wordSize);
- __ cmpptr(rdx_temp, rsp);
- __ jcc(Assembler::aboveEqual, loop);
+void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm,
+ vmIntrinsics::ID iid,
+ Register receiver_reg,
+ Register member_reg,
+ bool for_compiler_entry) {
+ assert(is_signature_polymorphic(iid), "expected invoke iid");
+ Register rbx_method = rbx; // eventual target of this invocation
+ // temps used in this code are not used in *either* compiled or interpreted calling sequences
+#ifdef _LP64
+ Register temp1 = rscratch1;
+ Register temp2 = rscratch2;
+ Register temp3 = rax;
+ if (for_compiler_entry) {
+ assert(receiver_reg == (iid == vmIntrinsics::_linkToStatic ? noreg : j_rarg0), "only valid assignment");
+ assert_different_registers(temp1, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5);
+ assert_different_registers(temp2, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5);
+ assert_different_registers(temp3, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5);
}
-
- // Now move the argslot up, to point to the just-copied block.
- __ 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, 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);
+#else
+ Register temp1 = (for_compiler_entry ? rsi : rdx);
+ Register temp2 = rdi;
+ Register temp3 = rax;
+ if (for_compiler_entry) {
+ assert(receiver_reg == (iid == vmIntrinsics::_linkToStatic ? noreg : rcx), "only valid assignment");
+ assert_different_registers(temp1, rcx, rdx);
+ assert_different_registers(temp2, rcx, rdx);
+ assert_different_registers(temp3, rcx, rdx);
}
#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");
-}
+ assert_different_registers(temp1, temp2, temp3, receiver_reg);
+ assert_different_registers(temp1, temp2, temp3, member_reg);
+ if (!for_compiler_entry)
+ assert_different_registers(temp1, temp2, temp3, saved_last_sp_register()); // don't trash lastSP
+
+ if (iid == vmIntrinsics::_invokeBasic) {
+ // indirect through MH.form.vmentry.vmtarget
+ jump_to_lambda_form(_masm, receiver_reg, rbx_method, temp1, for_compiler_entry);
+
+ } else {
+ // The method is a member invoker used by direct method handles.
+ if (VerifyMethodHandles) {
+ // make sure the trailing argument really is a MemberName (caller responsibility)
+ verify_klass(_masm, member_reg, SystemDictionaryHandles::MemberName_klass(),
+ "MemberName required for invokeVirtual etc.");
+ }
+
+ Address member_clazz( member_reg, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()));
+ Address member_vmindex( member_reg, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes()));
+ Address member_vmtarget( member_reg, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()));
-// 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);
+ Register temp1_recv_klass = temp1;
+ if (iid != vmIntrinsics::_linkToStatic) {
+ __ verify_oop(receiver_reg);
+ if (iid == vmIntrinsics::_linkToSpecial) {
+ // Don't actually load the klass; just null-check the receiver.
+ __ null_check(receiver_reg);
+ } else {
+ // load receiver klass itself
+ __ null_check(receiver_reg, oopDesc::klass_offset_in_bytes());
+ __ load_klass(temp1_recv_klass, receiver_reg);
+ __ verify_oop(temp1_recv_klass);
+ }
+ BLOCK_COMMENT("check_receiver {");
+ // The receiver for the MemberName must be in receiver_reg.
+ // Check the receiver against the MemberName.clazz
+ if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) {
+ // Did not load it above...
+ __ load_klass(temp1_recv_klass, receiver_reg);
+ __ verify_oop(temp1_recv_klass);
+ }
+ if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) {
+ Label L_ok;
+ Register temp2_defc = temp2;
+ __ load_heap_oop(temp2_defc, member_clazz);
+ load_klass_from_Class(_masm, temp2_defc);
+ __ verify_oop(temp2_defc);
+ __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, L_ok);
+ // If we get here, the type check failed!
+ __ STOP("receiver class disagrees with MemberName.clazz");
+ __ bind(L_ok);
+ }
+ BLOCK_COMMENT("} check_receiver");
}
- __ 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");
-}
+ if (iid == vmIntrinsics::_linkToSpecial ||
+ iid == vmIntrinsics::_linkToStatic) {
+ DEBUG_ONLY(temp1_recv_klass = noreg); // these guys didn't load the recv_klass
+ }
+
+ // Live registers at this point:
+ // member_reg - MemberName that was the trailing argument
+ // temp1_recv_klass - klass of stacked receiver, if needed
+ // rsi/r13 - interpreter linkage (if interpreted)
+ // rcx, rdx, rsi, rdi, r8, r8 - compiler arguments (if compiled)
+
+ bool method_is_live = false;
+ switch (iid) {
+ case vmIntrinsics::_linkToSpecial:
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp3);
+ }
+ __ load_heap_oop(rbx_method, member_vmtarget);
+ method_is_live = true;
+ break;
+
+ case vmIntrinsics::_linkToStatic:
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp3);
+ }
+ __ load_heap_oop(rbx_method, member_vmtarget);
+ method_is_live = true;
+ break;
-// 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");
-}
+ case vmIntrinsics::_linkToVirtual:
+ {
+ // same as TemplateTable::invokevirtual,
+ // minus the CP setup and profiling:
+
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeVirtual, member_reg, temp3);
+ }
+
+ // pick out the vtable index from the MemberName, and then we can discard it:
+ Register temp2_index = temp2;
+ __ movptr(temp2_index, member_vmindex);
-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);
+ if (VerifyMethodHandles) {
+ Label L_index_ok;
+ __ cmpl(temp2_index, 0);
+ __ jcc(Assembler::greaterEqual, L_index_ok);
+ __ STOP("no virtual index");
+ __ BIND(L_index_ok);
+ }
+
+ // Note: The verifier invariants allow us to ignore MemberName.clazz and vmtarget
+ // at this point. And VerifyMethodHandles has already checked clazz, if needed.
+
+ // get target methodOop & entry point
+ __ lookup_virtual_method(temp1_recv_klass, temp2_index, rbx_method);
+ method_is_live = true;
+ break;
}
- } else if (UseSSE < 2) {
- __ empty_FPU_stack();
- }
-#endif //COMPILER2
-#endif //!_LP64
+
+ case vmIntrinsics::_linkToInterface:
+ {
+ // same as TemplateTable::invokeinterface
+ // (minus the CP setup and profiling, with different argument motion)
+ if (VerifyMethodHandles) {
+ verify_ref_kind(_masm, JVM_REF_invokeInterface, member_reg, temp3);
+ }
+
+ Register temp3_intf = temp3;
+ __ load_heap_oop(temp3_intf, member_clazz);
+ load_klass_from_Class(_masm, temp3_intf);
+ __ verify_oop(temp3_intf);
+
+ Register rbx_index = rbx_method;
+ __ movptr(rbx_index, member_vmindex);
+ if (VerifyMethodHandles) {
+ Label L;
+ __ cmpl(rbx_index, 0);
+ __ jcc(Assembler::greaterEqual, L);
+ __ STOP("invalid vtable index for MH.invokeInterface");
+ __ bind(L);
+ }
- // 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();
+ // given intf, index, and recv klass, dispatch to the implementation method
+ Label L_no_such_interface;
+ __ lookup_interface_method(temp1_recv_klass, temp3_intf,
+ // note: next two args must be the same:
+ rbx_index, rbx_method,
+ temp2,
+ L_no_such_interface);
+
+ __ verify_oop(rbx_method);
+ jump_from_method_handle(_masm, rbx_method, temp2, for_compiler_entry);
+ __ hlt();
+
+ __ bind(L_no_such_interface);
+ __ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
+ break;
+ }
+
+ default:
+ fatal(err_msg("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
+ break;
+ }
+
+ if (method_is_live) {
+ // live at this point: rbx_method, rsi/r13 (if interpreted)
+
+ // After figuring out which concrete method to call, jump into it.
+ // Note that this works in the interpreter with no data motion.
+ // But the compiled version will require that rcx_recv be shifted out.
+ __ verify_oop(rbx_method);
+ jump_from_method_handle(_masm, rbx_method, temp1, for_compiler_entry);
+ }
}
- BLOCK_COMMENT("} move_return_value");
}
#ifndef PRODUCT
-#define DESCRIBE_RICOCHET_OFFSET(rf, name) \
- values.describe(frame_no, (intptr_t *) (((uintptr_t)rf) + MethodHandles::RicochetFrame::name##_offset_in_bytes()), #name)
-
-void MethodHandles::RicochetFrame::describe(const frame* fr, FrameValues& values, int frame_no) {
- address bp = (address) fr->fp();
- RicochetFrame* rf = (RicochetFrame*)(bp - sender_link_offset_in_bytes());
-
- // ricochet slots
- DESCRIBE_RICOCHET_OFFSET(rf, exact_sender_sp);
- DESCRIBE_RICOCHET_OFFSET(rf, conversion);
- DESCRIBE_RICOCHET_OFFSET(rf, saved_args_base);
- DESCRIBE_RICOCHET_OFFSET(rf, saved_args_layout);
- DESCRIBE_RICOCHET_OFFSET(rf, saved_target);
- DESCRIBE_RICOCHET_OFFSET(rf, continuation);
-
- // relevant ricochet targets (in caller frame)
- values.describe(-1, rf->saved_args_base(), err_msg("*saved_args_base for #%d", frame_no));
-}
-#endif // ASSERT
-
-#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) {
// called as a leaf from native code: do not block the JVM!
- bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have rcx_mh
+ bool has_mh = (strstr(adaptername, "/static") == NULL &&
+ strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH
const char* mh_reg_name = has_mh ? "rcx_mh" : "rcx";
- tty->print_cr("MH %s %s="PTR_FORMAT" sp="PTR_FORMAT, adaptername, mh_reg_name, mh, entry_sp);
+ tty->print_cr("MH %s %s="PTR_FORMAT" sp="PTR_FORMAT,
+ adaptername, mh_reg_name,
+ mh, entry_sp);
if (Verbose) {
tty->print_cr("Registers:");
@@ -1086,12 +575,18 @@
values.describe(-1, dump_fp, "fp for #1 <not parsed, cannot trust pc>");
values.describe(-1, dump_sp, "sp for #1");
}
+ values.describe(-1, entry_sp, "raw top of stack");
tty->print_cr("Stack layout:");
values.print(p);
}
- if (has_mh)
- print_method_handle(mh);
+ if (has_mh && mh->is_oop()) {
+ mh->print();
+ if (java_lang_invoke_MethodHandle::is_instance(mh)) {
+ if (java_lang_invoke_MethodHandle::form_offset_in_bytes() != 0)
+ java_lang_invoke_MethodHandle::form(mh)->print();
+ }
+ }
}
}
@@ -1159,1363 +654,3 @@
}
#endif //PRODUCT
-// which conversion op types are implemented here?
-int MethodHandles::adapter_conversion_ops_supported_mask() {
- return ((1<<java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY)
- |(1<<java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW)
- |(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)
- //OP_COLLECT_ARGS is below...
- |(1<<java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS)
- |(
- 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)
- ))
- );
-}
-
-//------------------------------------------------------------------------------
-// MethodHandles::generate_method_handle_stub
-//
-// 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)
- // - rcx: receiver method handle
- // - rax: method handle type (only used by the check_mtype entry point)
- // - rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
- // - rdx: garbage temp, can blow away
-
- const Register rcx_recv = rcx;
- 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 = saved_last_sp_register();
-
- // Argument registers for _raise_exception.
- // 32-bit: Pass first two oop/int args in registers ECX and EDX.
- const Register rarg0_code = LP64_ONLY(j_rarg0) NOT_LP64(rcx);
- const Register rarg1_actual = LP64_ONLY(j_rarg1) NOT_LP64(rdx);
- const Register rarg2_required = LP64_ONLY(j_rarg2) NOT_LP64(rdi);
- assert_different_registers(rarg0_code, rarg1_actual, rarg2_required, saved_last_sp);
-
- guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
-
- // some handy addresses
- Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() );
- Address rcx_dmh_vmindex( rcx_recv, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes() );
-
- Address rcx_bmh_vmargslot( rcx_recv, java_lang_invoke_BoundMethodHandle::vmargslot_offset_in_bytes() );
- Address rcx_bmh_argument( rcx_recv, java_lang_invoke_BoundMethodHandle::argument_offset_in_bytes() );
-
- Address rcx_amh_vmargslot( rcx_recv, java_lang_invoke_AdapterMethodHandle::vmargslot_offset_in_bytes() );
- Address rcx_amh_argument( rcx_recv, java_lang_invoke_AdapterMethodHandle::argument_offset_in_bytes() );
- Address rcx_amh_conversion( rcx_recv, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes() );
- Address vmarg; // __ argument_address(vmargslot)
-
- const int java_mirror_offset = in_bytes(Klass::java_mirror_offset());
-
- if (have_entry(ek)) {
- __ nop(); // empty stubs make SG sick
- 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));
-
- BLOCK_COMMENT(err_msg("Entry %s {", entry_name(ek)));
-
- switch ((int) ek) {
- case _raise_exception:
- {
- // Not a real MH entry, but rather shared code for raising an
- // exception. Since we use the compiled entry, arguments are
- // expected in compiler argument registers.
- assert(raise_exception_method(), "must be set");
- assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
-
- const Register rax_pc = rax;
- __ pop(rax_pc); // caller PC
- __ mov(rsp, saved_last_sp); // cut the stack back to where the caller started
-
- Register rbx_method = rbx_temp;
- __ movptr(rbx_method, ExternalAddress((address) &_raise_exception_method));
-
- const int jobject_oop_offset = 0;
- __ movptr(rbx_method, Address(rbx_method, jobject_oop_offset)); // dereference the jobject
-
- __ movptr(saved_last_sp, rsp);
- __ subptr(rsp, 3 * wordSize);
- __ push(rax_pc); // restore caller PC
-
- __ movl (__ argument_address(constant(2)), rarg0_code);
- __ movptr(__ argument_address(constant(1)), rarg1_actual);
- __ movptr(__ argument_address(constant(0)), rarg2_required);
- jump_from_method_handle(_masm, rbx_method, rax);
- }
- break;
-
- case _invokestatic_mh:
- case _invokespecial_mh:
- {
- Register rbx_method = rbx_temp;
- __ load_heap_oop(rbx_method, rcx_mh_vmtarget); // target is a methodOop
- __ verify_oop(rbx_method);
- // same as TemplateTable::invokestatic or invokespecial,
- // minus the CP setup and profiling:
- if (ek == _invokespecial_mh) {
- // Must load & check the first argument before entering the target method.
- __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
- __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
- __ null_check(rcx_recv);
- __ verify_oop(rcx_recv);
- }
- jump_from_method_handle(_masm, rbx_method, rax);
- }
- break;
-
- case _invokevirtual_mh:
- {
- // same as TemplateTable::invokevirtual,
- // minus the CP setup and profiling:
-
- // pick out the vtable index and receiver offset from the MH,
- // and then we can discard it:
- __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
- Register rbx_index = rbx_temp;
- __ movl(rbx_index, rcx_dmh_vmindex);
- // Note: The verifier allows us to ignore rcx_mh_vmtarget.
- __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
- __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes());
-
- // get receiver klass
- Register rax_klass = rax_argslot;
- __ load_klass(rax_klass, rcx_recv);
- __ verify_oop(rax_klass);
-
- // get target methodOop & entry point
- const int base = instanceKlass::vtable_start_offset() * wordSize;
- assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below");
- Address vtable_entry_addr(rax_klass,
- rbx_index, Address::times_ptr,
- base + vtableEntry::method_offset_in_bytes());
- Register rbx_method = rbx_temp;
- __ movptr(rbx_method, vtable_entry_addr);
-
- __ verify_oop(rbx_method);
- jump_from_method_handle(_masm, rbx_method, rax);
- }
- break;
-
- case _invokeinterface_mh:
- {
- // same as TemplateTable::invokeinterface,
- // minus the CP setup and profiling:
-
- // pick out the interface and itable index from the MH.
- __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
- Register rdx_intf = rdx_temp;
- Register rbx_index = rbx_temp;
- __ load_heap_oop(rdx_intf, rcx_mh_vmtarget);
- __ movl(rbx_index, rcx_dmh_vmindex);
- __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
- __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes());
-
- // get receiver klass
- Register rax_klass = rax_argslot;
- __ load_klass(rax_klass, rcx_recv);
- __ verify_oop(rax_klass);
-
- Register rbx_method = rbx_index;
-
- // get interface klass
- Label no_such_interface;
- __ verify_oop(rdx_intf);
- __ lookup_interface_method(rax_klass, rdx_intf,
- // note: next two args must be the same:
- rbx_index, rbx_method,
- rdi_temp,
- no_such_interface);
-
- __ verify_oop(rbx_method);
- jump_from_method_handle(_masm, rbx_method, rax);
- __ hlt();
-
- __ bind(no_such_interface);
- // Throw an exception.
- // For historical reasons, it will be IncompatibleClassChangeError.
- __ mov(rbx_temp, rcx_recv); // rarg2_required might be RCX
- assert_different_registers(rarg2_required, rbx_temp);
- __ movptr(rarg2_required, Address(rdx_intf, java_mirror_offset)); // required interface
- __ mov( rarg1_actual, rbx_temp); // bad receiver
- __ movl( rarg0_code, (int) Bytecodes::_invokeinterface); // who is complaining?
- __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
- }
- break;
-
- case _bound_ref_mh:
- case _bound_int_mh:
- case _bound_long_mh:
- case _bound_ref_direct_mh:
- case _bound_int_direct_mh:
- case _bound_long_direct_mh:
- {
- const bool direct_to_method = (ek >= _bound_ref_direct_mh);
- 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(), rax_argslot, rbx_temp, rdx_temp);
-
- // store bound argument into the new stack slot:
- __ load_heap_oop(rbx_temp, rcx_bmh_argument);
- if (arg_type == T_OBJECT) {
- __ movptr(Address(rax_argslot, 0), rbx_temp);
- } else {
- Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type));
- move_typed_arg(_masm, arg_type, false,
- Address(rax_argslot, 0),
- prim_value_addr,
- rbx_temp, rdx_temp);
- }
-
- if (direct_to_method) {
- Register rbx_method = rbx_temp;
- __ load_heap_oop(rbx_method, rcx_mh_vmtarget);
- __ verify_oop(rbx_method);
- jump_from_method_handle(_masm, rbx_method, rax);
- } else {
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ verify_oop(rcx_recv);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- }
- break;
-
- case _adapter_opt_profiling:
- if (java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes() != 0) {
- Address rcx_mh_vmcount(rcx_recv, java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes());
- __ incrementl(rcx_mh_vmcount);
- }
- // fall through
-
- case _adapter_retype_only:
- case _adapter_retype_raw:
- // immediately jump to the next MH layer:
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ verify_oop(rcx_recv);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- // This is OK when all parameter types widen.
- // It is also OK when a return type narrows.
- break;
-
- case _adapter_check_cast:
- {
- // temps:
- Register rbx_klass = rbx_temp; // interesting AMH data
-
- // check a reference argument before jumping to the next layer of MH:
- __ movl(rax_argslot, rcx_amh_vmargslot);
- vmarg = __ argument_address(rax_argslot);
-
- // What class are we casting to?
- __ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- load_klass_from_Class(_masm, rbx_klass);
-
- Label done;
- __ movptr(rdx_temp, vmarg);
- __ testptr(rdx_temp, rdx_temp);
- __ jcc(Assembler::zero, done); // no cast if null
- __ load_klass(rdx_temp, rdx_temp);
-
- // live at this point:
- // - rbx_klass: klass required by the target method
- // - rdx_temp: argument klass to test
- // - rcx_recv: adapter method handle
- __ check_klass_subtype(rdx_temp, rbx_klass, rax_argslot, done);
-
- // If we get here, the type check failed!
- // Call the wrong_method_type stub, passing the failing argument type in rax.
- Register rax_mtype = rax_argslot;
- __ movl(rax_argslot, rcx_amh_vmargslot); // reload argslot field
- __ movptr(rdx_temp, vmarg);
-
- assert_different_registers(rarg2_required, rdx_temp);
- __ load_heap_oop(rarg2_required, rcx_amh_argument); // required class
- __ mov( rarg1_actual, rdx_temp); // bad object
- __ movl( rarg0_code, (int) Bytecodes::_checkcast); // who is complaining?
- __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
-
- __ bind(done);
- // get the new MH:
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- 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;
-
- case _adapter_opt_i2i: // optimized subcase of adapt_prim_to_prim
-//case _adapter_opt_f2i: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_l2i: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_unboxi: // optimized subcase of adapt_ref_to_prim
- {
- // perform an in-place conversion to int or an int subword
- __ movl(rax_argslot, rcx_amh_vmargslot);
- vmarg = __ argument_address(rax_argslot);
-
- switch (ek) {
- case _adapter_opt_i2i:
- __ movl(rdx_temp, vmarg);
- break;
- case _adapter_opt_l2i:
- {
- // just delete the extra slot; on a little-endian machine we keep the first
- __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
- remove_arg_slots(_masm, -stack_move_unit(),
- rax_argslot, rbx_temp, rdx_temp);
- vmarg = Address(rax_argslot, -Interpreter::stackElementSize);
- __ movl(rdx_temp, vmarg);
- }
- break;
- case _adapter_opt_unboxi:
- {
- // Load the value up from the heap.
- __ movptr(rdx_temp, vmarg);
- int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT);
-#ifdef ASSERT
- for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
- if (is_subword_type(BasicType(bt)))
- assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(BasicType(bt)), "");
- }
-#endif
- __ null_check(rdx_temp, value_offset);
- __ movl(rdx_temp, Address(rdx_temp, value_offset));
- // We load this as a word. Because we are little-endian,
- // the low bits will be correct, but the high bits may need cleaning.
- // The vminfo will guide us to clean those bits.
- }
- break;
- default:
- ShouldNotReachHere();
- }
-
- // Do the requested conversion and store the value.
- Register rbx_vminfo = rbx_temp;
- load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
-
- // get the new MH:
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- // (now we are done with the old MH)
-
- // original 32-bit vmdata word must be of this form:
- // | MBZ:6 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 |
- __ xchgptr(rcx, rbx_vminfo); // free rcx for shifts
- __ shll(rdx_temp /*, rcx*/);
- Label zero_extend, done;
- __ testl(rcx, CONV_VMINFO_SIGN_FLAG);
- __ jccb(Assembler::zero, zero_extend);
-
- // this path is taken for int->byte, int->short
- __ sarl(rdx_temp /*, rcx*/);
- __ jmpb(done);
-
- __ bind(zero_extend);
- // this is taken for int->char
- __ shrl(rdx_temp /*, rcx*/);
-
- __ bind(done);
- __ movl(vmarg, rdx_temp); // Store the value.
- __ xchgptr(rcx, rbx_vminfo); // restore rcx_recv
-
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- case _adapter_opt_i2l: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_unboxl: // optimized subcase of adapt_ref_to_prim
- {
- // perform an in-place int-to-long or ref-to-long conversion
- __ movl(rax_argslot, rcx_amh_vmargslot);
-
- // on a little-endian machine we keep the first slot and add another after
- __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
- insert_arg_slots(_masm, stack_move_unit(),
- rax_argslot, rbx_temp, rdx_temp);
- Address vmarg1(rax_argslot, -Interpreter::stackElementSize);
- Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize);
-
- switch (ek) {
- case _adapter_opt_i2l:
- {
-#ifdef _LP64
- __ movslq(rdx_temp, vmarg1); // Load sign-extended
- __ movq(vmarg1, rdx_temp); // Store into first slot
-#else
- __ movl(rdx_temp, vmarg1);
- __ sarl(rdx_temp, BitsPerInt - 1); // __ extend_sign()
- __ movl(vmarg2, rdx_temp); // store second word
-#endif
- }
- break;
- case _adapter_opt_unboxl:
- {
- // Load the value up from the heap.
- __ movptr(rdx_temp, vmarg1);
- int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_LONG);
- assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(T_DOUBLE), "");
- __ null_check(rdx_temp, value_offset);
-#ifdef _LP64
- __ movq(rbx_temp, Address(rdx_temp, value_offset));
- __ movq(vmarg1, rbx_temp);
-#else
- __ movl(rbx_temp, Address(rdx_temp, value_offset + 0*BytesPerInt));
- __ movl(rdx_temp, Address(rdx_temp, value_offset + 1*BytesPerInt));
- __ movl(vmarg1, rbx_temp);
- __ movl(vmarg2, rdx_temp);
-#endif
- }
- break;
- default:
- ShouldNotReachHere();
- }
-
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- case _adapter_opt_f2d: // optimized subcase of adapt_prim_to_prim
- case _adapter_opt_d2f: // optimized subcase of adapt_prim_to_prim
- {
- // perform an in-place floating primitive conversion
- __ movl(rax_argslot, rcx_amh_vmargslot);
- __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
- if (ek == _adapter_opt_f2d) {
- insert_arg_slots(_masm, stack_move_unit(),
- rax_argslot, rbx_temp, rdx_temp);
- }
- Address vmarg(rax_argslot, -Interpreter::stackElementSize);
-
-#ifdef _LP64
- if (ek == _adapter_opt_f2d) {
- __ movflt(xmm0, vmarg);
- __ cvtss2sd(xmm0, xmm0);
- __ movdbl(vmarg, xmm0);
- } else {
- __ movdbl(xmm0, vmarg);
- __ cvtsd2ss(xmm0, xmm0);
- __ movflt(vmarg, xmm0);
- }
-#else //_LP64
- if (ek == _adapter_opt_f2d) {
- __ fld_s(vmarg); // load float to ST0
- __ fstp_d(vmarg); // store double
- } else {
- __ fld_d(vmarg); // load double to ST0
- __ fstp_s(vmarg); // store single
- }
-#endif //_LP64
-
- if (ek == _adapter_opt_d2f) {
- remove_arg_slots(_masm, -stack_move_unit(),
- rax_argslot, rbx_temp, rdx_temp);
- }
-
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- case _adapter_swap_args:
- case _adapter_rot_args:
- // handled completely by optimized cases
- __ stop("init_AdapterMethodHandle should not issue this");
- break;
-
- case _adapter_opt_swap_1:
- case _adapter_opt_swap_2:
- case _adapter_opt_rot_1_up:
- case _adapter_opt_rot_1_down:
- case _adapter_opt_rot_2_up:
- case _adapter_opt_rot_2_down:
- {
- int 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);
- __ lea(rax_argslot, __ argument_address(rax_argslot));
-
- // 'vminfo' is the second
- Register rbx_destslot = rbx_temp;
- load_conversion_vminfo(_masm, rbx_destslot, rcx_amh_conversion);
- __ lea(rbx_destslot, __ argument_address(rbx_destslot));
- 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) {
- // 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 {
- // 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) {
- // 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--;
- move_arg_slots_up(_masm,
- rbx_destslot,
- Address(rax_argslot, 0),
- swap_slots,
- rax_argslot, rdx_temp);
- } else {
- // 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++;
- // dest_slot denotes an exclusive upper limit
- int limit_bias = OP_ROT_ARGS_DOWN_LIMIT_BIAS;
- if (limit_bias != 0)
- __ addptr(rbx_destslot, - limit_bias * wordSize);
- move_arg_slots_down(_masm,
- Address(rax_argslot, swap_slots * wordSize),
- rbx_destslot,
- -swap_slots,
- rax_argslot, rdx_temp);
- __ subptr(rbx_destslot, swap_slots * wordSize);
- }
- // pop the original first chunk into the destination slot, now free
- 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));
- }
- }
-
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- case _adapter_dup_args:
- {
- // 'argslot' is the position of the first argument to duplicate
- __ movl(rax_argslot, rcx_amh_vmargslot);
- __ lea(rax_argslot, __ argument_address(rax_argslot));
-
- // 'stack_move' is negative number of words to duplicate
- Register rdi_stack_move = rdi_temp;
- load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
-
- if (VerifyMethodHandles) {
- verify_argslots(_masm, rdi_stack_move, rax_argslot, true,
- "copied argument(s) must fall within current frame");
- }
-
- // 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, "");
-
- __ 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);
- }
- break;
-
- case _adapter_drop_args:
- {
- // 'argslot' is the position of the first argument to nuke
- __ movl(rax_argslot, rcx_amh_vmargslot);
- __ lea(rax_argslot, __ argument_address(rax_argslot));
-
- // (must do previous push after argslot address is taken)
-
- // 'stack_move' is number of words to drop
- 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);
-
- __ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
- __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
- }
- break;
-
- case _adapter_collect_args:
- 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 original 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 to 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_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 = 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 another temp
- Register rsi_temp = rsi;
-
- // arx_argslot points both to the array and to the first output arg
- vmarg = Address(rax_argslot, 0);
-
- // Get the array value.
- Register rdi_array = rdi_temp;
- Register rdx_array_klass = rdx_temp;
- 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(rdi_array, vmarg);
-
- 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(rdi_array, rdi_array);
- __ jcc(Assembler::notZero, L_skip);
-
- // If 'rsi' contains the 'saved_last_sp' (this is only the
- // case in a 32-bit version of the VM) we have to save 'rsi'
- // on the stack because later on (at 'L_array_is_empty') 'rsi'
- // will be overwritten.
- { if (rsi_temp == saved_last_sp) __ push(saved_last_sp); }
- // Also prepare a handy macro which restores 'rsi' if required.
-#define UNPUSH_RSI \
- { if (rsi_temp == saved_last_sp) __ pop(saved_last_sp); }
-
- __ jmp(L_array_is_empty);
- __ bind(L_skip);
- }
- __ null_check(rdi_array, oopDesc::klass_offset_in_bytes());
- __ load_klass(rdx_array_klass, rdi_array);
-
- // Save 'rsi' if required (see comment above). Do this only
- // after the null check such that the exception handler which is
- // called in the case of a null pointer exception will not be
- // confused by the extra value on the stack (it expects the
- // return pointer on top of the stack)
- { if (rsi_temp == saved_last_sp) __ push(saved_last_sp); }
-
- // Check the array type.
- Register rbx_klass = rbx_temp;
- __ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- 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, rsi_temp, ok_array_klass);
- // If we get here, the type check failed!
- __ jmp(bad_array_klass);
- __ BIND(ok_array_klass);
-
- // Check length.
- if (length_constant >= 0) {
- __ cmpl(Address(rdi_array, length_offset), length_constant);
- } else {
- Register rbx_vminfo = rbx_temp;
- load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
- __ cmpl(rbx_vminfo, Address(rdi_array, length_offset));
- }
- __ jcc(Assembler::notEqual, bad_array_length);
-
- Register rdx_argslot_limit = rdx_temp;
-
- // Array length checks out. Now insert any required stack slots.
- if (length_constant == -1) {
- // Form a pointer to the end of the affected region.
- __ lea(rdx_argslot_limit, Address(rax_argslot, Interpreter::stackElementSize));
- // 'stack_move' is negative number of words to insert
- // This number already accounts for elem_slots.
- Register rsi_stack_move = rsi_temp;
- load_stack_move(_masm, rsi_stack_move, rcx_recv, true);
- __ cmpptr(rsi_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 rdi_temp = rdi_array; // spill this
- insert_arg_slots(_masm, rsi_stack_move,
- rax_argslot, rbx_temp, rdi_temp);
- // reload the array since rsi was killed
- // reload from rdx_argslot_limit since rax_argslot is now decremented
- __ movptr(rdi_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 == 0) {
- __ 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 rdi_source = rdi_array;
- __ lea(rdi_source, Address(rdi_array, elem0_offset));
- Register rdx_fill_ptr = rdx_argslot_limit;
- Label loop;
- __ BIND(loop);
- __ addptr(rdx_fill_ptr, -Interpreter::stackElementSize * elem_slots);
- move_typed_arg(_masm, elem_type, true,
- Address(rdx_fill_ptr, 0), Address(rdi_source, 0),
- rbx_temp, rsi_temp);
- __ addptr(rdi_source, type2aelembytes(elem_type));
- __ cmpptr(rdx_fill_ptr, rax_argslot);
- __ jcc(Assembler::above, loop);
- } else if (length_constant == 0) {
- // nothing to copy
- } else {
- int elem_offset = elem0_offset;
- int slot_offset = length_constant * Interpreter::stackElementSize;
- for (int index = 0; index < length_constant; index++) {
- slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward
- move_typed_arg(_masm, elem_type, true,
- Address(rax_argslot, slot_offset), Address(rdi_array, elem_offset),
- rbx_temp, rsi_temp);
- elem_offset += type2aelembytes(elem_type);
- }
- }
- __ BIND(L_args_done);
-
- // Arguments are spread. Move to next method handle.
- 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;
- assert(!vmarg.uses(rarg2_required), "must be different registers");
- __ 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;
- 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?
- __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
-#undef UNPUSH_RSI
-
- break;
- }
-
- default:
- // do not require all platforms to recognize all adapter types
- __ nop();
- return;
- }
- BLOCK_COMMENT(err_msg("} Entry %s", entry_name(ek)));
- __ hlt();
-
- address me_cookie = MethodHandleEntry::start_compiled_entry(_masm, interp_entry);
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
-
- init_entry(ek, MethodHandleEntry::finish_compiled_entry(_masm, me_cookie));
-}
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -27,266 +27,12 @@
// Adapters
enum /* platform_dependent_constants */ {
- adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 15000)) LP64_ONLY(32000 DEBUG_ONLY(+ 120000))
-};
-
-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;
- friend class VMStructs;
-
- 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 {
- // The extended sender SP is above the current RicochetFrame.
- return (intptr_t*) (((address) this) + sizeof(RicochetFrame));
- }
-
- 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* bounce_offset,
- int* exception_offset,
- int* frame_size_in_words);
-
- 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;
-
- static void describe(const frame* fr, FrameValues& values, int frame_no) PRODUCT_RETURN;
+ adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 25000)) LP64_ONLY(32000 DEBUG_ONLY(+ 150000))
};
// 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,
@@ -297,9 +43,17 @@
"reference is a MH");
}
+ static void verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) NOT_DEBUG_RETURN;
+
// Similar to InterpreterMacroAssembler::jump_from_interpreted.
// Takes care of special dispatch from single stepping too.
- static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp);
+ static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp,
+ bool for_compiler_entry);
+
+ static void jump_to_lambda_form(MacroAssembler* _masm,
+ Register recv, Register method_temp,
+ Register temp2,
+ bool for_compiler_entry);
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -643,6 +643,19 @@
__ movdbl(r, Address(saved_sp, next_val_off));
}
+static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg,
+ address code_start, address code_end,
+ Label& L_ok) {
+ Label L_fail;
+ __ lea(temp_reg, ExternalAddress(code_start));
+ __ cmpptr(pc_reg, temp_reg);
+ __ jcc(Assembler::belowEqual, L_fail);
+ __ lea(temp_reg, ExternalAddress(code_end));
+ __ cmpptr(pc_reg, temp_reg);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_fail);
+}
+
static void gen_i2c_adapter(MacroAssembler *masm,
int total_args_passed,
int comp_args_on_stack,
@@ -653,9 +666,53 @@
// we may do a i2c -> c2i transition if we lose a race where compiled
// code goes non-entrant while we get args ready.
+ // Adapters can be frameless because they do not require the caller
+ // to perform additional cleanup work, such as correcting the stack pointer.
+ // An i2c adapter is frameless because the *caller* frame, which is interpreted,
+ // routinely repairs its own stack pointer (from interpreter_frame_last_sp),
+ // even if a callee has modified the stack pointer.
+ // A c2i adapter is frameless because the *callee* frame, which is interpreted,
+ // routinely repairs its caller's stack pointer (from sender_sp, which is set
+ // up via the senderSP register).
+ // In other words, if *either* the caller or callee is interpreted, we can
+ // get the stack pointer repaired after a call.
+ // This is why c2i and i2c adapters cannot be indefinitely composed.
+ // In particular, if a c2i adapter were to somehow call an i2c adapter,
+ // both caller and callee would be compiled methods, and neither would
+ // clean up the stack pointer changes performed by the two adapters.
+ // If this happens, control eventually transfers back to the compiled
+ // caller, but with an uncorrected stack, causing delayed havoc.
+
// Pick up the return address
__ movptr(rax, Address(rsp, 0));
+ if (VerifyAdapterCalls &&
+ (Interpreter::code() != NULL || StubRoutines::code1() != NULL)) {
+ // So, let's test for cascading c2i/i2c adapters right now.
+ // assert(Interpreter::contains($return_addr) ||
+ // StubRoutines::contains($return_addr),
+ // "i2c adapter must return to an interpreter frame");
+ __ block_comment("verify_i2c { ");
+ Label L_ok;
+ if (Interpreter::code() != NULL)
+ range_check(masm, rax, rdi,
+ Interpreter::code()->code_start(), Interpreter::code()->code_end(),
+ L_ok);
+ if (StubRoutines::code1() != NULL)
+ range_check(masm, rax, rdi,
+ StubRoutines::code1()->code_begin(), StubRoutines::code1()->code_end(),
+ L_ok);
+ if (StubRoutines::code2() != NULL)
+ range_check(masm, rax, rdi,
+ StubRoutines::code2()->code_begin(), StubRoutines::code2()->code_end(),
+ L_ok);
+ const char* msg = "i2c adapter must return to an interpreter frame";
+ __ block_comment(msg);
+ __ stop(msg);
+ __ bind(L_ok);
+ __ block_comment("} verify_i2ce ");
+ }
+
// Must preserve original SP for loading incoming arguments because
// we need to align the outgoing SP for compiled code.
__ movptr(rdi, rsp);
@@ -1293,6 +1350,89 @@
__ bind(done);
}
+static void verify_oop_args(MacroAssembler* masm,
+ int total_args_passed,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ Register temp_reg = rbx; // not part of any compiled calling seq
+ if (VerifyOops) {
+ for (int i = 0; i < total_args_passed; i++) {
+ if (sig_bt[i] == T_OBJECT ||
+ sig_bt[i] == T_ARRAY) {
+ VMReg r = regs[i].first();
+ assert(r->is_valid(), "bad oop arg");
+ if (r->is_stack()) {
+ __ movptr(temp_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ __ verify_oop(temp_reg);
+ } else {
+ __ verify_oop(r->as_Register());
+ }
+ }
+ }
+ }
+}
+
+static void gen_special_dispatch(MacroAssembler* masm,
+ int total_args_passed,
+ int comp_args_on_stack,
+ vmIntrinsics::ID special_dispatch,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ verify_oop_args(masm, total_args_passed, sig_bt, regs);
+
+ // Now write the args into the outgoing interpreter space
+ bool has_receiver = false;
+ Register receiver_reg = noreg;
+ int member_arg_pos = -1;
+ Register member_reg = noreg;
+ int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(special_dispatch);
+ if (ref_kind != 0) {
+ member_arg_pos = total_args_passed - 1; // trailing MemberName argument
+ member_reg = rbx; // known to be free at this point
+ has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind);
+ } else if (special_dispatch == vmIntrinsics::_invokeBasic) {
+ has_receiver = true;
+ } else {
+ guarantee(false, err_msg("special_dispatch=%d", special_dispatch));
+ }
+
+ if (member_reg != noreg) {
+ // Load the member_arg into register, if necessary.
+ assert(member_arg_pos >= 0 && member_arg_pos < total_args_passed, "oob");
+ assert(sig_bt[member_arg_pos] == T_OBJECT, "dispatch argument must be an object");
+ VMReg r = regs[member_arg_pos].first();
+ assert(r->is_valid(), "bad member arg");
+ if (r->is_stack()) {
+ __ movptr(member_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ } else {
+ // no data motion is needed
+ member_reg = r->as_Register();
+ }
+ }
+
+ if (has_receiver) {
+ // Make sure the receiver is loaded into a register.
+ assert(total_args_passed > 0, "oob");
+ assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object");
+ VMReg r = regs[0].first();
+ assert(r->is_valid(), "bad receiver arg");
+ if (r->is_stack()) {
+ // Porting note: This assumes that compiled calling conventions always
+ // pass the receiver oop in a register. If this is not true on some
+ // platform, pick a temp and load the receiver from stack.
+ assert(false, "receiver always in a register");
+ receiver_reg = rcx; // known to be free at this point
+ __ movptr(receiver_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ } else {
+ // no data motion is needed
+ receiver_reg = r->as_Register();
+ }
+ }
+
+ // Figure out which address we are really jumping to:
+ MethodHandles::generate_method_handle_dispatch(masm, special_dispatch,
+ receiver_reg, member_reg, /*for_compiler_entry:*/ true);
+}
// ---------------------------------------------------------------------------
// Generate a native wrapper for a given method. The method takes arguments
@@ -1323,14 +1463,37 @@
// transition back to thread_in_Java
// return to caller
//
-nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
+nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
methodHandle method,
int compile_id,
int total_in_args,
int comp_args_on_stack,
- BasicType *in_sig_bt,
- VMRegPair *in_regs,
+ BasicType* in_sig_bt,
+ VMRegPair* in_regs,
BasicType ret_type) {
+ if (method->is_method_handle_intrinsic()) {
+ vmIntrinsics::ID iid = method->intrinsic_id();
+ intptr_t start = (intptr_t)__ pc();
+ int vep_offset = ((intptr_t)__ pc()) - start;
+ gen_special_dispatch(masm,
+ total_in_args,
+ comp_args_on_stack,
+ method->intrinsic_id(),
+ in_sig_bt,
+ in_regs);
+ int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period
+ __ flush();
+ int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually
+ return nmethod::new_native_nmethod(method,
+ compile_id,
+ masm->code(),
+ vep_offset,
+ frame_complete,
+ stack_slots / VMRegImpl::slots_per_word,
+ in_ByteSize(-1),
+ in_ByteSize(-1),
+ (OopMapSet*)NULL);
+ }
bool is_critical_native = true;
address native_func = method->critical_native_function();
if (native_func == NULL) {
@@ -1436,7 +1599,7 @@
if (in_regs[i].first()->is_Register()) {
const Register reg = in_regs[i].first()->as_Register();
switch (in_sig_bt[i]) {
- case T_ARRAY:
+ case T_ARRAY: // critical array (uses 2 slots on LP64)
case T_BOOLEAN:
case T_BYTE:
case T_SHORT:
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -590,6 +590,19 @@
__ jmp(rcx);
}
+static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg,
+ address code_start, address code_end,
+ Label& L_ok) {
+ Label L_fail;
+ __ lea(temp_reg, ExternalAddress(code_start));
+ __ cmpptr(pc_reg, temp_reg);
+ __ jcc(Assembler::belowEqual, L_fail);
+ __ lea(temp_reg, ExternalAddress(code_end));
+ __ cmpptr(pc_reg, temp_reg);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_fail);
+}
+
static void gen_i2c_adapter(MacroAssembler *masm,
int total_args_passed,
int comp_args_on_stack,
@@ -605,9 +618,53 @@
// save code can segv when fxsave instructions find improperly
// aligned stack pointer.
+ // Adapters can be frameless because they do not require the caller
+ // to perform additional cleanup work, such as correcting the stack pointer.
+ // An i2c adapter is frameless because the *caller* frame, which is interpreted,
+ // routinely repairs its own stack pointer (from interpreter_frame_last_sp),
+ // even if a callee has modified the stack pointer.
+ // A c2i adapter is frameless because the *callee* frame, which is interpreted,
+ // routinely repairs its caller's stack pointer (from sender_sp, which is set
+ // up via the senderSP register).
+ // In other words, if *either* the caller or callee is interpreted, we can
+ // get the stack pointer repaired after a call.
+ // This is why c2i and i2c adapters cannot be indefinitely composed.
+ // In particular, if a c2i adapter were to somehow call an i2c adapter,
+ // both caller and callee would be compiled methods, and neither would
+ // clean up the stack pointer changes performed by the two adapters.
+ // If this happens, control eventually transfers back to the compiled
+ // caller, but with an uncorrected stack, causing delayed havoc.
+
// Pick up the return address
__ movptr(rax, Address(rsp, 0));
+ if (VerifyAdapterCalls &&
+ (Interpreter::code() != NULL || StubRoutines::code1() != NULL)) {
+ // So, let's test for cascading c2i/i2c adapters right now.
+ // assert(Interpreter::contains($return_addr) ||
+ // StubRoutines::contains($return_addr),
+ // "i2c adapter must return to an interpreter frame");
+ __ block_comment("verify_i2c { ");
+ Label L_ok;
+ if (Interpreter::code() != NULL)
+ range_check(masm, rax, r11,
+ Interpreter::code()->code_start(), Interpreter::code()->code_end(),
+ L_ok);
+ if (StubRoutines::code1() != NULL)
+ range_check(masm, rax, r11,
+ StubRoutines::code1()->code_begin(), StubRoutines::code1()->code_end(),
+ L_ok);
+ if (StubRoutines::code2() != NULL)
+ range_check(masm, rax, r11,
+ StubRoutines::code2()->code_begin(), StubRoutines::code2()->code_end(),
+ L_ok);
+ const char* msg = "i2c adapter must return to an interpreter frame";
+ __ block_comment(msg);
+ __ stop(msg);
+ __ bind(L_ok);
+ __ block_comment("} verify_i2ce ");
+ }
+
// Must preserve original SP for loading incoming arguments because
// we need to align the outgoing SP for compiled code.
__ movptr(r11, rsp);
@@ -1366,6 +1423,14 @@
}
+// Different signatures may require very different orders for the move
+// to avoid clobbering other arguments. There's no simple way to
+// order them safely. Compute a safe order for issuing stores and
+// break any cycles in those stores. This code is fairly general but
+// it's not necessary on the other platforms so we keep it in the
+// platform dependent code instead of moving it into a shared file.
+// (See bugs 7013347 & 7145024.)
+// Note that this code is specific to LP64.
class ComputeMoveOrder: public StackObj {
class MoveOperation: public ResourceObj {
friend class ComputeMoveOrder;
@@ -1532,6 +1597,89 @@
}
};
+static void verify_oop_args(MacroAssembler* masm,
+ int total_args_passed,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ Register temp_reg = rbx; // not part of any compiled calling seq
+ if (VerifyOops) {
+ for (int i = 0; i < total_args_passed; i++) {
+ if (sig_bt[i] == T_OBJECT ||
+ sig_bt[i] == T_ARRAY) {
+ VMReg r = regs[i].first();
+ assert(r->is_valid(), "bad oop arg");
+ if (r->is_stack()) {
+ __ movptr(temp_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ __ verify_oop(temp_reg);
+ } else {
+ __ verify_oop(r->as_Register());
+ }
+ }
+ }
+ }
+}
+
+static void gen_special_dispatch(MacroAssembler* masm,
+ int total_args_passed,
+ int comp_args_on_stack,
+ vmIntrinsics::ID special_dispatch,
+ const BasicType* sig_bt,
+ const VMRegPair* regs) {
+ verify_oop_args(masm, total_args_passed, sig_bt, regs);
+
+ // Now write the args into the outgoing interpreter space
+ bool has_receiver = false;
+ Register receiver_reg = noreg;
+ int member_arg_pos = -1;
+ Register member_reg = noreg;
+ int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(special_dispatch);
+ if (ref_kind != 0) {
+ member_arg_pos = total_args_passed - 1; // trailing MemberName argument
+ member_reg = rbx; // known to be free at this point
+ has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind);
+ } else if (special_dispatch == vmIntrinsics::_invokeBasic) {
+ has_receiver = true;
+ } else {
+ guarantee(false, err_msg("special_dispatch=%d", special_dispatch));
+ }
+
+ if (member_reg != noreg) {
+ // Load the member_arg into register, if necessary.
+ assert(member_arg_pos >= 0 && member_arg_pos < total_args_passed, "oob");
+ assert(sig_bt[member_arg_pos] == T_OBJECT, "dispatch argument must be an object");
+ VMReg r = regs[member_arg_pos].first();
+ assert(r->is_valid(), "bad member arg");
+ if (r->is_stack()) {
+ __ movptr(member_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ } else {
+ // no data motion is needed
+ member_reg = r->as_Register();
+ }
+ }
+
+ if (has_receiver) {
+ // Make sure the receiver is loaded into a register.
+ assert(total_args_passed > 0, "oob");
+ assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object");
+ VMReg r = regs[0].first();
+ assert(r->is_valid(), "bad receiver arg");
+ if (r->is_stack()) {
+ // Porting note: This assumes that compiled calling conventions always
+ // pass the receiver oop in a register. If this is not true on some
+ // platform, pick a temp and load the receiver from stack.
+ assert(false, "receiver always in a register");
+ receiver_reg = j_rarg0; // known to be free at this point
+ __ movptr(receiver_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize));
+ } else {
+ // no data motion is needed
+ receiver_reg = r->as_Register();
+ }
+ }
+
+ // Figure out which address we are really jumping to:
+ MethodHandles::generate_method_handle_dispatch(masm, special_dispatch,
+ receiver_reg, member_reg, /*for_compiler_entry:*/ true);
+}
// ---------------------------------------------------------------------------
// Generate a native wrapper for a given method. The method takes arguments
@@ -1539,14 +1687,60 @@
// convention (handlizes oops, etc), transitions to native, makes the call,
// returns to java state (possibly blocking), unhandlizes any result and
// returns.
-nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
+//
+// Critical native functions are a shorthand for the use of
+// GetPrimtiveArrayCritical and disallow the use of any other JNI
+// functions. The wrapper is expected to unpack the arguments before
+// passing them to the callee and perform checks before and after the
+// native call to ensure that they GC_locker
+// lock_critical/unlock_critical semantics are followed. Some other
+// parts of JNI setup are skipped like the tear down of the JNI handle
+// block and the check for pending exceptions it's impossible for them
+// to be thrown.
+//
+// They are roughly structured like this:
+// if (GC_locker::needs_gc())
+// SharedRuntime::block_for_jni_critical();
+// tranistion to thread_in_native
+// unpack arrray arguments and call native entry point
+// check for safepoint in progress
+// check if any thread suspend flags are set
+// call into JVM and possible unlock the JNI critical
+// if a GC was suppressed while in the critical native.
+// transition back to thread_in_Java
+// return to caller
+//
+nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
methodHandle method,
int compile_id,
int total_in_args,
int comp_args_on_stack,
- BasicType *in_sig_bt,
- VMRegPair *in_regs,
+ BasicType* in_sig_bt,
+ VMRegPair* in_regs,
BasicType ret_type) {
+ if (method->is_method_handle_intrinsic()) {
+ vmIntrinsics::ID iid = method->intrinsic_id();
+ intptr_t start = (intptr_t)__ pc();
+ int vep_offset = ((intptr_t)__ pc()) - start;
+ gen_special_dispatch(masm,
+ total_in_args,
+ comp_args_on_stack,
+ method->intrinsic_id(),
+ in_sig_bt,
+ in_regs);
+ int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period
+ __ flush();
+ int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually
+ return nmethod::new_native_nmethod(method,
+ compile_id,
+ masm->code(),
+ vep_offset,
+ frame_complete,
+ stack_slots / VMRegImpl::slots_per_word,
+ in_ByteSize(-1),
+ in_ByteSize(-1),
+ (OopMapSet*)NULL);
+ }
bool is_critical_native = true;
address native_func = method->critical_native_function();
if (native_func == NULL) {
@@ -1658,7 +1852,7 @@
case T_SHORT:
case T_CHAR:
case T_INT: single_slots++; break;
- case T_ARRAY:
+ case T_ARRAY: // specific to LP64 (7145024)
case T_LONG: double_slots++; break;
default: ShouldNotReachHere();
}
--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -2327,12 +2327,6 @@
CAST_FROM_FN_PTR(address, SharedRuntime::d2l));
// Build this early so it's available for the interpreter
- StubRoutines::_throw_WrongMethodTypeException_entry =
- generate_throw_exception("WrongMethodTypeException throw_exception",
- CAST_FROM_FN_PTR(address, SharedRuntime::throw_WrongMethodTypeException),
- rax, rcx);
-
- // Build this early so it's available for the interpreter
StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError));
}
--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3102,14 +3102,6 @@
StubRoutines::x86::_verify_mxcsr_entry = generate_verify_mxcsr();
- // Build this early so it's available for the interpreter. Stub
- // expects the required and actual types as register arguments in
- // j_rarg0 and j_rarg1 respectively.
- StubRoutines::_throw_WrongMethodTypeException_entry =
- generate_throw_exception("WrongMethodTypeException throw_exception",
- CAST_FROM_FN_PTR(address, SharedRuntime::throw_WrongMethodTypeException),
- rax, rcx);
-
// Build this early so it's available for the interpreter.
StubRoutines::_throw_StackOverflowError_entry =
generate_throw_exception("StackOverflowError throw_exception",
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -710,9 +710,9 @@
// Need to differentiate between igetfield, agetfield, bgetfield etc.
// because they are different sizes.
// Use the type from the constant pool cache
- __ shrl(rdx, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask rdx for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask rdx after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmpl(rdx, btos);
__ jcc(Assembler::notEqual, notByte);
__ load_signed_byte(rax, field_address);
@@ -1513,7 +1513,6 @@
case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
- case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break;
case Interpreter::java_lang_math_sin : // fall thru
case Interpreter::java_lang_math_cos : // fall thru
@@ -1526,7 +1525,9 @@
case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break;
case Interpreter::java_lang_ref_reference_get
: entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break;
- default : ShouldNotReachHere(); break;
+ default:
+ fatal(err_msg("unexpected method kind: %d", kind));
+ break;
}
if (entry_point) return entry_point;
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -683,9 +683,9 @@
// Need to differentiate between igetfield, agetfield, bgetfield etc.
// because they are different sizes.
// Use the type from the constant pool cache
- __ shrl(rdx, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask edx for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask edx after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmpl(rdx, atos);
__ jcc(Assembler::notEqual, notObj);
@@ -1524,12 +1524,11 @@
switch (kind) {
case Interpreter::zerolocals : break;
case Interpreter::zerolocals_synchronized: synchronized = true; break;
- case Interpreter::native : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); break;
- case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(true); break;
- case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break;
- case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break;
- case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break;
- case Interpreter::method_handle : entry_point = ((InterpreterGenerator*) this)->generate_method_handle_entry();break;
+ case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break;
+ case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break;
+ case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
+ case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
+ case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
case Interpreter::java_lang_math_sin : // fall thru
case Interpreter::java_lang_math_cos : // fall thru
@@ -1539,10 +1538,12 @@
case Interpreter::java_lang_math_log10 : // fall thru
case Interpreter::java_lang_math_sqrt : // fall thru
case Interpreter::java_lang_math_pow : // fall thru
- case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break;
+ case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break;
case Interpreter::java_lang_ref_reference_get
: entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break;
- default : ShouldNotReachHere(); break;
+ default:
+ fatal(err_msg("unexpected method kind: %d", kind));
+ break;
}
if (entry_point) {
--- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -446,13 +446,13 @@
const Register cache = rcx;
const Register index = rdx;
- resolve_cache_and_index(f1_oop, rax, cache, index, wide ? sizeof(u2) : sizeof(u1));
+ resolve_cache_and_index(f12_oop, rax, cache, index, wide ? sizeof(u2) : sizeof(u1));
if (VerifyOops) {
__ verify_oop(rax);
}
Label L_done, L_throw_exception;
- const Register con_klass_temp = rcx; // same as Rcache
+ const Register con_klass_temp = rcx; // same as cache
__ load_klass(con_klass_temp, rax);
__ cmpptr(con_klass_temp, ExternalAddress((address)Universe::systemObjArrayKlassObj_addr()));
__ jcc(Assembler::notEqual, L_done);
@@ -2084,15 +2084,15 @@
Register Rcache,
Register index,
size_t index_size) {
- Register temp = rbx;
-
+ const Register temp = rbx;
assert_different_registers(result, Rcache, index, temp);
Label resolved;
- if (byte_no == f1_oop) {
- // We are resolved if the f1 field contains a non-null object (CallSite, etc.)
- // This kind of CP cache entry does not need to match the flags byte, because
+ if (byte_no == f12_oop) {
+ // We are resolved if the f1 field contains a non-null object (CallSite, MethodType, etc.)
+ // This kind of CP cache entry does not need to match bytecode_1 or bytecode_2, because
// there is a 1-1 relation between bytecode type and CP entry type.
+ // The caller will also load a methodOop from f2.
assert(result != noreg, ""); //else do cmpptr(Address(...), (int32_t) NULL_WORD)
__ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
__ movptr(result, Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()));
@@ -2112,15 +2112,18 @@
case Bytecodes::_getstatic : // fall through
case Bytecodes::_putstatic : // fall through
case Bytecodes::_getfield : // fall through
- case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break;
+ case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break;
case Bytecodes::_invokevirtual : // fall through
case Bytecodes::_invokespecial : // fall through
case Bytecodes::_invokestatic : // fall through
- case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break;
- case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break;
- case Bytecodes::_fast_aldc : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
- case Bytecodes::_fast_aldc_w : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
- default : ShouldNotReachHere(); break;
+ case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break;
+ case Bytecodes::_invokehandle : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); break;
+ case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break;
+ case Bytecodes::_fast_aldc : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
+ case Bytecodes::_fast_aldc_w : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break;
+ default:
+ fatal(err_msg("unexpected bytecode: %s", Bytecodes::name(bytecode())));
+ break;
}
__ movl(temp, (int)bytecode());
__ call_VM(noreg, entry, temp);
@@ -2149,7 +2152,7 @@
__ movl(flags, Address(cache, index, Address::times_ptr,
in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset())));
- // klass overwrite register
+ // klass overwrite register
if (is_static) {
__ movptr(obj, Address(cache, index, Address::times_ptr,
in_bytes(cp_base_offset + ConstantPoolCacheEntry::f1_offset())));
@@ -2161,7 +2164,7 @@
Register itable_index,
Register flags,
bool is_invokevirtual,
- bool is_invokevfinal /*unused*/,
+ bool is_invokevfinal, /*unused*/
bool is_invokedynamic) {
// setup registers
const Register cache = rcx;
@@ -2171,28 +2174,33 @@
assert_different_registers(itable_index, flags);
assert_different_registers(itable_index, cache, index);
// determine constant pool cache field offsets
+ assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
const int method_offset = in_bytes(
constantPoolCacheOopDesc::base_offset() +
- (is_invokevirtual
+ ((byte_no == f2_byte)
? ConstantPoolCacheEntry::f2_offset()
- : ConstantPoolCacheEntry::f1_offset()
- )
- );
+ : ConstantPoolCacheEntry::f1_offset()));
const int flags_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
ConstantPoolCacheEntry::flags_offset());
// access constant pool cache fields
const int index_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
ConstantPoolCacheEntry::f2_offset());
- if (byte_no == f1_oop) {
- // Resolved f1_oop goes directly into 'method' register.
- assert(is_invokedynamic, "");
- resolve_cache_and_index(byte_no, method, cache, index, sizeof(u4));
+ if (byte_no == f12_oop) {
+ // Resolved f1_oop (CallSite, MethodType, etc.) goes into 'itable_index'.
+ // Resolved f2_oop (methodOop invoker) will go into 'method' (at index_offset).
+ // See ConstantPoolCacheEntry::set_dynamic_call and set_method_handle.
+ size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
+ resolve_cache_and_index(byte_no, itable_index, cache, index, index_size);
+ __ movptr(method, Address(cache, index, Address::times_ptr, index_offset));
+ itable_index = noreg; // hack to disable load below
} else {
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
__ movptr(method, Address(cache, index, Address::times_ptr, method_offset));
}
if (itable_index != noreg) {
+ // pick up itable index from f2 also:
+ assert(byte_no == f1_byte, "already picked up f1");
__ movptr(itable_index, Address(cache, index, Address::times_ptr, index_offset));
}
__ movl(flags, Address(cache, index, Address::times_ptr, flags_offset));
@@ -2260,10 +2268,10 @@
Label Done, notByte, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble;
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
assert(btos == 0, "change code, btos != 0");
// btos
- __ andptr(flags, 0x0f);
+ __ andptr(flags, ConstantPoolCacheEntry::tos_state_mask);
__ jcc(Assembler::notZero, notByte);
__ load_signed_byte(rax, lo );
@@ -2415,9 +2423,9 @@
__ movl(rcx, Address(rax, rdx, Address::times_ptr, in_bytes(cp_base_offset +
ConstantPoolCacheEntry::flags_offset())));
__ mov(rbx, rsp);
- __ shrl(rcx, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask rcx for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(rcx, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask rcx after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ cmpl(rcx, ltos);
__ jccb(Assembler::equal, two_word);
__ cmpl(rcx, dtos);
@@ -2467,7 +2475,7 @@
Label notVolatile, Done;
__ movl(rdx, flags);
- __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
__ andl(rdx, 0x1);
// field addresses
@@ -2476,9 +2484,9 @@
Label notByte, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble;
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
assert(btos == 0, "change code, btos != 0");
- __ andl(flags, 0x0f);
+ __ andl(flags, ConstantPoolCacheEntry::tos_state_mask);
__ jcc(Assembler::notZero, notByte);
// btos
@@ -2719,7 +2727,7 @@
// volatile_barrier( );
Label notVolatile, Done;
- __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
__ andl(rdx, 0x1);
// Check for volatile store
__ testl(rdx, rdx);
@@ -2885,19 +2893,29 @@
}
-void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) {
+void TemplateTable::prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register index, // itable index, MethodType, etc.
+ Register recv, // if caller wants to see it
+ Register flags // if caller wants to test it
+ ) {
// determine flags
- Bytecodes::Code code = bytecode();
+ const Bytecodes::Code code = bytecode();
const bool is_invokeinterface = code == Bytecodes::_invokeinterface;
const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
+ const bool is_invokehandle = code == Bytecodes::_invokehandle;
const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
const bool is_invokespecial = code == Bytecodes::_invokespecial;
- const bool load_receiver = (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic);
- const bool receiver_null_check = is_invokespecial;
- const bool save_flags = is_invokeinterface || is_invokevirtual;
+ const bool load_receiver = (recv != noreg);
+ const bool save_flags = (flags != noreg);
+ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), "");
+ assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal");
+ assert(flags == noreg || flags == rdx, "");
+ assert(recv == noreg || recv == rcx, "");
+
// setup registers & access constant pool cache
- const Register recv = rcx;
- const Register flags = rdx;
+ if (recv == noreg) recv = rcx;
+ if (flags == noreg) flags = rdx;
assert_different_registers(method, index, recv, flags);
// save 'interpreter return address'
@@ -2905,37 +2923,43 @@
load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
+ // maybe push appendix to arguments (just before return address)
+ if (is_invokedynamic || is_invokehandle) {
+ Label L_no_push;
+ __ verify_oop(index);
+ __ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift));
+ __ jccb(Assembler::zero, L_no_push);
+ // Push the appendix as a trailing parameter.
+ // This must be done before we get the receiver,
+ // since the parameter_size includes it.
+ __ push(index); // push appendix (MethodType, CallSite, etc.)
+ __ bind(L_no_push);
+ }
+
// load receiver if needed (note: no return address pushed yet)
if (load_receiver) {
- assert(!is_invokedynamic, "");
__ movl(recv, flags);
- __ andl(recv, 0xFF);
- // recv count is 0 based?
- Address recv_addr(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1));
+ __ andl(recv, ConstantPoolCacheEntry::parameter_size_mask);
+ const int no_return_pc_pushed_yet = -1; // argument slot correction before we push return address
+ const int receiver_is_at_end = -1; // back off one slot to get receiver
+ Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end);
__ movptr(recv, recv_addr);
__ verify_oop(recv);
}
- // do null check if needed
- if (receiver_null_check) {
- __ null_check(recv);
- }
-
if (save_flags) {
__ mov(rsi, flags);
}
// compute return type
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask flags for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask flags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
// load return address
{
- address table_addr;
- if (is_invokeinterface || is_invokedynamic)
- table_addr = (address)Interpreter::return_5_addrs_by_index_table();
- else
- table_addr = (address)Interpreter::return_3_addrs_by_index_table();
+ const address table_addr = (is_invokeinterface || is_invokedynamic) ?
+ (address)Interpreter::return_5_addrs_by_index_table() :
+ (address)Interpreter::return_3_addrs_by_index_table();
ExternalAddress table(table_addr);
__ movptr(flags, ArrayAddress(table, Address(noreg, flags, Address::times_ptr)));
}
@@ -2943,7 +2967,7 @@
// push return address
__ push(flags);
- // Restore flag value from the constant pool cache, and restore rsi
+ // Restore flags value from the constant pool cache, and restore rsi
// for later null checks. rsi is the bytecode pointer
if (save_flags) {
__ mov(flags, rsi);
@@ -2952,22 +2976,26 @@
}
-void TemplateTable::invokevirtual_helper(Register index, Register recv,
- Register flags) {
-
+void TemplateTable::invokevirtual_helper(Register index,
+ Register recv,
+ Register flags) {
// Uses temporary registers rax, rdx
assert_different_registers(index, recv, rax, rdx);
+ assert(index == rbx, "");
+ assert(recv == rcx, "");
// Test for an invoke of a final method
Label notFinal;
__ movl(rax, flags);
- __ andl(rax, (1 << ConstantPoolCacheEntry::vfinalMethod));
+ __ andl(rax, (1 << ConstantPoolCacheEntry::is_vfinal_shift));
__ jcc(Assembler::zero, notFinal);
- Register method = index; // method must be rbx,
- assert(method == rbx, "methodOop must be rbx, for interpreter calling convention");
+ const Register method = index; // method must be rbx
+ assert(method == rbx,
+ "methodOop must be rbx for interpreter calling convention");
// do the call - the index is actually the method to call
+ // that is, f2 is a vtable index if !is_vfinal, else f2 is a methodOop
__ verify_oop(method);
// It's final, need a null check here!
@@ -2982,7 +3010,6 @@
// get receiver klass
__ null_check(recv, oopDesc::klass_offset_in_bytes());
- // Keep recv in rcx for callee expects it there
__ load_klass(rax, recv);
__ verify_oop(rax);
@@ -2990,9 +3017,7 @@
__ profile_virtual_call(rax, rdi, rdx);
// get target methodOop & entry point
- const int base = instanceKlass::vtable_start_offset() * wordSize;
- assert(vtableEntry::size() * wordSize == 4, "adjust the scaling in the code below");
- __ movptr(method, Address(rax, index, Address::times_ptr, base + vtableEntry::method_offset_in_bytes()));
+ __ lookup_virtual_method(rax, index, method);
__ jump_from_interpreted(method, rdx);
}
@@ -3000,9 +3025,12 @@
void TemplateTable::invokevirtual(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f2_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
-
- // rbx,: index
+ prepare_invoke(byte_no,
+ rbx, // method or vtable index
+ noreg, // unused itable index
+ rcx, rdx); // recv, flags
+
+ // rbx: index
// rcx: receiver
// rdx: flags
@@ -3013,7 +3041,10 @@
void TemplateTable::invokespecial(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
+ prepare_invoke(byte_no, rbx, noreg, // get f1 methodOop
+ rcx); // get receiver also for null check
+ __ verify_oop(rcx);
+ __ null_check(rcx);
// do the call
__ verify_oop(rbx);
__ profile_call(rax);
@@ -3024,7 +3055,7 @@
void TemplateTable::invokestatic(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
+ prepare_invoke(byte_no, rbx); // get f1 methodOop
// do the call
__ verify_oop(rbx);
__ profile_call(rax);
@@ -3042,10 +3073,11 @@
void TemplateTable::invokeinterface(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rax, rbx, byte_no);
-
- // rax,: Interface
- // rbx,: index
+ prepare_invoke(byte_no, rax, rbx, // get f1 klassOop, f2 itable index
+ rcx, rdx); // recv, flags
+
+ // rax: interface klass (from f1)
+ // rbx: itable index (from f2)
// rcx: receiver
// rdx: flags
@@ -3055,7 +3087,7 @@
// another compliant java compiler.
Label notMethod;
__ movl(rdi, rdx);
- __ andl(rdi, (1 << ConstantPoolCacheEntry::methodInterface));
+ __ andl(rdi, (1 << ConstantPoolCacheEntry::is_forced_virtual_shift));
__ jcc(Assembler::zero, notMethod);
invokevirtual_helper(rbx, rcx, rdx);
@@ -3063,6 +3095,7 @@
// Get receiver klass into rdx - also a null check
__ restore_locals(); // restore rdi
+ __ null_check(rcx, oopDesc::klass_offset_in_bytes());
__ load_klass(rdx, rcx);
__ verify_oop(rdx);
@@ -3077,7 +3110,7 @@
rbx, rsi,
no_such_interface);
- // rbx,: methodOop to call
+ // rbx: methodOop to call
// rcx: receiver
// Check for abstract method error
// Note: This should be done more efficiently via a throw_abstract_method_error
@@ -3116,9 +3149,39 @@
__ should_not_reach_here();
}
+void TemplateTable::invokehandle(int byte_no) {
+ transition(vtos, vtos);
+ assert(byte_no == f12_oop, "use this argument");
+ const Register rbx_method = rbx; // (from f2)
+ const Register rax_mtype = rax; // (from f1)
+ const Register rcx_recv = rcx;
+ const Register rdx_flags = rdx;
+
+ if (!EnableInvokeDynamic) {
+ // rewriter does not generate this bytecode
+ __ should_not_reach_here();
+ return;
+ }
+
+ prepare_invoke(byte_no,
+ rbx_method, rax_mtype, // get f2 methodOop, f1 MethodType
+ rcx_recv);
+ __ verify_oop(rbx_method);
+ __ verify_oop(rcx_recv);
+ __ null_check(rcx_recv);
+
+ // Note: rax_mtype is already pushed (if necessary) by prepare_invoke
+
+ // FIXME: profile the LambdaForm also
+ __ profile_final_call(rax);
+
+ __ jump_from_interpreted(rbx_method, rdx);
+}
+
+
void TemplateTable::invokedynamic(int byte_no) {
transition(vtos, vtos);
- assert(byte_no == f1_oop, "use this argument");
+ assert(byte_no == f12_oop, "use this argument");
if (!EnableInvokeDynamic) {
// We should not encounter this bytecode if !EnableInvokeDynamic.
@@ -3131,26 +3194,23 @@
return;
}
- prepare_invoke(rax, rbx, byte_no);
-
- // rax: CallSite object (f1)
- // rbx: unused (f2)
- // rcx: receiver address
- // rdx: flags (unused)
-
- Register rax_callsite = rax;
- Register rcx_method_handle = rcx;
+ const Register rbx_method = rbx;
+ const Register rax_callsite = rax;
+
+ prepare_invoke(byte_no, rbx_method, rax_callsite);
+
+ // rax: CallSite object (from f1)
+ // rbx: MH.linkToCallSite method (from f2)
+
+ // Note: rax_callsite is already pushed by prepare_invoke
// %%% should make a type profile for any invokedynamic that takes a ref argument
// profile this call
__ profile_call(rsi);
__ verify_oop(rax_callsite);
- __ load_heap_oop(rcx_method_handle, Address(rax_callsite, __ delayed_value(java_lang_invoke_CallSite::target_offset_in_bytes, rdx)));
- __ null_check(rcx_method_handle);
- __ verify_oop(rcx_method_handle);
- __ prepare_to_jump_from_interpreted();
- __ jump_to_method_handle_entry(rcx_method_handle, rdx);
+
+ __ jump_from_interpreted(rbx_method, rdx);
}
//----------------------------------------------------------------------------------------------------
--- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -25,10 +25,15 @@
#ifndef CPU_X86_VM_TEMPLATETABLE_X86_32_HPP
#define CPU_X86_VM_TEMPLATETABLE_X86_32_HPP
- static void prepare_invoke(Register method, Register index, int byte_no);
+ static void prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register index = noreg, // itable index, MethodType, etc.
+ Register recv = noreg, // if caller wants to see it
+ Register flags = noreg // if caller wants to test it
+ );
static void invokevirtual_helper(Register index, Register recv,
Register flags);
- static void volatile_barrier(Assembler::Membar_mask_bits order_constraint );
+ static void volatile_barrier(Assembler::Membar_mask_bits order_constraint);
// Helpers
static void index_check(Register array, Register index);
--- a/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -458,7 +458,7 @@
const Register cache = rcx;
const Register index = rdx;
- resolve_cache_and_index(f1_oop, rax, cache, index, wide ? sizeof(u2) : sizeof(u1));
+ resolve_cache_and_index(f12_oop, rax, cache, index, wide ? sizeof(u2) : sizeof(u1));
if (VerifyOops) {
__ verify_oop(rax);
}
@@ -2125,10 +2125,11 @@
assert_different_registers(result, Rcache, index, temp);
Label resolved;
- if (byte_no == f1_oop) {
- // We are resolved if the f1 field contains a non-null object (CallSite, etc.)
- // This kind of CP cache entry does not need to match the flags byte, because
+ if (byte_no == f12_oop) {
+ // We are resolved if the f1 field contains a non-null object (CallSite, MethodType, etc.)
+ // This kind of CP cache entry does not need to match bytecode_1 or bytecode_2, because
// there is a 1-1 relation between bytecode type and CP entry type.
+ // The caller will also load a methodOop from f2.
assert(result != noreg, ""); //else do cmpptr(Address(...), (int32_t) NULL_WORD)
__ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
__ movptr(result, Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()));
@@ -2157,6 +2158,9 @@
case Bytecodes::_invokeinterface:
entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke);
break;
+ case Bytecodes::_invokehandle:
+ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle);
+ break;
case Bytecodes::_invokedynamic:
entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic);
break;
@@ -2167,7 +2171,7 @@
entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc);
break;
default:
- ShouldNotReachHere();
+ fatal(err_msg("unexpected bytecode: %s", Bytecodes::name(bytecode())));
break;
}
__ movl(temp, (int) bytecode());
@@ -2180,7 +2184,7 @@
__ bind(resolved);
}
-// The Rcache and index registers must be set before call
+// The cache and index registers must be set before call
void TemplateTable::load_field_cp_cache_entry(Register obj,
Register cache,
Register index,
@@ -2191,17 +2195,17 @@
ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset();
// Field offset
- __ movptr(off, Address(cache, index, Address::times_8,
+ __ movptr(off, Address(cache, index, Address::times_ptr,
in_bytes(cp_base_offset +
ConstantPoolCacheEntry::f2_offset())));
// Flags
- __ movl(flags, Address(cache, index, Address::times_8,
+ __ movl(flags, Address(cache, index, Address::times_ptr,
in_bytes(cp_base_offset +
ConstantPoolCacheEntry::flags_offset())));
// klass overwrite register
if (is_static) {
- __ movptr(obj, Address(cache, index, Address::times_8,
+ __ movptr(obj, Address(cache, index, Address::times_ptr,
in_bytes(cp_base_offset +
ConstantPoolCacheEntry::f1_offset())));
}
@@ -2222,9 +2226,10 @@
assert_different_registers(itable_index, flags);
assert_different_registers(itable_index, cache, index);
// determine constant pool cache field offsets
+ assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
const int method_offset = in_bytes(
constantPoolCacheOopDesc::base_offset() +
- (is_invokevirtual
+ ((byte_no == f2_byte)
? ConstantPoolCacheEntry::f2_offset()
: ConstantPoolCacheEntry::f1_offset()));
const int flags_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
@@ -2233,15 +2238,21 @@
const int index_offset = in_bytes(constantPoolCacheOopDesc::base_offset() +
ConstantPoolCacheEntry::f2_offset());
- if (byte_no == f1_oop) {
- // Resolved f1_oop goes directly into 'method' register.
- assert(is_invokedynamic, "");
- resolve_cache_and_index(byte_no, method, cache, index, sizeof(u4));
+ if (byte_no == f12_oop) {
+ // Resolved f1_oop (CallSite, MethodType, etc.) goes into 'itable_index'.
+ // Resolved f2_oop (methodOop invoker) will go into 'method' (at index_offset).
+ // See ConstantPoolCacheEntry::set_dynamic_call and set_method_handle.
+ size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
+ resolve_cache_and_index(byte_no, itable_index, cache, index, index_size);
+ __ movptr(method, Address(cache, index, Address::times_ptr, index_offset));
+ itable_index = noreg; // hack to disable load below
} else {
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
__ movptr(method, Address(cache, index, Address::times_ptr, method_offset));
}
if (itable_index != noreg) {
+ // pick up itable index from f2 also:
+ assert(byte_no == f1_byte, "already picked up f1");
__ movptr(itable_index, Address(cache, index, Address::times_ptr, index_offset));
}
__ movl(flags, Address(cache, index, Address::times_ptr, flags_offset));
@@ -2317,10 +2328,11 @@
Label Done, notByte, notInt, notShort, notChar,
notLong, notFloat, notObj, notDouble;
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask edx after the above shift
assert(btos == 0, "change code, btos != 0");
- __ andl(flags, 0x0F);
+ __ andl(flags, ConstantPoolCacheEntry::tos_state_mask);
__ jcc(Assembler::notZero, notByte);
// btos
__ load_signed_byte(rax, field);
@@ -2466,10 +2478,9 @@
Address::times_8,
in_bytes(cp_base_offset +
ConstantPoolCacheEntry::flags_offset())));
- __ shrl(c_rarg3, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask rcx for tosBits after the
- // above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(c_rarg3, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask rcx after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
__ movptr(c_rarg1, at_tos_p1()); // initially assume a one word jvalue
__ cmpl(c_rarg3, ltos);
__ cmovptr(Assembler::equal,
@@ -2516,7 +2527,7 @@
Label notVolatile, Done;
__ movl(rdx, flags);
- __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
__ andl(rdx, 0x1);
// field address
@@ -2525,10 +2536,10 @@
Label notByte, notInt, notShort, notChar,
notLong, notFloat, notObj, notDouble;
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
assert(btos == 0, "change code, btos != 0");
- __ andl(flags, 0x0f);
+ __ andl(flags, ConstantPoolCacheEntry::tos_state_mask);
__ jcc(Assembler::notZero, notByte);
// btos
@@ -2751,7 +2762,7 @@
// Assembler::StoreStore));
Label notVolatile;
- __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
__ andl(rdx, 0x1);
// Get object from stack
@@ -2832,7 +2843,7 @@
// __ movl(rdx, Address(rcx, rbx, Address::times_8,
// in_bytes(constantPoolCacheOopDesc::base_offset() +
// ConstantPoolCacheEntry::flags_offset())));
- // __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ // __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
// __ andl(rdx, 0x1);
// }
__ movptr(rbx, Address(rcx, rbx, Address::times_8,
@@ -2920,7 +2931,7 @@
// __ movl(rdx, Address(rcx, rdx, Address::times_8,
// in_bytes(constantPoolCacheOopDesc::base_offset() +
// ConstantPoolCacheEntry::flags_offset())));
- // __ shrl(rdx, ConstantPoolCacheEntry::volatileField);
+ // __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
// __ testl(rdx, 0x1);
// __ jcc(Assembler::zero, notVolatile);
// __ membar(Assembler::LoadLoad);
@@ -2940,19 +2951,29 @@
ShouldNotReachHere();
}
-void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) {
+void TemplateTable::prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register index, // itable index, MethodType, etc.
+ Register recv, // if caller wants to see it
+ Register flags // if caller wants to test it
+ ) {
// determine flags
- Bytecodes::Code code = bytecode();
+ const Bytecodes::Code code = bytecode();
const bool is_invokeinterface = code == Bytecodes::_invokeinterface;
const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
+ const bool is_invokehandle = code == Bytecodes::_invokehandle;
const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
const bool is_invokespecial = code == Bytecodes::_invokespecial;
- const bool load_receiver = (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic);
- const bool receiver_null_check = is_invokespecial;
- const bool save_flags = is_invokeinterface || is_invokevirtual;
+ const bool load_receiver = (recv != noreg);
+ const bool save_flags = (flags != noreg);
+ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), "");
+ assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal");
+ assert(flags == noreg || flags == rdx, "");
+ assert(recv == noreg || recv == rcx, "");
+
// setup registers & access constant pool cache
- const Register recv = rcx;
- const Register flags = rdx;
+ if (recv == noreg) recv = rcx;
+ if (flags == noreg) flags = rdx;
assert_different_registers(method, index, recv, flags);
// save 'interpreter return address'
@@ -2960,36 +2981,44 @@
load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
- // load receiver if needed (note: no return address pushed yet)
+ // maybe push appendix to arguments (just before return address)
+ if (is_invokedynamic || is_invokehandle) {
+ Label L_no_push;
+ __ verify_oop(index);
+ __ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift));
+ __ jccb(Assembler::zero, L_no_push);
+ // Push the appendix as a trailing parameter.
+ // This must be done before we get the receiver,
+ // since the parameter_size includes it.
+ __ push(index); // push appendix (MethodType, CallSite, etc.)
+ __ bind(L_no_push);
+ }
+
+ // load receiver if needed (after appendix is pushed so parameter size is correct)
+ // Note: no return address pushed yet
if (load_receiver) {
- assert(!is_invokedynamic, "");
__ movl(recv, flags);
- __ andl(recv, 0xFF);
- Address recv_addr(rsp, recv, Address::times_8, -Interpreter::expr_offset_in_bytes(1));
+ __ andl(recv, ConstantPoolCacheEntry::parameter_size_mask);
+ const int no_return_pc_pushed_yet = -1; // argument slot correction before we push return address
+ const int receiver_is_at_end = -1; // back off one slot to get receiver
+ Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end);
__ movptr(recv, recv_addr);
__ verify_oop(recv);
}
- // do null check if needed
- if (receiver_null_check) {
- __ null_check(recv);
- }
-
if (save_flags) {
__ movl(r13, flags);
}
// compute return type
- __ shrl(flags, ConstantPoolCacheEntry::tosBits);
- // Make sure we don't need to mask flags for tosBits after the above shift
- ConstantPoolCacheEntry::verify_tosBits();
+ __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
+ // Make sure we don't need to mask flags after the above shift
+ ConstantPoolCacheEntry::verify_tos_state_shift();
// load return address
{
- address table_addr;
- if (is_invokeinterface || is_invokedynamic)
- table_addr = (address)Interpreter::return_5_addrs_by_index_table();
- else
- table_addr = (address)Interpreter::return_3_addrs_by_index_table();
+ const address table_addr = (is_invokeinterface || is_invokedynamic) ?
+ (address)Interpreter::return_5_addrs_by_index_table() :
+ (address)Interpreter::return_3_addrs_by_index_table();
ExternalAddress table(table_addr);
__ lea(rscratch1, table);
__ movptr(flags, Address(rscratch1, flags, Address::times_ptr));
@@ -2998,7 +3027,7 @@
// push return address
__ push(flags);
- // Restore flag field from the constant pool cache, and restore esi
+ // Restore flags value from the constant pool cache, and restore rsi
// for later null checks. r13 is the bytecode pointer
if (save_flags) {
__ movl(flags, r13);
@@ -3012,11 +3041,13 @@
Register flags) {
// Uses temporary registers rax, rdx
assert_different_registers(index, recv, rax, rdx);
+ assert(index == rbx, "");
+ assert(recv == rcx, "");
// Test for an invoke of a final method
Label notFinal;
__ movl(rax, flags);
- __ andl(rax, (1 << ConstantPoolCacheEntry::vfinalMethod));
+ __ andl(rax, (1 << ConstantPoolCacheEntry::is_vfinal_shift));
__ jcc(Assembler::zero, notFinal);
const Register method = index; // method must be rbx
@@ -3024,6 +3055,7 @@
"methodOop must be rbx for interpreter calling convention");
// do the call - the index is actually the method to call
+ // that is, f2 is a vtable index if !is_vfinal, else f2 is a methodOop
__ verify_oop(method);
// It's final, need a null check here!
@@ -3039,20 +3071,13 @@
// get receiver klass
__ null_check(recv, oopDesc::klass_offset_in_bytes());
__ load_klass(rax, recv);
-
__ verify_oop(rax);
// profile this call
__ profile_virtual_call(rax, r14, rdx);
// get target methodOop & entry point
- const int base = instanceKlass::vtable_start_offset() * wordSize;
- assert(vtableEntry::size() * wordSize == 8,
- "adjust the scaling in the code below");
- __ movptr(method, Address(rax, index,
- Address::times_8,
- base + vtableEntry::method_offset_in_bytes()));
- __ movptr(rdx, Address(method, methodOopDesc::interpreter_entry_offset()));
+ __ lookup_virtual_method(rax, index, method);
__ jump_from_interpreted(method, rdx);
}
@@ -3060,7 +3085,10 @@
void TemplateTable::invokevirtual(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f2_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
+ prepare_invoke(byte_no,
+ rbx, // method or vtable index
+ noreg, // unused itable index
+ rcx, rdx); // recv, flags
// rbx: index
// rcx: receiver
@@ -3073,7 +3101,10 @@
void TemplateTable::invokespecial(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
+ prepare_invoke(byte_no, rbx, noreg, // get f1 methodOop
+ rcx); // get receiver also for null check
+ __ verify_oop(rcx);
+ __ null_check(rcx);
// do the call
__ verify_oop(rbx);
__ profile_call(rax);
@@ -3084,7 +3115,7 @@
void TemplateTable::invokestatic(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rbx, noreg, byte_no);
+ prepare_invoke(byte_no, rbx); // get f1 methodOop
// do the call
__ verify_oop(rbx);
__ profile_call(rax);
@@ -3100,10 +3131,11 @@
void TemplateTable::invokeinterface(int byte_no) {
transition(vtos, vtos);
assert(byte_no == f1_byte, "use this argument");
- prepare_invoke(rax, rbx, byte_no);
-
- // rax: Interface
- // rbx: index
+ prepare_invoke(byte_no, rax, rbx, // get f1 klassOop, f2 itable index
+ rcx, rdx); // recv, flags
+
+ // rax: interface klass (from f1)
+ // rbx: itable index (from f2)
// rcx: receiver
// rdx: flags
@@ -3113,14 +3145,15 @@
// another compliant java compiler.
Label notMethod;
__ movl(r14, rdx);
- __ andl(r14, (1 << ConstantPoolCacheEntry::methodInterface));
+ __ andl(r14, (1 << ConstantPoolCacheEntry::is_forced_virtual_shift));
__ jcc(Assembler::zero, notMethod);
invokevirtual_helper(rbx, rcx, rdx);
__ bind(notMethod);
// Get receiver klass into rdx - also a null check
- __ restore_locals(); // restore r14
+ __ restore_locals(); // restore r14
+ __ null_check(rcx, oopDesc::klass_offset_in_bytes());
__ load_klass(rdx, rcx);
__ verify_oop(rdx);
@@ -3135,7 +3168,7 @@
rbx, r13,
no_such_interface);
- // rbx,: methodOop to call
+ // rbx: methodOop to call
// rcx: receiver
// Check for abstract method error
// Note: This should be done more efficiently via a throw_abstract_method_error
@@ -3172,12 +3205,42 @@
InterpreterRuntime::throw_IncompatibleClassChangeError));
// the call_VM checks for exception, so we should never return here.
__ should_not_reach_here();
- return;
}
+
+void TemplateTable::invokehandle(int byte_no) {
+ transition(vtos, vtos);
+ assert(byte_no == f12_oop, "use this argument");
+ const Register rbx_method = rbx; // f2
+ const Register rax_mtype = rax; // f1
+ const Register rcx_recv = rcx;
+ const Register rdx_flags = rdx;
+
+ if (!EnableInvokeDynamic) {
+ // rewriter does not generate this bytecode
+ __ should_not_reach_here();
+ return;
+ }
+
+ prepare_invoke(byte_no,
+ rbx_method, rax_mtype, // get f2 methodOop, f1 MethodType
+ rcx_recv);
+ __ verify_oop(rbx_method);
+ __ verify_oop(rcx_recv);
+ __ null_check(rcx_recv);
+
+ // Note: rax_mtype is already pushed (if necessary) by prepare_invoke
+
+ // FIXME: profile the LambdaForm also
+ __ profile_final_call(rax);
+
+ __ jump_from_interpreted(rbx_method, rdx);
+}
+
+
void TemplateTable::invokedynamic(int byte_no) {
transition(vtos, vtos);
- assert(byte_no == f1_oop, "use this argument");
+ assert(byte_no == f12_oop, "use this argument");
if (!EnableInvokeDynamic) {
// We should not encounter this bytecode if !EnableInvokeDynamic.
@@ -3190,26 +3253,23 @@
return;
}
- prepare_invoke(rax, rbx, byte_no);
-
- // rax: CallSite object (f1)
- // rbx: unused (f2)
- // rcx: receiver address
- // rdx: flags (unused)
-
- Register rax_callsite = rax;
- Register rcx_method_handle = rcx;
+ const Register rbx_method = rbx;
+ const Register rax_callsite = rax;
+
+ prepare_invoke(byte_no, rbx_method, rax_callsite);
+
+ // rax: CallSite object (from f1)
+ // rbx: MH.linkToCallSite method (from f2)
+
+ // Note: rax_callsite is already pushed by prepare_invoke
// %%% should make a type profile for any invokedynamic that takes a ref argument
// profile this call
__ profile_call(r13);
__ verify_oop(rax_callsite);
- __ load_heap_oop(rcx_method_handle, Address(rax_callsite, __ delayed_value(java_lang_invoke_CallSite::target_offset_in_bytes, rdx)));
- __ null_check(rcx_method_handle);
- __ verify_oop(rcx_method_handle);
- __ prepare_to_jump_from_interpreted();
- __ jump_to_method_handle_entry(rcx_method_handle, rdx);
+
+ __ jump_from_interpreted(rbx_method, rdx);
}
--- a/hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -25,7 +25,12 @@
#ifndef CPU_X86_VM_TEMPLATETABLE_X86_64_HPP
#define CPU_X86_VM_TEMPLATETABLE_X86_64_HPP
- static void prepare_invoke(Register method, Register index, int byte_no);
+ static void prepare_invoke(int byte_no,
+ Register method, // linked method (or i-klass)
+ Register index = noreg, // itable index, MethodType, etc.
+ Register recv = noreg, // if caller wants to see it
+ Register flags = noreg // if caller wants to test it
+ );
static void invokevirtual_helper(Register index, Register recv,
Register flags);
static void volatile_barrier(Assembler::Membar_mask_bits order_constraint);
--- a/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -76,8 +76,7 @@
// get receiver klass
address npe_addr = __ pc();
__ movptr(rax, Address(rcx, oopDesc::klass_offset_in_bytes()));
- // compute entry offset (in words)
- int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size();
+
#ifndef PRODUCT
if (DebugVtables) {
Label L;
@@ -93,7 +92,8 @@
const Register method = rbx;
// load methodOop and target address
- __ movptr(method, Address(rax, entry_offset*wordSize + vtableEntry::method_offset_in_bytes()));
+ __ lookup_virtual_method(rax, vtable_index, method);
+
if (DebugVtables) {
Label L;
__ cmpptr(method, (int32_t)NULL_WORD);
--- a/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -69,10 +69,6 @@
address npe_addr = __ pc();
__ load_klass(rax, j_rarg0);
- // compute entry offset (in words)
- int entry_offset =
- instanceKlass::vtable_start_offset() + vtable_index * vtableEntry::size();
-
#ifndef PRODUCT
if (DebugVtables) {
Label L;
@@ -90,9 +86,8 @@
// load methodOop and target address
const Register method = rbx;
- __ movptr(method, Address(rax,
- entry_offset * wordSize +
- vtableEntry::method_offset_in_bytes()));
+ __ lookup_virtual_method(rax, vtable_index, method);
+
if (DebugVtables) {
Label L;
__ cmpptr(method, (int32_t)NULL_WORD);
--- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -646,16 +646,15 @@
oop method_type = (oop) p;
// The MethodHandle is in the slot after the arguments
- oop form = java_lang_invoke_MethodType::form(method_type);
- int num_vmslots = java_lang_invoke_MethodTypeForm::vmslots(form);
- assert(argument_slots == num_vmslots + 1, "should be");
+ int num_vmslots = argument_slots - 1;
oop method_handle = VMSLOTS_OBJECT(num_vmslots);
// InvokeGeneric requires some extra shuffling
oop mhtype = java_lang_invoke_MethodHandle::type(method_handle);
bool is_exact = mhtype == method_type;
if (!is_exact) {
- if (method->intrinsic_id() == vmIntrinsics::_invokeExact) {
+ if (true || // FIXME
+ method->intrinsic_id() == vmIntrinsics::_invokeExact) {
CALL_VM_NOCHECK_NOFIX(
SharedRuntime::throw_WrongMethodTypeException(
thread, method_type, mhtype));
@@ -670,8 +669,8 @@
// NB the x86 code for this (in methodHandles_x86.cpp, search for
// "genericInvoker") is really really odd. I'm hoping it's trying
// to accomodate odd VM/class library combinations I can ignore.
- oop adapter = java_lang_invoke_MethodTypeForm::genericInvoker(form);
- if (adapter == NULL) {
+ oop adapter = NULL; //FIXME: load the adapter from the CP cache
+ IF (adapter == NULL) {
CALL_VM_NOCHECK_NOFIX(
SharedRuntime::throw_WrongMethodTypeException(
thread, method_type, mhtype));
@@ -761,7 +760,7 @@
return;
}
if (entry_kind != MethodHandles::_invokespecial_mh) {
- int index = java_lang_invoke_DirectMethodHandle::vmindex(method_handle);
+ intptr_t index = java_lang_invoke_DirectMethodHandle::vmindex(method_handle);
instanceKlass* rcvrKlass =
(instanceKlass *) receiver->klass()->klass_part();
if (entry_kind == MethodHandles::_invokevirtual_mh) {
@@ -1179,8 +1178,7 @@
intptr_t* CppInterpreter::calculate_unwind_sp(ZeroStack* stack,
oop method_handle) {
oop method_type = java_lang_invoke_MethodHandle::type(method_handle);
- oop form = java_lang_invoke_MethodType::form(method_type);
- int argument_slots = java_lang_invoke_MethodTypeForm::vmslots(form);
+ int argument_slots = java_lang_invoke_MethodType::ptype_slot_count(method_type);
return stack->sp() + argument_slots;
}
--- a/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -38,6 +38,5 @@
address generate_empty_entry();
address generate_accessor_entry();
address generate_Reference_get_entry();
- address generate_method_handle_entry();
#endif // CPU_ZERO_VM_INTERPRETERGENERATOR_ZERO_HPP
--- a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -70,14 +70,6 @@
return generate_entry((address) ShouldNotCallThisEntry());
}
-address InterpreterGenerator::generate_method_handle_entry() {
-#ifdef CC_INTERP
- return generate_entry((address) CppInterpreter::method_handle_entry);
-#else
- return generate_entry((address) ShouldNotCallThisEntry());
-#endif // CC_INTERP
-}
-
bool AbstractInterpreter::can_be_compiled(methodHandle m) {
return true;
}
--- a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -201,13 +201,23 @@
impl[i] = (char)toupper((uint)impl[i]);
if (strstr(impl, "SPARC64") != NULL) {
features |= sparc64_family_m;
+ } else if (strstr(impl, "SPARC-M") != NULL) {
+ // M-series SPARC is based on T-series.
+ features |= (M_family_m | T_family_m);
} else if (strstr(impl, "SPARC-T") != NULL) {
features |= T_family_m;
if (strstr(impl, "SPARC-T1") != NULL) {
features |= T1_model_m;
}
} else {
- assert(strstr(impl, "SPARC") != NULL, "should be sparc");
+ if (strstr(impl, "SPARC") == NULL) {
+#ifndef PRODUCT
+ // kstat on Solaris 8 virtual machines (branded zones)
+ // returns "(unsupported)" implementation.
+ warning("kstat cpu_info implementation = '%s', should contain SPARC", impl);
+#endif
+ implementation = "SPARC";
+ }
}
free((void*)impl);
break;
--- a/hotspot/src/share/vm/adlc/output_h.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/adlc/output_h.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -674,16 +674,19 @@
else if( inst.is_ideal_mem() ) {
// Print out the field name if available to improve readability
fprintf(fp, " if (ra->C->alias_type(adr_type())->field() != NULL) {\n");
- fprintf(fp, " st->print(\" ! Field \");\n");
- fprintf(fp, " if( ra->C->alias_type(adr_type())->is_volatile() )\n");
- fprintf(fp, " st->print(\" Volatile\");\n");
- fprintf(fp, " ra->C->alias_type(adr_type())->field()->holder()->name()->print_symbol_on(st);\n");
+ fprintf(fp, " ciField* f = ra->C->alias_type(adr_type())->field();\n");
+ fprintf(fp, " st->print(\" ! Field: \");\n");
+ fprintf(fp, " if (f->is_volatile())\n");
+ fprintf(fp, " st->print(\"volatile \");\n");
+ fprintf(fp, " f->holder()->name()->print_symbol_on(st);\n");
fprintf(fp, " st->print(\".\");\n");
- fprintf(fp, " ra->C->alias_type(adr_type())->field()->name()->print_symbol_on(st);\n");
+ fprintf(fp, " f->name()->print_symbol_on(st);\n");
+ fprintf(fp, " if (f->is_constant())\n");
+ fprintf(fp, " st->print(\" (constant)\");\n");
fprintf(fp, " } else\n");
// Make sure 'Volatile' gets printed out
- fprintf(fp, " if( ra->C->alias_type(adr_type())->is_volatile() )\n");
- fprintf(fp, " st->print(\" Volatile!\");\n");
+ fprintf(fp, " if (ra->C->alias_type(adr_type())->is_volatile())\n");
+ fprintf(fp, " st->print(\" volatile!\");\n");
}
// Complete the definition of the format function
--- a/hotspot/src/share/vm/asm/assembler.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/asm/assembler.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -318,6 +318,16 @@
}
}
+RegisterOrConstant AbstractAssembler::delayed_value(int(*value_fn)(), Register tmp, int offset) {
+ intptr_t val = (intptr_t) (*value_fn)();
+ if (val != 0) return val + offset;
+ return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
+}
+RegisterOrConstant AbstractAssembler::delayed_value(address(*value_fn)(), Register tmp, int offset) {
+ intptr_t val = (intptr_t) (*value_fn)();
+ if (val != 0) return val + offset;
+ return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
+}
intptr_t* AbstractAssembler::delayed_value_addr(int(*value_fn)()) {
DelayedConstant* dcon = DelayedConstant::add(T_INT, (DelayedConstant::value_fn_t) value_fn);
return &dcon->value;
--- a/hotspot/src/share/vm/asm/assembler.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/asm/assembler.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -406,12 +406,8 @@
// offsets in code which must be generated before the object class is loaded.
// Field offsets are never zero, since an object's header (mark word)
// is located at offset zero.
- RegisterOrConstant delayed_value(int(*value_fn)(), Register tmp, int offset = 0) {
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
- }
- RegisterOrConstant delayed_value(address(*value_fn)(), Register tmp, int offset = 0) {
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
- }
+ RegisterOrConstant delayed_value(int(*value_fn)(), Register tmp, int offset = 0);
+ RegisterOrConstant delayed_value(address(*value_fn)(), Register tmp, int offset = 0);
virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, Register tmp, int offset) = 0;
// Last overloading is platform-dependent; look in assembler_<arch>.cpp.
static intptr_t* delayed_value_addr(int(*constant_fn)());
--- a/hotspot/src/share/vm/asm/register.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/asm/register.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -103,7 +103,8 @@
) {
assert(
a != b,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d",
+ a, b)
);
}
@@ -116,7 +117,8 @@
assert(
a != b && a != c
&& b != c,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d",
+ a, b, c)
);
}
@@ -131,7 +133,8 @@
a != b && a != c && a != d
&& b != c && b != d
&& c != d,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d",
+ a, b, c, d)
);
}
@@ -148,7 +151,8 @@
&& b != c && b != d && b != e
&& c != d && c != e
&& d != e,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d",
+ a, b, c, d, e)
);
}
@@ -167,7 +171,8 @@
&& c != d && c != e && c != f
&& d != e && d != f
&& e != f,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d",
+ a, b, c, d, e, f)
);
}
@@ -188,7 +193,8 @@
&& d != e && d != f && d != g
&& e != f && e != g
&& f != g,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d",
+ a, b, c, d, e, f, g)
);
}
@@ -211,7 +217,34 @@
&& e != f && e != g && e != h
&& f != g && f != h
&& g != h,
- "registers must be different"
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d",
+ a, b, c, d, e, f, g, h)
+ );
+}
+
+
+inline void assert_different_registers(
+ AbstractRegister a,
+ AbstractRegister b,
+ AbstractRegister c,
+ AbstractRegister d,
+ AbstractRegister e,
+ AbstractRegister f,
+ AbstractRegister g,
+ AbstractRegister h,
+ AbstractRegister i
+) {
+ assert(
+ a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i
+ && b != c && b != d && b != e && b != f && b != g && b != h && b != i
+ && c != d && c != e && c != f && c != g && c != h && c != i
+ && d != e && d != f && d != g && d != h && d != i
+ && e != f && e != g && e != h && e != i
+ && f != g && f != h && f != i
+ && g != h && g != i
+ && h != i,
+ err_msg("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d",
+ a, b, c, d, e, f, g, h, i)
);
}
--- a/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -567,6 +567,7 @@
}
}
+void Canonicalizer::do_TypeCast (TypeCast* x) {}
void Canonicalizer::do_Invoke (Invoke* x) {}
void Canonicalizer::do_NewInstance (NewInstance* x) {}
void Canonicalizer::do_NewTypeArray (NewTypeArray* x) {}
--- a/hotspot/src/share/vm/c1/c1_Canonicalizer.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -74,6 +74,7 @@
virtual void do_IfInstanceOf (IfInstanceOf* x);
virtual void do_Convert (Convert* x);
virtual void do_NullCheck (NullCheck* x);
+ virtual void do_TypeCast (TypeCast* x);
virtual void do_Invoke (Invoke* x);
virtual void do_NewInstance (NewInstance* x);
virtual void do_NewTypeArray (NewTypeArray* x);
--- a/hotspot/src/share/vm/c1/c1_Compilation.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Compilation.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -523,7 +523,7 @@
assert(msg != NULL, "bailout message must exist");
if (!bailed_out()) {
// keep first bailout message
- if (PrintBailouts) tty->print_cr("compilation bailout: %s", msg);
+ if (PrintCompilation || PrintBailouts) tty->print_cr("compilation bailout: %s", msg);
_bailout_msg = msg;
}
}
--- a/hotspot/src/share/vm/c1/c1_FrameMap.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_FrameMap.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -92,7 +92,6 @@
for (i = 0; i < sizeargs;) {
BasicType t = sig_bt[i];
assert(t != T_VOID, "should be skipping these");
-
LIR_Opr opr = map_to_opr(t, regs + i, outgoing);
args->append(opr);
if (opr->is_address()) {
--- a/hotspot/src/share/vm/c1/c1_FrameMap.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_FrameMap.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -181,8 +181,8 @@
// for outgoing calls, these also update the reserved area to
// include space for arguments and any ABI area.
- CallingConvention* c_calling_convention (const BasicTypeArray* signature);
- CallingConvention* java_calling_convention (const BasicTypeArray* signature, bool outgoing);
+ CallingConvention* c_calling_convention(const BasicTypeArray* signature);
+ CallingConvention* java_calling_convention(const BasicTypeArray* signature, bool outgoing);
// deopt support
ByteSize sp_offset_for_orig_pc() { return sp_offset_for_monitor_base(_num_monitors); }
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -31,7 +31,7 @@
#include "ci/ciCallSite.hpp"
#include "ci/ciField.hpp"
#include "ci/ciKlass.hpp"
-#include "ci/ciMethodHandle.hpp"
+#include "ci/ciMemberName.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/bytecode.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -914,11 +914,11 @@
void GraphBuilder::store_local(ValueType* type, int index) {
Value x = pop(type);
- store_local(state(), x, type, index);
+ store_local(state(), x, index);
}
-void GraphBuilder::store_local(ValueStack* state, Value x, ValueType* type, int index) {
+void GraphBuilder::store_local(ValueStack* state, Value x, int index) {
if (parsing_jsr()) {
// We need to do additional tracking of the location of the return
// address for jsrs since we don't handle arbitrary jsr/ret
@@ -1535,7 +1535,7 @@
case T_ARRAY:
case T_OBJECT:
if (field_val.as_object()->should_be_constant()) {
- constant = new Constant(as_ValueType(field_val));
+ constant = new Constant(as_ValueType(field_val));
}
break;
@@ -1562,12 +1562,53 @@
append(new StoreField(append(obj), offset, field, val, true, state_before, needs_patching));
}
break;
- case Bytecodes::_getfield :
- {
+ case Bytecodes::_getfield: {
+ // Check for compile-time constants, i.e., trusted final non-static fields.
+ Instruction* constant = NULL;
+ obj = apop();
+ ObjectType* obj_type = obj->type()->as_ObjectType();
+ if (obj_type->is_constant() && !PatchALot) {
+ ciObject* const_oop = obj_type->constant_value();
+ if (!const_oop->is_null_object()) {
+ if (field->is_constant()) {
+ ciConstant field_val = field->constant_value_of(const_oop);
+ BasicType field_type = field_val.basic_type();
+ switch (field_type) {
+ case T_ARRAY:
+ case T_OBJECT:
+ if (field_val.as_object()->should_be_constant()) {
+ constant = new Constant(as_ValueType(field_val));
+ }
+ break;
+ default:
+ constant = new Constant(as_ValueType(field_val));
+ }
+ } else {
+ // For CallSite objects treat the target field as a compile time constant.
+ if (const_oop->is_call_site()) {
+ ciCallSite* call_site = const_oop->as_call_site();
+ if (field->is_call_site_target()) {
+ ciMethodHandle* target = call_site->get_target();
+ if (target != NULL) { // just in case
+ ciConstant field_val(T_OBJECT, target);
+ constant = new Constant(as_ValueType(field_val));
+ // Add a dependence for invalidation of the optimization.
+ if (!call_site->is_constant_call_site()) {
+ dependency_recorder()->assert_call_site_target_value(call_site, target);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (constant != NULL) {
+ push(type, append(constant));
+ } else {
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
- LoadField* load = new LoadField(apop(), offset, field, false, state_before, needs_patching);
+ LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
Value replacement = !needs_patching ? _memory->load(load) : load;
if (replacement != load) {
assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked");
@@ -1575,22 +1616,23 @@
} else {
push(type, append(load));
}
- break;
- }
-
- case Bytecodes::_putfield :
- { Value val = pop(type);
- if (state_before == NULL) {
- state_before = copy_state_for_exception();
- }
- StoreField* store = new StoreField(apop(), offset, field, val, false, state_before, needs_patching);
- if (!needs_patching) store = _memory->store(store);
- if (store != NULL) {
- append(store);
- }
}
break;
- default :
+ }
+ case Bytecodes::_putfield: {
+ Value val = pop(type);
+ obj = apop();
+ if (state_before == NULL) {
+ state_before = copy_state_for_exception();
+ }
+ StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
+ if (!needs_patching) store = _memory->store(store);
+ if (store != NULL) {
+ append(store);
+ }
+ break;
+ }
+ default:
ShouldNotReachHere();
break;
}
@@ -1604,38 +1646,73 @@
void GraphBuilder::invoke(Bytecodes::Code code) {
+ const bool has_receiver =
+ code == Bytecodes::_invokespecial ||
+ code == Bytecodes::_invokevirtual ||
+ code == Bytecodes::_invokeinterface;
+ const bool is_invokedynamic = (code == Bytecodes::_invokedynamic);
+
bool will_link;
- ciMethod* target = stream()->get_method(will_link);
+ ciMethod* target = stream()->get_method(will_link);
+ ciKlass* holder = stream()->get_declared_method_holder();
+ const Bytecodes::Code bc_raw = stream()->cur_bc_raw();
+
+ // FIXME bail out for now
+ if ((bc_raw == Bytecodes::_invokehandle || is_invokedynamic) && !will_link) {
+ BAILOUT("unlinked call site (FIXME needs patching or recompile support)");
+ }
+
// we have to make sure the argument size (incl. the receiver)
// is correct for compilation (the call would fail later during
// linkage anyway) - was bug (gri 7/28/99)
- if (target->is_loaded() && target->is_static() != (code == Bytecodes::_invokestatic)) BAILOUT("will cause link error");
+ {
+ // Use raw to get rewritten bytecode.
+ const bool is_invokestatic = bc_raw == Bytecodes::_invokestatic;
+ const bool allow_static =
+ is_invokestatic ||
+ bc_raw == Bytecodes::_invokehandle ||
+ bc_raw == Bytecodes::_invokedynamic;
+ if (target->is_loaded()) {
+ if (( target->is_static() && !allow_static) ||
+ (!target->is_static() && is_invokestatic)) {
+ BAILOUT("will cause link error");
+ }
+ }
+ }
ciInstanceKlass* klass = target->holder();
// check if CHA possible: if so, change the code to invoke_special
ciInstanceKlass* calling_klass = method()->holder();
- ciKlass* holder = stream()->get_declared_method_holder();
ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder);
ciInstanceKlass* actual_recv = callee_holder;
- // some methods are obviously bindable without any type checks so
- // convert them directly to an invokespecial.
- if (target->is_loaded() && !target->is_abstract() &&
- target->can_be_statically_bound() && code == Bytecodes::_invokevirtual) {
- code = Bytecodes::_invokespecial;
+ // Some methods are obviously bindable without any type checks so
+ // convert them directly to an invokespecial or invokestatic.
+ if (target->is_loaded() && !target->is_abstract() && target->can_be_statically_bound()) {
+ switch (bc_raw) {
+ case Bytecodes::_invokevirtual: code = Bytecodes::_invokespecial; break;
+ case Bytecodes::_invokehandle: code = Bytecodes::_invokestatic; break;
+ }
}
- bool is_invokedynamic = code == Bytecodes::_invokedynamic;
+ // Push appendix argument (MethodType, CallSite, etc.), if one.
+ if (stream()->has_appendix()) {
+ ciObject* appendix = stream()->get_appendix();
+ Value arg = append(new Constant(new ObjectConstant(appendix)));
+ apush(arg);
+ }
// NEEDS_CLEANUP
- // I've added the target-is_loaded() test below but I don't really understand
+ // I've added the target->is_loaded() test below but I don't really understand
// how klass->is_loaded() can be true and yet target->is_loaded() is false.
// this happened while running the JCK invokevirtual tests under doit. TKR
ciMethod* cha_monomorphic_target = NULL;
ciMethod* exact_target = NULL;
Value better_receiver = NULL;
if (UseCHA && DeoptC1 && klass->is_loaded() && target->is_loaded() &&
- !target->is_method_handle_invoke()) {
+ !(// %%% FIXME: Are both of these relevant?
+ target->is_method_handle_intrinsic() ||
+ target->is_compiled_lambda_form())) {
Value receiver = NULL;
ciInstanceKlass* receiver_klass = NULL;
bool type_is_exact = false;
@@ -1761,23 +1838,15 @@
code == Bytecodes::_invokedynamic) {
ciMethod* inline_target = (cha_monomorphic_target != NULL) ? cha_monomorphic_target : target;
bool success = false;
- if (target->is_method_handle_invoke()) {
+ if (target->is_method_handle_intrinsic()) {
// method handle invokes
- success = !is_invokedynamic ? for_method_handle_inline(target) : for_invokedynamic_inline(target);
- }
- if (!success) {
+ success = for_method_handle_inline(target);
+ } else {
// static binding => check if callee is ok
- success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), better_receiver);
+ success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), code, better_receiver);
}
CHECK_BAILOUT();
-#ifndef PRODUCT
- // printing
- if (PrintInlining && !success) {
- // if it was successfully inlined, then it was already printed.
- print_inline_result(inline_target, success);
- }
-#endif
clear_inline_bailout();
if (success) {
// Register dependence if JVMTI has either breakpoint
@@ -1788,8 +1857,13 @@
}
return;
}
+ } else {
+ print_inlining(target, "no static binding", /*success*/ false);
}
+ } else {
+ print_inlining(target, "not inlineable", /*success*/ false);
}
+
// If we attempted an inline which did not succeed because of a
// bailout during construction of the callee graph, the entire
// compilation has to be aborted. This is fairly rare and currently
@@ -1803,10 +1877,6 @@
// inlining not successful => standard invoke
bool is_loaded = target->is_loaded();
- bool has_receiver =
- code == Bytecodes::_invokespecial ||
- code == Bytecodes::_invokevirtual ||
- code == Bytecodes::_invokeinterface;
ValueType* result_type = as_ValueType(target->return_type());
// We require the debug info to be the "state before" because
@@ -1855,7 +1925,7 @@
} else if (exact_target != NULL) {
target_klass = exact_target->holder();
}
- profile_call(recv, target_klass);
+ profile_call(target, recv, target_klass);
}
}
@@ -3097,30 +3167,61 @@
}
-bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, Value receiver) {
- // Clear out any existing inline bailout condition
+bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, Bytecodes::Code bc, Value receiver) {
+ const char* msg = NULL;
+
+ // clear out any existing inline bailout condition
clear_inline_bailout();
- if (callee->should_exclude()) {
- // callee is excluded
- INLINE_BAILOUT("excluded by CompilerOracle")
- } else if (callee->should_not_inline()) {
- // callee is excluded
- INLINE_BAILOUT("disallowed by CompilerOracle")
- } else if (!callee->can_be_compiled()) {
- // callee is not compilable (prob. has breakpoints)
- INLINE_BAILOUT("not compilable (disabled)")
- } else if (callee->intrinsic_id() != vmIntrinsics::_none && try_inline_intrinsics(callee)) {
- // intrinsics can be native or not
+ // exclude methods we don't want to inline
+ msg = should_not_inline(callee);
+ if (msg != NULL) {
+ print_inlining(callee, msg, /*success*/ false);
+ return false;
+ }
+
+ // handle intrinsics
+ if (callee->intrinsic_id() != vmIntrinsics::_none) {
+ if (try_inline_intrinsics(callee)) {
+ print_inlining(callee, "intrinsic");
+ return true;
+ }
+ // try normal inlining
+ }
+
+ // certain methods cannot be parsed at all
+ msg = check_can_parse(callee);
+ if (msg != NULL) {
+ print_inlining(callee, msg, /*success*/ false);
+ return false;
+ }
+
+ // If bytecode not set use the current one.
+ if (bc == Bytecodes::_illegal) {
+ bc = code();
+ }
+ if (try_inline_full(callee, holder_known, bc, receiver))
return true;
- } else if (callee->is_native()) {
- // non-intrinsic natives cannot be inlined
- INLINE_BAILOUT("non-intrinsic native")
- } else if (callee->is_abstract()) {
- INLINE_BAILOUT("abstract")
- } else {
- return try_inline_full(callee, holder_known, NULL, receiver);
- }
+ print_inlining(callee, _inline_bailout_msg, /*success*/ false);
+ return false;
+}
+
+
+const char* GraphBuilder::check_can_parse(ciMethod* callee) const {
+ // Certain methods cannot be parsed at all:
+ if ( callee->is_native()) return "native method";
+ if ( callee->is_abstract()) return "abstract method";
+ if (!callee->can_be_compiled()) return "not compilable (disabled)";
+ return NULL;
+}
+
+
+// negative filter: should callee NOT be inlined? returns NULL, ok to inline, or rejection msg
+const char* GraphBuilder::should_not_inline(ciMethod* callee) const {
+ if ( callee->should_exclude()) return "excluded by CompilerOracle";
+ if ( callee->should_not_inline()) return "disallowed by CompilerOracle";
+ if ( callee->dont_inline()) return "don't inline by annotation";
+ return NULL;
}
@@ -3304,7 +3405,7 @@
recv = args->at(0);
null_check(recv);
}
- profile_call(recv, NULL);
+ profile_call(callee, recv, NULL);
}
}
}
@@ -3315,13 +3416,6 @@
Value value = append_split(result);
if (result_type != voidType) push(result_type, value);
-#ifndef PRODUCT
- // printing
- if (PrintInlining) {
- print_inline_result(callee, true);
- }
-#endif
-
// done
return true;
}
@@ -3477,7 +3571,7 @@
}
-bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBegin* cont_block, Value receiver) {
+bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecodes::Code bc, Value receiver) {
assert(!callee->is_native(), "callee must not be native");
if (CompilationPolicy::policy()->should_not_inline(compilation()->env(), callee)) {
INLINE_BAILOUT("inlining prohibited by policy");
@@ -3508,10 +3602,10 @@
if (callee->force_inline() || callee->should_inline()) {
// ignore heuristic controls on inlining
if (callee->force_inline())
- CompileTask::print_inlining(callee, scope()->level(), bci(), "force inline by annotation");
+ print_inlining(callee, "force inline by annotation");
} else {
- if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("too-deep inlining");
- if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("too-deep recursive inlining");
+ if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("inlining too deep");
+ if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("recursive inlining too deep");
if (callee->code_size_for_inlining() > max_inline_size() ) INLINE_BAILOUT("callee is too large");
// don't inline throwable methods unless the inlining tree is rooted in a throwable class
@@ -3530,28 +3624,25 @@
if (compilation()->env()->num_inlined_bytecodes() > DesiredMethodLimit) {
INLINE_BAILOUT("total inlining greater than DesiredMethodLimit");
}
+ // printing
+ print_inlining(callee, "");
}
-#ifndef PRODUCT
- // printing
- if (PrintInlining) {
- print_inline_result(callee, true);
- }
-#endif
-
// NOTE: Bailouts from this point on, which occur at the
// GraphBuilder level, do not cause bailout just of the inlining but
// in fact of the entire compilation.
BlockBegin* orig_block = block();
+ const bool is_invokedynamic = bc == Bytecodes::_invokedynamic;
+ const bool has_receiver = (bc != Bytecodes::_invokestatic && !is_invokedynamic);
+
const int args_base = state()->stack_size() - callee->arg_size();
assert(args_base >= 0, "stack underflow during inlining");
// Insert null check if necessary
Value recv = NULL;
- if (code() != Bytecodes::_invokestatic &&
- code() != Bytecodes::_invokedynamic) {
+ if (has_receiver) {
// note: null check must happen even if first instruction of callee does
// an implicit null check since the callee is in a different scope
// and we must make sure exception handling does the right thing
@@ -3567,7 +3658,7 @@
compilation()->set_would_profile(true);
if (profile_calls()) {
- profile_call(recv, holder_known ? callee->holder() : NULL);
+ profile_call(callee, recv, holder_known ? callee->holder() : NULL);
}
}
@@ -3576,7 +3667,7 @@
// fall-through of control flow, all return instructions of the
// callee will need to be replaced by Goto's pointing to this
// continuation point.
- BlockBegin* cont = cont_block != NULL ? cont_block : block_at(next_bci());
+ BlockBegin* cont = block_at(next_bci());
bool continuation_existed = true;
if (cont == NULL) {
cont = new BlockBegin(next_bci());
@@ -3609,17 +3700,10 @@
// note: this will also ensure that all arguments are computed before being passed
ValueStack* callee_state = state();
ValueStack* caller_state = state()->caller_state();
- { int i = args_base;
- while (i < caller_state->stack_size()) {
- const int par_no = i - args_base;
- Value arg = caller_state->stack_at_inc(i);
- // NOTE: take base() of arg->type() to avoid problems storing
- // constants
- if (receiver != NULL && par_no == 0) {
- arg = receiver;
- }
- store_local(callee_state, arg, arg->type()->base(), par_no);
- }
+ for (int i = args_base; i < caller_state->stack_size(); ) {
+ const int arg_no = i - args_base;
+ Value arg = caller_state->stack_at_inc(i);
+ store_local(callee_state, arg, arg_no);
}
// Remove args from stack.
@@ -3695,29 +3779,27 @@
// block merging. This allows load elimination and CSE to take place
// across multiple callee scopes if they are relatively simple, and
// is currently essential to making inlining profitable.
- if (cont_block == NULL) {
- if (num_returns() == 1
- && block() == orig_block
- && block() == inline_cleanup_block()) {
- _last = inline_cleanup_return_prev();
- _state = inline_cleanup_state();
- } else if (continuation_preds == cont->number_of_preds()) {
- // Inlining caused that the instructions after the invoke in the
- // caller are not reachable any more. So skip filling this block
- // with instructions!
- assert(cont == continuation(), "");
+ if (num_returns() == 1
+ && block() == orig_block
+ && block() == inline_cleanup_block()) {
+ _last = inline_cleanup_return_prev();
+ _state = inline_cleanup_state();
+ } else if (continuation_preds == cont->number_of_preds()) {
+ // Inlining caused that the instructions after the invoke in the
+ // caller are not reachable any more. So skip filling this block
+ // with instructions!
+ assert(cont == continuation(), "");
+ assert(_last && _last->as_BlockEnd(), "");
+ _skip_block = true;
+ } else {
+ // Resume parsing in continuation block unless it was already parsed.
+ // Note that if we don't change _last here, iteration in
+ // iterate_bytecodes_for_block will stop when we return.
+ if (!continuation()->is_set(BlockBegin::was_visited_flag)) {
+ // add continuation to work list instead of parsing it immediately
assert(_last && _last->as_BlockEnd(), "");
+ scope_data()->parent()->add_to_work_list(continuation());
_skip_block = true;
- } else {
- // Resume parsing in continuation block unless it was already parsed.
- // Note that if we don't change _last here, iteration in
- // iterate_bytecodes_for_block will stop when we return.
- if (!continuation()->is_set(BlockBegin::was_visited_flag)) {
- // add continuation to work list instead of parsing it immediately
- assert(_last && _last->as_BlockEnd(), "");
- scope_data()->parent()->add_to_work_list(continuation());
- _skip_block = true;
- }
}
}
@@ -3735,114 +3817,88 @@
bool GraphBuilder::for_method_handle_inline(ciMethod* callee) {
- assert(!callee->is_static(), "change next line");
- int index = state()->stack_size() - (callee->arg_size_no_receiver() + 1);
- Value receiver = state()->stack_at(index);
-
- if (receiver->type()->is_constant()) {
- ciMethodHandle* method_handle = receiver->type()->as_ObjectType()->constant_value()->as_method_handle();
-
- // Set the callee to have access to the class and signature in
- // the MethodHandleCompiler.
- method_handle->set_callee(callee);
- method_handle->set_caller(method());
-
- // Get an adapter for the MethodHandle.
- ciMethod* method_handle_adapter = method_handle->get_method_handle_adapter();
- if (method_handle_adapter != NULL) {
- return try_inline(method_handle_adapter, /*holder_known=*/ true);
- }
- } else if (receiver->as_CheckCast()) {
- // Match MethodHandle.selectAlternative idiom
- Phi* phi = receiver->as_CheckCast()->obj()->as_Phi();
-
- if (phi != NULL && phi->operand_count() == 2) {
- // Get the two MethodHandle inputs from the Phi.
- Value op1 = phi->operand_at(0);
- Value op2 = phi->operand_at(1);
- ObjectType* op1type = op1->type()->as_ObjectType();
- ObjectType* op2type = op2->type()->as_ObjectType();
-
- if (op1type->is_constant() && op2type->is_constant()) {
- ciMethodHandle* mh1 = op1type->constant_value()->as_method_handle();
- ciMethodHandle* mh2 = op2type->constant_value()->as_method_handle();
-
- // Set the callee to have access to the class and signature in
- // the MethodHandleCompiler.
- mh1->set_callee(callee);
- mh1->set_caller(method());
- mh2->set_callee(callee);
- mh2->set_caller(method());
-
- // Get adapters for the MethodHandles.
- ciMethod* mh1_adapter = mh1->get_method_handle_adapter();
- ciMethod* mh2_adapter = mh2->get_method_handle_adapter();
-
- if (mh1_adapter != NULL && mh2_adapter != NULL) {
- set_inline_cleanup_info();
-
- // Build the If guard
- BlockBegin* one = new BlockBegin(next_bci());
- BlockBegin* two = new BlockBegin(next_bci());
- BlockBegin* end = new BlockBegin(next_bci());
- Instruction* iff = append(new If(phi, If::eql, false, op1, one, two, NULL, false));
- block()->set_end(iff->as_BlockEnd());
-
- // Connect up the states
- one->merge(block()->end()->state());
- two->merge(block()->end()->state());
-
- // Save the state for the second inlinee
- ValueStack* state_before = copy_state_before();
-
- // Parse first adapter
- _last = _block = one;
- if (!try_inline_full(mh1_adapter, /*holder_known=*/ true, end, NULL)) {
- restore_inline_cleanup_info();
- block()->clear_end(); // remove appended iff
- return false;
- }
-
- // Parse second adapter
- _last = _block = two;
- _state = state_before;
- if (!try_inline_full(mh2_adapter, /*holder_known=*/ true, end, NULL)) {
- restore_inline_cleanup_info();
- block()->clear_end(); // remove appended iff
- return false;
- }
-
- connect_to_end(end);
+ ValueStack* state_before = state()->copy_for_parsing();
+ vmIntrinsics::ID iid = callee->intrinsic_id();
+ switch (iid) {
+ case vmIntrinsics::_invokeBasic:
+ {
+ // get MethodHandle receiver
+ const int args_base = state()->stack_size() - callee->arg_size();
+ ValueType* type = state()->stack_at(args_base)->type();
+ if (type->is_constant()) {
+ ciMethod* target = type->as_ObjectType()->constant_value()->as_method_handle()->get_vmtarget();
+ guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove
+ Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
+ if (try_inline(target, /*holder_known*/ true, bc)) {
return true;
}
+ } else {
+ print_inlining(callee, "receiver not constant", /*success*/ false);
}
}
+ break;
+
+ case vmIntrinsics::_linkToVirtual:
+ case vmIntrinsics::_linkToStatic:
+ case vmIntrinsics::_linkToSpecial:
+ case vmIntrinsics::_linkToInterface:
+ {
+ // pop MemberName argument
+ const int args_base = state()->stack_size() - callee->arg_size();
+ ValueType* type = apop()->type();
+ if (type->is_constant()) {
+ ciMethod* target = type->as_ObjectType()->constant_value()->as_member_name()->get_vmtarget();
+ // If the target is another method handle invoke try recursivly to get
+ // a better target.
+ if (target->is_method_handle_intrinsic()) {
+ if (for_method_handle_inline(target)) {
+ return true;
+ }
+ } else {
+ ciSignature* signature = target->signature();
+ const int receiver_skip = target->is_static() ? 0 : 1;
+ // Cast receiver to its type.
+ if (!target->is_static()) {
+ ciKlass* tk = signature->accessing_klass();
+ Value obj = state()->stack_at(args_base);
+ if (obj->exact_type() == NULL &&
+ obj->declared_type() != tk && tk != compilation()->env()->Object_klass()) {
+ TypeCast* c = new TypeCast(tk, obj, state_before);
+ append(c);
+ state()->stack_at_put(args_base, c);
+ }
+ }
+ // Cast reference arguments to its type.
+ for (int i = 0, j = 0; i < signature->count(); i++) {
+ ciType* t = signature->type_at(i);
+ if (t->is_klass()) {
+ ciKlass* tk = t->as_klass();
+ Value obj = state()->stack_at(args_base + receiver_skip + j);
+ if (obj->exact_type() == NULL &&
+ obj->declared_type() != tk && tk != compilation()->env()->Object_klass()) {
+ TypeCast* c = new TypeCast(t, obj, state_before);
+ append(c);
+ state()->stack_at_put(args_base + receiver_skip + j, c);
+ }
+ }
+ j += t->size(); // long and double take two slots
+ }
+ Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
+ if (try_inline(target, /*holder_known*/ true, bc)) {
+ return true;
+ }
+ }
+ } else {
+ print_inlining(callee, "MemberName not constant", /*success*/ false);
+ }
+ }
+ break;
+
+ default:
+ fatal(err_msg("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
+ break;
}
- return false;
-}
-
-
-bool GraphBuilder::for_invokedynamic_inline(ciMethod* callee) {
- // Get the MethodHandle from the CallSite.
- ciCallSite* call_site = stream()->get_call_site();
- ciMethodHandle* method_handle = call_site->get_target();
-
- // Set the callee to have access to the class and signature in the
- // MethodHandleCompiler.
- method_handle->set_callee(callee);
- method_handle->set_caller(method());
-
- // Get an adapter for the MethodHandle.
- ciMethod* method_handle_adapter = method_handle->get_invokedynamic_adapter();
- if (method_handle_adapter != NULL) {
- if (try_inline(method_handle_adapter, /*holder_known=*/ true)) {
- // Add a dependence for invalidation of the optimization.
- if (!call_site->is_constant_call_site()) {
- dependency_recorder()->assert_call_site_target_value(call_site, method_handle);
- }
- return true;
- }
- }
+ set_state(state_before);
return false;
}
@@ -4034,22 +4090,24 @@
}
-#ifndef PRODUCT
-void GraphBuilder::print_inline_result(ciMethod* callee, bool res) {
- CompileTask::print_inlining(callee, scope()->level(), bci(), _inline_bailout_msg);
- if (res && CIPrintMethodCodes) {
+void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool success) {
+ if (!PrintInlining) return;
+ assert(msg != NULL, "must be");
+ CompileTask::print_inlining(callee, scope()->level(), bci(), msg);
+ if (success && CIPrintMethodCodes) {
callee->print_codes();
}
}
+#ifndef PRODUCT
void GraphBuilder::print_stats() {
vmap()->print();
}
#endif // PRODUCT
-void GraphBuilder::profile_call(Value recv, ciKlass* known_holder) {
- append(new ProfileCall(method(), bci(), recv, known_holder));
+void GraphBuilder::profile_call(ciMethod* callee, Value recv, ciKlass* known_holder) {
+ append(new ProfileCall(method(), bci(), callee, recv, known_holder));
}
void GraphBuilder::profile_invocation(ciMethod* callee, ValueStack* state) {
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -225,7 +225,7 @@
void load_constant();
void load_local(ValueType* type, int index);
void store_local(ValueType* type, int index);
- void store_local(ValueStack* state, Value value, ValueType* type, int index);
+ void store_local(ValueStack* state, Value value, int index);
void load_indexed (BasicType type);
void store_indexed(BasicType type);
void stack_op(Bytecodes::Code code);
@@ -337,14 +337,16 @@
void fill_sync_handler(Value lock, BlockBegin* sync_handler, bool default_handler = false);
// inliners
- bool try_inline( ciMethod* callee, bool holder_known, Value receiver = NULL);
+ bool try_inline( ciMethod* callee, bool holder_known, Bytecodes::Code bc = Bytecodes::_illegal, Value receiver = NULL);
bool try_inline_intrinsics(ciMethod* callee);
- bool try_inline_full( ciMethod* callee, bool holder_known, BlockBegin* cont_block, Value receiver);
+ bool try_inline_full( ciMethod* callee, bool holder_known, Bytecodes::Code bc = Bytecodes::_illegal, Value receiver = NULL);
bool try_inline_jsr(int jsr_dest_bci);
+ const char* check_can_parse(ciMethod* callee) const;
+ const char* should_not_inline(ciMethod* callee) const;
+
// JSR 292 support
bool for_method_handle_inline(ciMethod* callee);
- bool for_invokedynamic_inline(ciMethod* callee);
// helpers
void inline_bailout(const char* msg);
@@ -366,9 +368,9 @@
bool append_unsafe_prefetch(ciMethod* callee, bool is_store, bool is_static);
void append_unsafe_CAS(ciMethod* callee);
- NOT_PRODUCT(void print_inline_result(ciMethod* callee, bool res);)
+ void print_inlining(ciMethod* callee, const char* msg, bool success = true);
- void profile_call(Value recv, ciKlass* predicted_holder);
+ void profile_call(ciMethod* callee, Value recv, ciKlass* predicted_holder);
void profile_invocation(ciMethod* inlinee, ValueStack* state);
// Shortcuts to profiling control.
--- a/hotspot/src/share/vm/c1/c1_Instruction.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Instruction.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -161,6 +161,12 @@
return NULL;
}
+ciType* Constant::exact_type() const {
+ if (type()->is_object()) {
+ return type()->as_ObjectType()->exact_type();
+ }
+ return NULL;
+}
ciType* LoadIndexed::exact_type() const {
ciType* array_type = array()->exact_type();
--- a/hotspot/src/share/vm/c1/c1_Instruction.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Instruction.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -66,6 +66,7 @@
class IfOp;
class Convert;
class NullCheck;
+class TypeCast;
class OsrEntry;
class ExceptionObject;
class StateSplit;
@@ -174,6 +175,7 @@
virtual void do_IfOp (IfOp* x) = 0;
virtual void do_Convert (Convert* x) = 0;
virtual void do_NullCheck (NullCheck* x) = 0;
+ virtual void do_TypeCast (TypeCast* x) = 0;
virtual void do_Invoke (Invoke* x) = 0;
virtual void do_NewInstance (NewInstance* x) = 0;
virtual void do_NewTypeArray (NewTypeArray* x) = 0;
@@ -302,7 +304,8 @@
void update_exception_state(ValueStack* state);
- protected:
+ //protected:
+ public:
void set_type(ValueType* type) {
assert(type != NULL, "type must exist");
_type = type;
@@ -485,6 +488,7 @@
virtual TypeCheck* as_TypeCheck() { return NULL; }
virtual CheckCast* as_CheckCast() { return NULL; }
virtual InstanceOf* as_InstanceOf() { return NULL; }
+ virtual TypeCast* as_TypeCast() { return NULL; }
virtual AccessMonitor* as_AccessMonitor() { return NULL; }
virtual MonitorEnter* as_MonitorEnter() { return NULL; }
virtual MonitorExit* as_MonitorExit() { return NULL; }
@@ -638,8 +642,8 @@
// accessors
int java_index() const { return _java_index; }
- ciType* declared_type() const { return _declared_type; }
- ciType* exact_type() const;
+ virtual ciType* declared_type() const { return _declared_type; }
+ virtual ciType* exact_type() const;
// generic
virtual void input_values_do(ValueVisitor* f) { /* no values */ }
@@ -650,13 +654,13 @@
public:
// creation
Constant(ValueType* type):
- Instruction(type, NULL, true)
+ Instruction(type, NULL, /*type_is_constant*/ true)
{
assert(type->is_constant(), "must be a constant");
}
Constant(ValueType* type, ValueStack* state_before):
- Instruction(type, state_before, true)
+ Instruction(type, state_before, /*type_is_constant*/ true)
{
assert(state_before != NULL, "only used for constants which need patching");
assert(type->is_constant(), "must be a constant");
@@ -670,6 +674,7 @@
virtual intx hash() const;
virtual bool is_equal(Value v) const;
+ virtual ciType* exact_type() const;
enum CompareResult { not_comparable = -1, cond_false, cond_true };
@@ -1103,6 +1108,29 @@
};
+// This node is supposed to cast the type of another node to a more precise
+// declared type.
+LEAF(TypeCast, Instruction)
+ private:
+ ciType* _declared_type;
+ Value _obj;
+
+ public:
+ // The type of this node is the same type as the object type (and it might be constant).
+ TypeCast(ciType* type, Value obj, ValueStack* state_before)
+ : Instruction(obj->type(), state_before, obj->type()->is_constant()),
+ _declared_type(type),
+ _obj(obj) {}
+
+ // accessors
+ ciType* declared_type() const { return _declared_type; }
+ Value obj() const { return _obj; }
+
+ // generic
+ virtual void input_values_do(ValueVisitor* f) { f->visit(&_obj); }
+};
+
+
BASE(StateSplit, Instruction)
private:
ValueStack* _state;
@@ -1166,6 +1194,7 @@
// JSR 292 support
bool is_invokedynamic() const { return code() == Bytecodes::_invokedynamic; }
+ bool is_method_handle_intrinsic() const { return target()->is_method_handle_intrinsic(); }
virtual bool needs_exception_state() const { return false; }
@@ -2277,14 +2306,16 @@
private:
ciMethod* _method;
int _bci_of_invoke;
+ ciMethod* _callee; // the method that is called at the given bci
Value _recv;
ciKlass* _known_holder;
public:
- ProfileCall(ciMethod* method, int bci, Value recv, ciKlass* known_holder)
+ ProfileCall(ciMethod* method, int bci, ciMethod* callee, Value recv, ciKlass* known_holder)
: Instruction(voidType)
, _method(method)
, _bci_of_invoke(bci)
+ , _callee(callee)
, _recv(recv)
, _known_holder(known_holder)
{
@@ -2294,6 +2325,7 @@
ciMethod* method() { return _method; }
int bci_of_invoke() { return _bci_of_invoke; }
+ ciMethod* callee() { return _callee; }
Value recv() { return _recv; }
ciKlass* known_holder() { return _known_holder; }
--- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -137,12 +137,16 @@
ciMethod* m = (ciMethod*)value;
output()->print("<method %s.%s>", m->holder()->name()->as_utf8(), m->name()->as_utf8());
} else {
- output()->print("<object " PTR_FORMAT ">", value->constant_encoding());
+ output()->print("<object " PTR_FORMAT " klass=", value->constant_encoding());
+ print_klass(value->klass());
+ output()->print(">");
}
} else if (type->as_InstanceConstant() != NULL) {
ciInstance* value = type->as_InstanceConstant()->value();
if (value->is_loaded()) {
- output()->print("<instance " PTR_FORMAT ">", value->constant_encoding());
+ output()->print("<instance " PTR_FORMAT " klass=", value->constant_encoding());
+ print_klass(value->klass());
+ output()->print(">");
} else {
output()->print("<unloaded instance " PTR_FORMAT ">", value);
}
@@ -453,6 +457,14 @@
}
+void InstructionPrinter::do_TypeCast(TypeCast* x) {
+ output()->print("type_cast(");
+ print_value(x->obj());
+ output()->print(") ");
+ print_klass(x->declared_type()->klass());
+}
+
+
void InstructionPrinter::do_Invoke(Invoke* x) {
if (x->receiver() != NULL) {
print_value(x->receiver());
--- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -101,6 +101,7 @@
virtual void do_IfOp (IfOp* x);
virtual void do_Convert (Convert* x);
virtual void do_NullCheck (NullCheck* x);
+ virtual void do_TypeCast (TypeCast* x);
virtual void do_Invoke (Invoke* x);
virtual void do_NewInstance (NewInstance* x);
virtual void do_NewTypeArray (NewTypeArray* x);
--- a/hotspot/src/share/vm/c1/c1_LIR.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_LIR.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, 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
@@ -26,6 +26,7 @@
#define SHARE_VM_C1_C1_LIR_HPP
#include "c1/c1_ValueType.hpp"
+#include "oops/methodOop.hpp"
class BlockBegin;
class BlockList;
@@ -1162,8 +1163,9 @@
return
is_invokedynamic() // An invokedynamic is always a MethodHandle call site.
||
- (method()->holder()->name() == ciSymbol::java_lang_invoke_MethodHandle() &&
- methodOopDesc::is_method_handle_invoke_name(method()->name()->sid()));
+ method()->is_compiled_lambda_form() // Java-generated adapter
+ ||
+ method()->is_method_handle_intrinsic(); // JVM-generated MH intrinsic
}
intptr_t vtable_offset() const {
@@ -1823,18 +1825,20 @@
private:
ciMethod* _profiled_method;
- int _profiled_bci;
- LIR_Opr _mdo;
- LIR_Opr _recv;
- LIR_Opr _tmp1;
- ciKlass* _known_holder;
+ int _profiled_bci;
+ ciMethod* _profiled_callee;
+ LIR_Opr _mdo;
+ LIR_Opr _recv;
+ LIR_Opr _tmp1;
+ ciKlass* _known_holder;
public:
// Destroys recv
- LIR_OpProfileCall(LIR_Code code, ciMethod* profiled_method, int profiled_bci, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* known_holder)
+ LIR_OpProfileCall(LIR_Code code, ciMethod* profiled_method, int profiled_bci, ciMethod* profiled_callee, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* known_holder)
: LIR_Op(code, LIR_OprFact::illegalOpr, NULL) // no result, no info
, _profiled_method(profiled_method)
, _profiled_bci(profiled_bci)
+ , _profiled_callee(profiled_callee)
, _mdo(mdo)
, _recv(recv)
, _tmp1(t1)
@@ -1842,6 +1846,7 @@
ciMethod* profiled_method() const { return _profiled_method; }
int profiled_bci() const { return _profiled_bci; }
+ ciMethod* profiled_callee() const { return _profiled_callee; }
LIR_Opr mdo() const { return _mdo; }
LIR_Opr recv() const { return _recv; }
LIR_Opr tmp1() const { return _tmp1; }
@@ -2145,8 +2150,8 @@
CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub,
ciMethod* profiled_method, int profiled_bci);
// methodDataOop profiling
- void profile_call(ciMethod* method, int bci, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* cha_klass) {
- append(new LIR_OpProfileCall(lir_profile_call, method, bci, mdo, recv, t1, cha_klass));
+ void profile_call(ciMethod* method, int bci, ciMethod* callee, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* cha_klass) {
+ append(new LIR_OpProfileCall(lir_profile_call, method, bci, callee, mdo, recv, t1, cha_klass));
}
};
--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1940,6 +1940,14 @@
}
+void LIRGenerator::do_TypeCast(TypeCast* x) {
+ LIRItem value(x->obj(), this);
+ value.load_item();
+ // the result is the same as from the node we are casting
+ set_result(x, value.result());
+}
+
+
void LIRGenerator::do_Throw(Throw* x) {
LIRItem exception(x->exception(), this);
exception.load_item();
@@ -2767,7 +2775,10 @@
// JSR 292
// Preserve the SP over MethodHandle call sites.
ciMethod* target = x->target();
- if (target->is_method_handle_invoke()) {
+ bool is_method_handle_invoke = (// %%% FIXME: Are both of these relevant?
+ target->is_method_handle_intrinsic() ||
+ target->is_compiled_lambda_form());
+ if (is_method_handle_invoke) {
info->set_is_method_handle_invoke(true);
__ move(FrameMap::stack_pointer(), FrameMap::method_handle_invoke_SP_save_opr());
}
@@ -2843,7 +2854,7 @@
// JSR 292
// Restore the SP after MethodHandle call sites.
- if (target->is_method_handle_invoke()) {
+ if (is_method_handle_invoke) {
__ move(FrameMap::method_handle_invoke_SP_save_opr(), FrameMap::stack_pointer());
}
@@ -3027,7 +3038,7 @@
recv = new_register(T_OBJECT);
__ move(value.result(), recv);
}
- __ profile_call(x->method(), x->bci_of_invoke(), mdo, recv, tmp, x->known_holder());
+ __ profile_call(x->method(), x->bci_of_invoke(), x->callee(), mdo, recv, tmp, x->known_holder());
}
void LIRGenerator::do_ProfileInvoke(ProfileInvoke* x) {
--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -500,6 +500,7 @@
virtual void do_IfOp (IfOp* x);
virtual void do_Convert (Convert* x);
virtual void do_NullCheck (NullCheck* x);
+ virtual void do_TypeCast (TypeCast* x);
virtual void do_Invoke (Invoke* x);
virtual void do_NewInstance (NewInstance* x);
virtual void do_NewTypeArray (NewTypeArray* x);
--- a/hotspot/src/share/vm/c1/c1_Optimizer.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_Optimizer.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -478,6 +478,7 @@
void do_IfOp (IfOp* x);
void do_Convert (Convert* x);
void do_NullCheck (NullCheck* x);
+ void do_TypeCast (TypeCast* x);
void do_Invoke (Invoke* x);
void do_NewInstance (NewInstance* x);
void do_NewTypeArray (NewTypeArray* x);
@@ -648,6 +649,7 @@
void NullCheckVisitor::do_IfOp (IfOp* x) {}
void NullCheckVisitor::do_Convert (Convert* x) {}
void NullCheckVisitor::do_NullCheck (NullCheck* x) { nce()->handle_NullCheck(x); }
+void NullCheckVisitor::do_TypeCast (TypeCast* x) {}
void NullCheckVisitor::do_Invoke (Invoke* x) { nce()->handle_Invoke(x); }
void NullCheckVisitor::do_NewInstance (NewInstance* x) { nce()->handle_NewInstance(x); }
void NullCheckVisitor::do_NewTypeArray (NewTypeArray* x) { nce()->handle_NewArray(x); }
--- a/hotspot/src/share/vm/c1/c1_ValueMap.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_ValueMap.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -178,6 +178,7 @@
void do_IfOp (IfOp* x) { /* nothing to do */ }
void do_Convert (Convert* x) { /* nothing to do */ }
void do_NullCheck (NullCheck* x) { /* nothing to do */ }
+ void do_TypeCast (TypeCast* x) { /* nothing to do */ }
void do_NewInstance (NewInstance* x) { /* nothing to do */ }
void do_NewTypeArray (NewTypeArray* x) { /* nothing to do */ }
void do_NewObjectArray (NewObjectArray* x) { /* nothing to do */ }
--- a/hotspot/src/share/vm/c1/c1_ValueStack.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_ValueStack.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -195,6 +195,7 @@
void ValueStack::print() {
scope()->method()->print_name();
+ tty->cr();
if (stack_is_empty()) {
tty->print_cr("empty stack");
} else {
--- a/hotspot/src/share/vm/c1/c1_ValueStack.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_ValueStack.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -142,6 +142,10 @@
return x;
}
+ void stack_at_put(int i, Value x) {
+ _stack.at_put(i, x);
+ }
+
// pinning support
void pin_stack_for_linear_scan();
--- a/hotspot/src/share/vm/c1/c1_ValueType.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_ValueType.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -101,6 +101,23 @@
ciObject* InstanceConstant::constant_value() const { return _value; }
ciObject* ClassConstant::constant_value() const { return _value; }
+ciType* ObjectConstant::exact_type() const {
+ ciObject* c = constant_value();
+ return (c != NULL && !c->is_null_object()) ? c->klass() : NULL;
+}
+ciType* ArrayConstant::exact_type() const {
+ ciObject* c = constant_value();
+ return (c != NULL && !c->is_null_object()) ? c->klass() : NULL;
+}
+ciType* InstanceConstant::exact_type() const {
+ ciObject* c = constant_value();
+ return (c != NULL && !c->is_null_object()) ? c->klass() : NULL;
+}
+ciType* ClassConstant::exact_type() const {
+ ciObject* c = constant_value();
+ return (c != NULL && !c->is_null_object()) ? c->klass() : NULL;
+}
+
ValueType* as_ValueType(BasicType type) {
switch (type) {
--- a/hotspot/src/share/vm/c1/c1_ValueType.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/c1/c1_ValueType.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -297,7 +297,8 @@
virtual const char tchar() const { return 'a'; }
virtual const char* name() const { return "object"; }
virtual ObjectType* as_ObjectType() { return this; }
- virtual ciObject* constant_value() const { ShouldNotReachHere(); return NULL; }
+ virtual ciObject* constant_value() const { ShouldNotReachHere(); return NULL; }
+ virtual ciType* exact_type() const { return NULL; }
bool is_loaded() const;
jobject encoding() const;
};
@@ -315,6 +316,7 @@
virtual bool is_constant() const { return true; }
virtual ObjectConstant* as_ObjectConstant() { return this; }
virtual ciObject* constant_value() const;
+ virtual ciType* exact_type() const;
};
@@ -334,9 +336,9 @@
ciArray* value() const { return _value; }
virtual bool is_constant() const { return true; }
-
virtual ArrayConstant* as_ArrayConstant() { return this; }
virtual ciObject* constant_value() const;
+ virtual ciType* exact_type() const;
};
@@ -356,9 +358,9 @@
ciInstance* value() const { return _value; }
virtual bool is_constant() const { return true; }
-
virtual InstanceConstant* as_InstanceConstant(){ return this; }
virtual ciObject* constant_value() const;
+ virtual ciType* exact_type() const;
};
@@ -378,9 +380,9 @@
ciInstanceKlass* value() const { return _value; }
virtual bool is_constant() const { return true; }
-
virtual ClassConstant* as_ClassConstant() { return this; }
virtual ciObject* constant_value() const;
+ virtual ciType* exact_type() const;
};
--- a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -238,9 +238,11 @@
// some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial.
- if (target->is_loaded() && !target->is_abstract() &&
- target->can_be_statically_bound() && code == Bytecodes::_invokevirtual) {
- code = Bytecodes::_invokespecial;
+ if (target->is_loaded() && !target->is_abstract() && target->can_be_statically_bound()) {
+ switch (code) {
+ case Bytecodes::_invokevirtual: code = Bytecodes::_invokespecial; break;
+ case Bytecodes::_invokehandle: code = Bytecodes::_invokestatic; break;
+ }
}
// compute size of arguments
@@ -866,7 +868,12 @@
{ bool will_link;
ciMethod* target = s.get_method(will_link);
ciKlass* holder = s.get_declared_method_holder();
- invoke(state, s.cur_bc(), target, holder);
+ // Push appendix argument, if one.
+ if (s.has_appendix()) {
+ state.apush(unknown_obj);
+ }
+ // Pass in raw bytecode because we need to see invokehandle instructions.
+ invoke(state, s.cur_bc_raw(), target, holder);
ciType* return_type = target->return_type();
if (!return_type->is_primitive_type()) {
state.apush(unknown_obj);
--- a/hotspot/src/share/vm/ci/ciClassList.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciClassList.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -47,6 +47,7 @@
class ciNullObject;
class ciInstance;
class ciCallSite;
+class ciMemberName;
class ciMethodHandle;
class ciMethod;
class ciMethodData;
@@ -100,6 +101,7 @@
friend class ciObject; \
friend class ciNullObject; \
friend class ciInstance; \
+friend class ciMemberName; \
friend class ciMethod; \
friend class ciMethodData; \
friend class ciMethodHandle; \
--- a/hotspot/src/share/vm/ci/ciEnv.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciEnv.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -50,7 +50,6 @@
#include "oops/oop.inline.hpp"
#include "oops/oop.inline2.hpp"
#include "prims/jvmtiExport.hpp"
-#include "prims/methodHandleWalk.hpp"
#include "runtime/init.hpp"
#include "runtime/reflection.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -582,7 +581,7 @@
assert(index < 0, "only one kind of index at a time");
ConstantPoolCacheEntry* cpc_entry = cpool->cache()->entry_at(cache_index);
index = cpc_entry->constant_pool_index();
- oop obj = cpc_entry->f1();
+ oop obj = cpc_entry->f1_as_instance();
if (obj != NULL) {
assert(obj->is_instance() || obj->is_array(), "must be a Java reference");
ciObject* ciobj = get_object(obj);
@@ -750,7 +749,7 @@
if (cpool->has_preresolution()
|| (holder == ciEnv::MethodHandle_klass() &&
- methodOopDesc::is_method_handle_invoke_name(name_sym))) {
+ MethodHandles::is_signature_polymorphic_name(holder->get_klassOop(), name_sym))) {
// Short-circuit lookups for JSR 292-related call sites.
// That is, do not rely only on name-based lookups, because they may fail
// if the names are not resolvable in the boot class loader (7056328).
@@ -760,11 +759,13 @@
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
{
- methodOop m = constantPoolOopDesc::method_at_if_loaded(cpool, index, bc);
+ oop appendix_oop = NULL;
+ methodOop m = constantPoolOopDesc::method_at_if_loaded(cpool, index);
if (m != NULL) {
return get_object(m)->as_method();
}
}
+ break;
}
}
@@ -800,27 +801,28 @@
// Compare the following logic with InterpreterRuntime::resolve_invokedynamic.
assert(bc == Bytecodes::_invokedynamic, "must be invokedynamic");
- bool is_resolved = cpool->cache()->main_entry_at(index)->is_resolved(bc);
- if (is_resolved && cpool->cache()->secondary_entry_at(index)->is_f1_null())
- // FIXME: code generation could allow for null (unlinked) call site
- is_resolved = false;
+ ConstantPoolCacheEntry* secondary_entry = cpool->cache()->secondary_entry_at(index);
+ bool is_resolved = !secondary_entry->is_f1_null();
+ // FIXME: code generation could allow for null (unlinked) call site
+ // The call site could be made patchable as follows:
+ // Load the appendix argument from the constant pool.
+ // Test the appendix argument and jump to a known deopt routine if it is null.
+ // Jump through a patchable call site, which is initially a deopt routine.
+ // Patch the call site to the nmethod entry point of the static compiled lambda form.
+ // As with other two-component call sites, both values must be independently verified.
- // Call site might not be resolved yet. We could create a real invoker method from the
- // compiler, but it is simpler to stop the code path here with an unlinked method.
+ // Call site might not be resolved yet.
+ // Stop the code path here with an unlinked method.
if (!is_resolved) {
ciInstanceKlass* holder = get_object(SystemDictionary::MethodHandle_klass())->as_instance_klass();
- ciSymbol* name = ciSymbol::invokeExact_name();
+ ciSymbol* name = ciSymbol::invokeBasic_name();
ciSymbol* signature = get_symbol(cpool->signature_ref_at(index));
return get_unloaded_method(holder, name, signature, accessor);
}
- // Get the invoker methodOop from the constant pool.
- oop f1_value = cpool->cache()->main_entry_at(index)->f1();
- methodOop signature_invoker = (methodOop) f1_value;
- assert(signature_invoker != NULL && signature_invoker->is_method() && signature_invoker->is_method_handle_invoke(),
- "correct result from LinkResolver::resolve_invokedynamic");
-
- return get_object(signature_invoker)->as_method();
+ // Get the invoker methodOop and the extra argument from the constant pool.
+ methodOop adapter = secondary_entry->f2_as_vfinal_method();
+ return get_object(adapter)->as_method();
}
@@ -1131,7 +1133,7 @@
// ------------------------------------------------------------------
// ciEnv::notice_inlined_method()
void ciEnv::notice_inlined_method(ciMethod* method) {
- _num_inlined_bytecodes += method->code_size();
+ _num_inlined_bytecodes += method->code_size_for_inlining();
}
// ------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/ci/ciMemberName.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 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 "ci/ciClassList.hpp"
+#include "ci/ciMemberName.hpp"
+#include "ci/ciUtilities.hpp"
+#include "classfile/javaClasses.hpp"
+
+// ------------------------------------------------------------------
+// ciMemberName::get_vmtarget
+//
+// Return: MN.vmtarget
+ciMethod* ciMemberName::get_vmtarget() const {
+ VM_ENTRY_MARK;
+ oop vmtarget_oop = java_lang_invoke_MemberName::vmtarget(get_oop());
+ return CURRENT_ENV->get_object(vmtarget_oop)->as_method();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/ci/ciMemberName.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012, 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_CI_CIMEMBERNAME_HPP
+#define SHARE_VM_CI_CIMEMBERNAME_HPP
+
+#include "ci/ciCallProfile.hpp"
+#include "ci/ciInstance.hpp"
+
+// ciMemberName
+//
+// The class represents a java.lang.invoke.MemberName object.
+class ciMemberName : public ciInstance {
+public:
+ ciMemberName(instanceHandle h_i) : ciInstance(h_i) {}
+
+ // What kind of ciObject is this?
+ bool is_member_name() const { return true; }
+
+ ciMethod* get_vmtarget() const;
+};
+
+#endif // SHARE_VM_CI_CIMEMBERNAME_HPP
--- a/hotspot/src/share/vm/ci/ciMethod.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciMethod.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -769,39 +769,37 @@
// invokedynamic support
// ------------------------------------------------------------------
-// ciMethod::is_method_handle_invoke
+// ciMethod::is_method_handle_intrinsic
//
-// Return true if the method is an instance of one of the two
-// signature-polymorphic MethodHandle methods, invokeExact or invokeGeneric.
-bool ciMethod::is_method_handle_invoke() const {
- if (!is_loaded()) {
- bool flag = (holder()->name() == ciSymbol::java_lang_invoke_MethodHandle() &&
- methodOopDesc::is_method_handle_invoke_name(name()->sid()));
- return flag;
- }
- VM_ENTRY_MARK;
- return get_methodOop()->is_method_handle_invoke();
+// Return true if the method is an instance of the JVM-generated
+// signature-polymorphic MethodHandle methods, _invokeBasic, _linkToVirtual, etc.
+bool ciMethod::is_method_handle_intrinsic() const {
+ vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded
+ return (MethodHandles::is_signature_polymorphic(iid) &&
+ MethodHandles::is_signature_polymorphic_intrinsic(iid));
}
// ------------------------------------------------------------------
-// ciMethod::is_method_handle_adapter
+// ciMethod::is_compiled_lambda_form
//
// Return true if the method is a generated MethodHandle adapter.
-// These are built by MethodHandleCompiler.
-bool ciMethod::is_method_handle_adapter() const {
- if (!is_loaded()) return false;
- VM_ENTRY_MARK;
- return get_methodOop()->is_method_handle_adapter();
+// These are built by Java code.
+bool ciMethod::is_compiled_lambda_form() const {
+ vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded
+ return iid == vmIntrinsics::_compiledLambdaForm;
}
-ciInstance* ciMethod::method_handle_type() {
- check_is_loaded();
- VM_ENTRY_MARK;
- oop mtype = get_methodOop()->method_handle_type();
- return CURRENT_THREAD_ENV->get_object(mtype)->as_instance();
+// ------------------------------------------------------------------
+// ciMethod::has_member_arg
+//
+// Return true if the method is a linker intrinsic like _linkToVirtual.
+// These are built by the JVM.
+bool ciMethod::has_member_arg() const {
+ vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded
+ return (MethodHandles::is_signature_polymorphic(iid) &&
+ MethodHandles::has_member_arg(iid));
}
-
// ------------------------------------------------------------------
// ciMethod::ensure_method_data
//
@@ -1024,28 +1022,13 @@
// ------------------------------------------------------------------
// ciMethod::code_size_for_inlining
//
-// Code size for inlining decisions.
-//
-// Don't fully count method handle adapters against inlining budgets:
-// the metric we use here is the number of call sites in the adapter
-// as they are probably the instructions which generate some code.
+// Code size for inlining decisions. This method returns a code
+// size of 1 for methods which has the ForceInline annotation.
int ciMethod::code_size_for_inlining() {
check_is_loaded();
-
- // Method handle adapters
- if (is_method_handle_adapter()) {
- // Count call sites
- int call_site_count = 0;
- ciBytecodeStream iter(this);
- while (iter.next() != ciBytecodeStream::EOBC()) {
- if (Bytecodes::is_invoke(iter.cur_bc())) {
- call_site_count++;
- }
- }
- return call_site_count;
+ if (get_methodOop()->force_inline()) {
+ return 1;
}
-
- // Normal method
return code_size();
}
@@ -1127,7 +1110,8 @@
constantPoolHandle pool (THREAD, get_methodOop()->constants());
methodHandle spec_method;
KlassHandle spec_klass;
- LinkResolver::resolve_method(spec_method, spec_klass, pool, refinfo_index, THREAD);
+ Bytecodes::Code code = (is_static ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual);
+ LinkResolver::resolve_method_statically(spec_method, spec_klass, code, pool, refinfo_index, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return false;
@@ -1207,8 +1191,16 @@
//
// Print the name of this method, without signature.
void ciMethod::print_short_name(outputStream* st) {
- check_is_loaded();
- GUARDED_VM_ENTRY(get_methodOop()->print_short_name(st);)
+ if (is_loaded()) {
+ GUARDED_VM_ENTRY(get_methodOop()->print_short_name(st););
+ } else {
+ // Fall back if method is not loaded.
+ holder()->print_name_on(st);
+ st->print("::");
+ name()->print_symbol_on(st);
+ if (WizardMode)
+ signature()->as_symbol()->print_symbol_on(st);
+ }
}
// ------------------------------------------------------------------
@@ -1223,6 +1215,7 @@
holder()->print_name_on(st);
st->print(" signature=");
signature()->as_symbol()->print_symbol_on(st);
+ st->print(" arg_size=%d", arg_size());
if (is_loaded()) {
st->print(" loaded=true flags=");
flags().print_member_flags(st);
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciMethod.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -133,16 +133,20 @@
return _signature->size() + (_flags.is_static() ? 0 : 1);
}
// Report the number of elements on stack when invoking this method.
- // This is different than the regular arg_size because invokdynamic
+ // This is different than the regular arg_size because invokedynamic
// has an implicit receiver.
int invoke_arg_size(Bytecodes::Code code) const {
- int arg_size = _signature->size();
- // Add a receiver argument, maybe:
- if (code != Bytecodes::_invokestatic &&
- code != Bytecodes::_invokedynamic) {
- arg_size++;
+ if (is_loaded()) {
+ return arg_size();
+ } else {
+ int arg_size = _signature->size();
+ // Add a receiver argument, maybe:
+ if (code != Bytecodes::_invokestatic &&
+ code != Bytecodes::_invokedynamic) {
+ arg_size++;
+ }
+ return arg_size;
}
- return arg_size;
}
@@ -161,6 +165,7 @@
int code_size_for_inlining();
bool force_inline() { return get_methodOop()->force_inline(); }
+ bool dont_inline() { return get_methodOop()->dont_inline(); }
int comp_level();
int highest_osr_comp_level();
@@ -258,9 +263,9 @@
int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC
// JSR 292 support
- bool is_method_handle_invoke() const;
- bool is_method_handle_adapter() const;
- ciInstance* method_handle_type();
+ bool is_method_handle_intrinsic() const;
+ bool is_compiled_lambda_form() const;
+ bool has_member_arg() const;
// What kind of ciObject is this?
bool is_method() { return true; }
--- a/hotspot/src/share/vm/ci/ciMethodHandle.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -24,84 +24,18 @@
#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"
-#include "prims/methodHandles.hpp"
-
-// ciMethodHandle
+#include "classfile/javaClasses.hpp"
// ------------------------------------------------------------------
-// ciMethodHandle::get_adapter
+// ciMethodHandle::get_vmtarget
//
-// Return an adapter for this MethodHandle.
-ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) {
+// Return: MH.form -> LF.vmentry -> MN.vmtarget
+ciMethod* ciMethodHandle::get_vmtarget() const {
VM_ENTRY_MARK;
- Handle h(get_oop());
- methodHandle callee(_callee->get_methodOop());
- assert(callee->is_method_handle_invoke(), "");
- oop mt1 = callee->method_handle_type();
- oop mt2 = java_lang_invoke_MethodHandle::type(h());
- if (!java_lang_invoke_MethodType::equals(mt1, mt2)) {
- if (PrintMiscellaneous && (Verbose || WizardMode)) {
- tty->print_cr("ciMethodHandle::get_adapter: types not equal");
- mt1->print(); mt2->print();
- }
- return NULL;
- }
- // We catch all exceptions here that could happen in the method
- // handle compiler and stop the VM.
- MethodHandleCompiler mhc(h, callee->name(), callee->signature(), _profile.count(), is_invokedynamic, THREAD);
- if (!HAS_PENDING_EXCEPTION) {
- methodHandle m = mhc.compile(THREAD);
- if (!HAS_PENDING_EXCEPTION) {
- return CURRENT_ENV->get_object(m())->as_method();
- }
- }
- if (PrintMiscellaneous && (Verbose || WizardMode)) {
- tty->print("*** ciMethodHandle::get_adapter => ");
- PENDING_EXCEPTION->print();
- tty->print("*** get_adapter (%s): ", is_invokedynamic ? "indy" : "mh"); ((ciObject*)this)->print();
- }
- CLEAR_PENDING_EXCEPTION;
- return NULL;
+ oop form_oop = java_lang_invoke_MethodHandle::form(get_oop());
+ oop vmentry_oop = java_lang_invoke_LambdaForm::vmentry(form_oop);
+ oop vmtarget_oop = java_lang_invoke_MemberName::vmtarget(vmentry_oop);
+ return CURRENT_ENV->get_object(vmtarget_oop)->as_method();
}
-
-// ------------------------------------------------------------------
-// ciMethodHandle::get_adapter
-//
-// Return an adapter for this MethodHandle.
-ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) {
- 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;
-}
-
-
-#ifdef ASSERT
-// ------------------------------------------------------------------
-// ciMethodHandle::print_chain_impl
-//
-// Implementation of the print method.
-void ciMethodHandle::print_chain_impl() {
- ASSERT_IN_VM;
- MethodHandleChain::print(get_oop());
-}
-
-
-// ------------------------------------------------------------------
-// ciMethodHandle::print_chain
-//
-// Implementation of the print_chain method.
-void ciMethodHandle::print_chain() {
- GUARDED_VM_ENTRY(print_chain_impl(););
-}
-#endif
--- a/hotspot/src/share/vm/ci/ciMethodHandle.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -25,61 +25,20 @@
#ifndef SHARE_VM_CI_CIMETHODHANDLE_HPP
#define SHARE_VM_CI_CIMETHODHANDLE_HPP
-#include "ci/ciCallProfile.hpp"
+#include "ci/ciClassList.hpp"
#include "ci/ciInstance.hpp"
-#include "prims/methodHandles.hpp"
// ciMethodHandle
//
// The class represents a java.lang.invoke.MethodHandle object.
class ciMethodHandle : public ciInstance {
-private:
- ciMethod* _callee;
- ciMethod* _caller;
- ciCallProfile _profile;
- ciMethod* _method_handle_adapter;
- ciMethod* _invokedynamic_adapter;
-
- // Return an adapter for this MethodHandle.
- ciMethod* get_adapter_impl(bool is_invokedynamic);
- ciMethod* get_adapter( bool is_invokedynamic);
-
-protected:
- void print_chain_impl() NOT_DEBUG_RETURN;
-
public:
- ciMethodHandle(instanceHandle h_i) :
- ciInstance(h_i),
- _callee(NULL),
- _caller(NULL),
- _method_handle_adapter(NULL),
- _invokedynamic_adapter(NULL)
- {}
+ ciMethodHandle(instanceHandle h_i) : ciInstance(h_i) {}
// What kind of ciObject is this?
bool is_method_handle() const { return true; }
- 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() {
- if (_method_handle_adapter == NULL) {
- _method_handle_adapter = get_adapter(false);
- }
- return _method_handle_adapter;
- }
-
- // Return an adapter for an invokedynamic call.
- ciMethod* get_invokedynamic_adapter() {
- if (_invokedynamic_adapter == NULL) {
- _invokedynamic_adapter = get_adapter(true);
- }
- return _invokedynamic_adapter;
- }
-
- void print_chain() NOT_DEBUG_RETURN;
+ ciMethod* get_vmtarget() const;
};
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
--- a/hotspot/src/share/vm/ci/ciObject.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciObject.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -138,13 +138,14 @@
jobject constant_encoding();
// What kind of ciObject is this?
- virtual bool is_null_object() const { return false; }
- virtual bool is_call_site() const { return false; }
- virtual bool is_cpcache() const { return false; }
+ virtual bool is_null_object() const { return false; }
+ virtual bool is_call_site() const { return false; }
+ virtual bool is_cpcache() const { return false; }
virtual bool is_instance() { return false; }
+ virtual bool is_member_name() const { return false; }
virtual bool is_method() { return false; }
virtual bool is_method_data() { return false; }
- virtual bool is_method_handle() const { return false; }
+ virtual bool is_method_handle() const { return false; }
virtual bool is_array() { return false; }
virtual bool is_obj_array() { return false; }
virtual bool is_type_array() { return false; }
@@ -208,6 +209,10 @@
assert(is_instance(), "bad cast");
return (ciInstance*)this;
}
+ ciMemberName* as_member_name() {
+ assert(is_member_name(), "bad cast");
+ return (ciMemberName*)this;
+ }
ciMethod* as_method() {
assert(is_method(), "bad cast");
return (ciMethod*)this;
@@ -290,7 +295,8 @@
}
// Print debugging output about this ciObject.
- void print(outputStream* st = tty);
+ void print(outputStream* st);
+ void print() { print(tty); } // GDB cannot handle default arguments
// Print debugging output about the oop this ciObject represents.
void print_oop(outputStream* st = tty);
--- a/hotspot/src/share/vm/ci/ciObjectFactory.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciObjectFactory.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -28,6 +28,7 @@
#include "ci/ciInstance.hpp"
#include "ci/ciInstanceKlass.hpp"
#include "ci/ciInstanceKlassKlass.hpp"
+#include "ci/ciMemberName.hpp"
#include "ci/ciMethod.hpp"
#include "ci/ciMethodData.hpp"
#include "ci/ciMethodHandle.hpp"
@@ -344,6 +345,8 @@
instanceHandle h_i(THREAD, (instanceOop)o);
if (java_lang_invoke_CallSite::is_instance(o))
return new (arena()) ciCallSite(h_i);
+ else if (java_lang_invoke_MemberName::is_instance(o))
+ return new (arena()) ciMemberName(h_i);
else if (java_lang_invoke_MethodHandle::is_instance(o))
return new (arena()) ciMethodHandle(h_i);
else
--- a/hotspot/src/share/vm/ci/ciSignature.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciSignature.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -39,10 +39,11 @@
ciKlass* _accessing_klass;
GrowableArray<ciType*>* _types;
- int _size;
- int _count;
+ int _size; // number of stack slots required for arguments
+ int _count; // number of parameter types in the signature
friend class ciMethod;
+ friend class ciBytecodeStream;
friend class ciObjectFactory;
ciSignature(ciKlass* accessing_klass, constantPoolHandle cpool, ciSymbol* signature);
--- a/hotspot/src/share/vm/ci/ciStreams.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciStreams.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -364,6 +364,29 @@
}
// ------------------------------------------------------------------
+// ciBytecodeStream::has_appendix
+//
+// Returns true if there is an appendix argument stored in the
+// constant pool cache at the current bci.
+bool ciBytecodeStream::has_appendix() {
+ VM_ENTRY_MARK;
+ constantPoolHandle cpool(_method->get_methodOop()->constants());
+ return constantPoolOopDesc::has_appendix_at_if_loaded(cpool, get_method_index());
+}
+
+// ------------------------------------------------------------------
+// ciBytecodeStream::get_appendix
+//
+// Return the appendix argument stored in the constant pool cache at
+// the current bci.
+ciObject* ciBytecodeStream::get_appendix() {
+ VM_ENTRY_MARK;
+ constantPoolHandle cpool(_method->get_methodOop()->constants());
+ oop appendix_oop = constantPoolOopDesc::appendix_at_if_loaded(cpool, get_method_index());
+ return CURRENT_ENV->get_object(appendix_oop);
+}
+
+// ------------------------------------------------------------------
// ciBytecodeStream::get_declared_method_holder
//
// Get the declared holder of the currently referenced method.
@@ -378,9 +401,9 @@
VM_ENTRY_MARK;
constantPoolHandle cpool(_method->get_methodOop()->constants());
bool ignore;
- // report as InvokeDynamic for invokedynamic, which is syntactically classless
+ // report as MethodHandle for invokedynamic, which is syntactically classless
if (cur_bc() == Bytecodes::_invokedynamic)
- return CURRENT_ENV->get_klass_by_name(_holder, ciSymbol::java_lang_invoke_InvokeDynamic(), false);
+ return CURRENT_ENV->get_klass_by_name(_holder, ciSymbol::java_lang_invoke_MethodHandle(), false);
return CURRENT_ENV->get_klass_by_index(cpool, get_method_holder_index(), ignore, _holder);
}
@@ -396,6 +419,24 @@
}
// ------------------------------------------------------------------
+// ciBytecodeStream::get_declared_method_signature
+//
+// Get the declared signature of the currently referenced method.
+//
+// This is always the same as the signature of the resolved method
+// itself, except for _invokehandle and _invokedynamic calls.
+//
+ciSignature* ciBytecodeStream::get_declared_method_signature() {
+ int sig_index = get_method_signature_index();
+ VM_ENTRY_MARK;
+ ciEnv* env = CURRENT_ENV;
+ constantPoolHandle cpool(_method->get_methodOop()->constants());
+ Symbol* sig_sym = cpool->symbol_at(sig_index);
+ ciKlass* pool_holder = env->get_object(cpool->pool_holder())->as_klass();
+ return new (env->arena()) ciSignature(pool_holder, cpool, env->get_symbol(sig_sym));
+}
+
+// ------------------------------------------------------------------
// ciBytecodeStream::get_method_signature_index
//
// Get the constant pool index of the signature of the method
@@ -434,7 +475,7 @@
// Get the CallSite from the constant pool cache.
int method_index = get_method_index();
ConstantPoolCacheEntry* cpcache_entry = cpcache->secondary_entry_at(method_index);
- oop call_site_oop = cpcache_entry->f1();
+ oop call_site_oop = cpcache_entry->f1_as_instance();
// Create a CallSite object and return it.
return CURRENT_ENV->get_object(call_site_oop)->as_call_site();
--- a/hotspot/src/share/vm/ci/ciStreams.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciStreams.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -259,8 +259,11 @@
// If this is a method invocation bytecode, get the invoked method.
ciMethod* get_method(bool& will_link);
+ bool has_appendix();
+ ciObject* get_appendix();
ciKlass* get_declared_method_holder();
int get_method_holder_index();
+ ciSignature* get_declared_method_signature();
int get_method_signature_index();
ciCPCache* get_cpcache() const;
--- a/hotspot/src/share/vm/ci/ciSymbol.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciSymbol.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -83,6 +83,10 @@
GUARDED_VM_ENTRY(return get_symbol()->starts_with(prefix, len);)
}
+bool ciSymbol::is_signature_polymorphic_name() const {
+ GUARDED_VM_ENTRY(return MethodHandles::is_signature_polymorphic_name(get_symbol());)
+}
+
// ------------------------------------------------------------------
// ciSymbol::index_of
//
--- a/hotspot/src/share/vm/ci/ciSymbol.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciSymbol.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -107,6 +107,8 @@
// Are two ciSymbols equal?
bool equals(ciSymbol* obj) { return this->_symbol == obj->get_symbol(); }
+
+ bool is_signature_polymorphic_name() const;
};
#endif // SHARE_VM_CI_CISYMBOL_HPP
--- a/hotspot/src/share/vm/ci/ciTypeFlow.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/ci/ciTypeFlow.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -643,9 +643,9 @@
// ------------------------------------------------------------------
// ciTypeFlow::StateVector::do_invoke
void ciTypeFlow::StateVector::do_invoke(ciBytecodeStream* str,
- bool has_receiver) {
+ bool has_receiver_foo) {
bool will_link;
- ciMethod* method = str->get_method(will_link);
+ ciMethod* callee = str->get_method(will_link);
if (!will_link) {
// We weren't able to find the method.
if (str->cur_bc() == Bytecodes::_invokedynamic) {
@@ -654,12 +654,24 @@
(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret));
} else {
- ciKlass* unloaded_holder = method->holder();
+ ciKlass* unloaded_holder = callee->holder();
trap(str, unloaded_holder, str->get_method_holder_index());
}
} else {
- ciSignature* signature = method->signature();
+ // TODO Use Bytecode_invoke after metadata changes.
+ //Bytecode_invoke inv(str->method(), str->cur_bci());
+ //const bool has_receiver = callee->is_loaded() ? !callee->is_static() : inv.has_receiver();
+ Bytecode inv(str);
+ Bytecodes::Code code = inv.invoke_code();
+ const bool has_receiver = callee->is_loaded() ? !callee->is_static() : code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic;
+
+ ciSignature* signature = callee->signature();
ciSignatureStream sigstr(signature);
+ // Push appendix argument, if one.
+ if (str->has_appendix()) {
+ ciObject* appendix = str->get_appendix();
+ push_object(appendix->klass());
+ }
int arg_size = signature->size();
int stack_base = stack_size() - arg_size;
int i = 0;
@@ -677,6 +689,7 @@
for (int j = 0; j < arg_size; j++) {
pop();
}
+ assert(!callee->is_loaded() || has_receiver == !callee->is_static(), "mismatch");
if (has_receiver) {
// Check this?
pop_object();
--- a/hotspot/src/share/vm/classfile/classFileParser.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1773,6 +1773,15 @@
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature):
if (_location != _in_method) break; // only allow for methods
return _method_ForceInline;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_DontInline_signature):
+ if (_location != _in_method) break; // only allow for methods
+ return _method_DontInline;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature):
+ if (_location != _in_method) break; // only allow for methods
+ return _method_LambdaForm_Compiled;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Hidden_signature):
+ if (_location != _in_method) break; // only allow for methods
+ return _method_LambdaForm_Hidden;
default: break;
}
return AnnotationCollector::_unknown;
@@ -1785,6 +1794,12 @@
void ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) {
if (has_annotation(_method_ForceInline))
m->set_force_inline(true);
+ if (has_annotation(_method_DontInline))
+ m->set_dont_inline(true);
+ if (has_annotation(_method_LambdaForm_Compiled) && m->intrinsic_id() == vmIntrinsics::_none)
+ m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm);
+ if (has_annotation(_method_LambdaForm_Hidden))
+ m->set_hidden(true);
}
void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) {
@@ -2336,12 +2351,6 @@
_has_vanilla_constructor = true;
}
- if (EnableInvokeDynamic && (m->is_method_handle_invoke() ||
- m->is_method_handle_adapter())) {
- THROW_MSG_(vmSymbols::java_lang_VirtualMachineError(),
- "Method handle invokers must be defined internally to the VM", nullHandle);
- }
-
return m;
}
--- a/hotspot/src/share/vm/classfile/classFileParser.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -89,6 +89,9 @@
enum ID {
_unknown = 0,
_method_ForceInline,
+ _method_DontInline,
+ _method_LambdaForm_Compiled,
+ _method_LambdaForm_Hidden,
_annotation_LIMIT
};
const Location _location;
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -126,6 +126,13 @@
if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
ResourceMark rm;
tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
+#ifndef PRODUCT
+ klass_oop->print();
+ tty->print_cr("all fields:");
+ for (AllFieldStream fs(instanceKlass::cast(klass_oop)); !fs.done(); fs.next()) {
+ tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int());
+ }
+#endif //PRODUCT
fatal("Invalid layout of preloaded class");
}
dest_offset = fd.offset();
@@ -1455,6 +1462,7 @@
nmethod* nm = NULL;
bool skip_fillInStackTrace_check = false;
bool skip_throwableInit_check = false;
+ bool skip_hidden = !ShowHiddenFrames;
for (frame fr = thread->last_frame(); max_depth != total_count;) {
methodOop method = NULL;
@@ -1534,6 +1542,9 @@
skip_throwableInit_check = true;
}
}
+ if (method->is_hidden()) {
+ if (skip_hidden) continue;
+ }
bt.push(method, bci, CHECK);
total_count++;
}
@@ -1724,6 +1735,8 @@
java_lang_StackTraceElement::set_methodName(element(), methodname);
// Fill in source file name
Symbol* source = instanceKlass::cast(method->method_holder())->source_file_name();
+ if (ShowHiddenFrames && source == NULL)
+ source = vmSymbols::unknown_class_name();
oop filename = StringTable::intern(source, CHECK_0);
java_lang_StackTraceElement::set_fileName(element(), filename);
// File in source line number
@@ -1736,6 +1749,9 @@
} else {
// Returns -1 if no LineNumberTable, and otherwise actual line number
line_number = method->line_number_from_bci(bci);
+ if (line_number == -1 && ShowHiddenFrames) {
+ line_number = bci + 1000000;
+ }
}
java_lang_StackTraceElement::set_lineNumber(element(), line_number);
@@ -2377,8 +2393,7 @@
// Support for java_lang_invoke_MethodHandle
int java_lang_invoke_MethodHandle::_type_offset;
-int java_lang_invoke_MethodHandle::_vmtarget_offset;
-int java_lang_invoke_MethodHandle::_vmentry_offset;
+int java_lang_invoke_MethodHandle::_form_offset;
int java_lang_invoke_MemberName::_clazz_offset;
int java_lang_invoke_MemberName::_name_offset;
@@ -2387,21 +2402,16 @@
int java_lang_invoke_MemberName::_vmtarget_offset;
int java_lang_invoke_MemberName::_vmindex_offset;
-int java_lang_invoke_DirectMethodHandle::_vmindex_offset;
-
-int java_lang_invoke_BoundMethodHandle::_argument_offset;
-int java_lang_invoke_BoundMethodHandle::_vmargslot_offset;
-
-int java_lang_invoke_AdapterMethodHandle::_conversion_offset;
-
-int java_lang_invoke_CountingMethodHandle::_vmcount_offset;
+int java_lang_invoke_LambdaForm::_vmentry_offset;
void java_lang_invoke_MethodHandle::compute_offsets() {
klassOop klass_oop = SystemDictionary::MethodHandle_klass();
if (klass_oop != NULL && EnableInvokeDynamic) {
- bool allow_super = false;
- compute_offset(_type_offset, klass_oop, vmSymbols::type_name(), vmSymbols::java_lang_invoke_MethodType_signature(), allow_super);
- METHODHANDLE_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
+ compute_offset(_type_offset, klass_oop, vmSymbols::type_name(), vmSymbols::java_lang_invoke_MethodType_signature());
+ compute_optional_offset(_form_offset, klass_oop, vmSymbols::form_name(), vmSymbols::java_lang_invoke_LambdaForm_signature());
+ if (_form_offset == 0) {
+ EnableInvokeDynamic = false;
+ }
}
}
@@ -2412,50 +2422,17 @@
compute_offset(_name_offset, klass_oop, vmSymbols::name_name(), vmSymbols::string_signature());
compute_offset(_type_offset, klass_oop, vmSymbols::type_name(), vmSymbols::object_signature());
compute_offset(_flags_offset, klass_oop, vmSymbols::flags_name(), vmSymbols::int_signature());
- compute_offset(_vmindex_offset, klass_oop, vmSymbols::vmindex_name(), vmSymbols::int_signature());
MEMBERNAME_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
}
}
-void java_lang_invoke_DirectMethodHandle::compute_offsets() {
- klassOop k = SystemDictionary::DirectMethodHandle_klass();
- if (k != NULL && EnableInvokeDynamic) {
- DIRECTMETHODHANDLE_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
- }
-}
-
-void java_lang_invoke_BoundMethodHandle::compute_offsets() {
- klassOop k = SystemDictionary::BoundMethodHandle_klass();
- if (k != NULL && EnableInvokeDynamic) {
- compute_offset(_vmargslot_offset, k, vmSymbols::vmargslot_name(), vmSymbols::int_signature(), true);
- compute_offset(_argument_offset, k, vmSymbols::argument_name(), vmSymbols::object_signature(), true);
+void java_lang_invoke_LambdaForm::compute_offsets() {
+ klassOop klass_oop = SystemDictionary::LambdaForm_klass();
+ if (klass_oop != NULL && EnableInvokeDynamic) {
+ compute_offset(_vmentry_offset, klass_oop, vmSymbols::vmentry_name(), vmSymbols::java_lang_invoke_MemberName_signature());
}
}
-void java_lang_invoke_AdapterMethodHandle::compute_offsets() {
- klassOop k = SystemDictionary::AdapterMethodHandle_klass();
- if (k != NULL && EnableInvokeDynamic) {
- compute_offset(_conversion_offset, k, vmSymbols::conversion_name(), vmSymbols::int_signature(), true);
- }
-}
-
-void java_lang_invoke_CountingMethodHandle::compute_offsets() {
- klassOop k = SystemDictionary::CountingMethodHandle_klass();
- if (k != NULL && EnableInvokeDynamic) {
- compute_offset(_vmcount_offset, k, vmSymbols::vmcount_name(), vmSymbols::int_signature(), true);
- }
-}
-
-int java_lang_invoke_CountingMethodHandle::vmcount(oop mh) {
- assert(is_instance(mh), "CMH only");
- return mh->int_field(_vmcount_offset);
-}
-
-void java_lang_invoke_CountingMethodHandle::set_vmcount(oop mh, int count) {
- assert(is_instance(mh), "CMH only");
- mh->int_field_put(_vmcount_offset, count);
-}
-
oop java_lang_invoke_MethodHandle::type(oop mh) {
return mh->obj_field(_type_offset);
}
@@ -2464,31 +2441,14 @@
mh->obj_field_put(_type_offset, mtype);
}
-// fetch type.form.vmslots, which is the number of JVM stack slots
-// required to carry the arguments of this MH
-int java_lang_invoke_MethodHandle::vmslots(oop mh) {
- oop mtype = type(mh);
- if (mtype == NULL) return 0; // Java code would get NPE
- oop form = java_lang_invoke_MethodType::form(mtype);
- if (form == NULL) return 0; // Java code would get NPE
- return java_lang_invoke_MethodTypeForm::vmslots(form);
+oop java_lang_invoke_MethodHandle::form(oop mh) {
+ assert(_form_offset != 0, "");
+ return mh->obj_field(_form_offset);
}
-// fetch the low-level entry point for this mh
-MethodHandleEntry* java_lang_invoke_MethodHandle::vmentry(oop mh) {
- return (MethodHandleEntry*) mh->address_field(_vmentry_offset);
-}
-
-void java_lang_invoke_MethodHandle::set_vmentry(oop mh, MethodHandleEntry* me) {
- assert(_vmentry_offset != 0, "must be present");
-
- // This is always the final step that initializes a valid method handle:
- mh->release_address_field_put(_vmentry_offset, (address) me);
-
- // There should be enough memory barriers on exit from native methods
- // to ensure that the MH is fully initialized to all threads before
- // Java code can publish it in global data structures.
- // But just in case, we use release_address_field_put.
+void java_lang_invoke_MethodHandle::set_form(oop mh, oop lform) {
+ assert(_form_offset != 0, "");
+ mh->obj_field_put(_form_offset, lform);
}
/// MemberName accessors
@@ -2540,57 +2500,40 @@
void java_lang_invoke_MemberName::set_vmtarget(oop mname, oop ref) {
assert(is_instance(mname), "wrong type");
+#ifdef ASSERT
+ // check the type of the vmtarget
+ if (ref != NULL) {
+ switch (flags(mname) & (MN_IS_METHOD |
+ MN_IS_CONSTRUCTOR |
+ MN_IS_FIELD)) {
+ case MN_IS_METHOD:
+ case MN_IS_CONSTRUCTOR:
+ assert(ref->is_method(), "should be a method");
+ break;
+ case MN_IS_FIELD:
+ assert(ref->is_klass(), "should be a class");
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+#endif //ASSERT
mname->obj_field_put(_vmtarget_offset, ref);
}
-int java_lang_invoke_MemberName::vmindex(oop mname) {
- assert(is_instance(mname), "wrong type");
- return mname->int_field(_vmindex_offset);
-}
-
-void java_lang_invoke_MemberName::set_vmindex(oop mname, int index) {
+intptr_t java_lang_invoke_MemberName::vmindex(oop mname) {
assert(is_instance(mname), "wrong type");
- mname->int_field_put(_vmindex_offset, index);
-}
-
-oop java_lang_invoke_MethodHandle::vmtarget(oop mh) {
- assert(is_instance(mh), "MH only");
- return mh->obj_field(_vmtarget_offset);
-}
-
-void java_lang_invoke_MethodHandle::set_vmtarget(oop mh, oop ref) {
- assert(is_instance(mh), "MH only");
- mh->obj_field_put(_vmtarget_offset, ref);
-}
-
-int java_lang_invoke_DirectMethodHandle::vmindex(oop mh) {
- assert(is_instance(mh), "DMH only");
- return mh->int_field(_vmindex_offset);
+ return (intptr_t) mname->address_field(_vmindex_offset);
}
-void java_lang_invoke_DirectMethodHandle::set_vmindex(oop mh, int index) {
- assert(is_instance(mh), "DMH only");
- mh->int_field_put(_vmindex_offset, index);
-}
-
-int java_lang_invoke_BoundMethodHandle::vmargslot(oop mh) {
- assert(is_instance(mh), "BMH only");
- return mh->int_field(_vmargslot_offset);
+void java_lang_invoke_MemberName::set_vmindex(oop mname, intptr_t index) {
+ assert(is_instance(mname), "wrong type");
+ mname->address_field_put(_vmindex_offset, (address) index);
}
-oop java_lang_invoke_BoundMethodHandle::argument(oop mh) {
- assert(is_instance(mh), "BMH only");
- return mh->obj_field(_argument_offset);
-}
-
-int java_lang_invoke_AdapterMethodHandle::conversion(oop mh) {
- assert(is_instance(mh), "AMH only");
- return mh->int_field(_conversion_offset);
-}
-
-void java_lang_invoke_AdapterMethodHandle::set_conversion(oop mh, int conv) {
- assert(is_instance(mh), "AMH only");
- mh->int_field_put(_conversion_offset, conv);
+oop java_lang_invoke_LambdaForm::vmentry(oop lform) {
+ assert(is_instance(lform), "wrong type");
+ return lform->obj_field(_vmentry_offset);
}
@@ -2598,14 +2541,12 @@
int java_lang_invoke_MethodType::_rtype_offset;
int java_lang_invoke_MethodType::_ptypes_offset;
-int java_lang_invoke_MethodType::_form_offset;
void java_lang_invoke_MethodType::compute_offsets() {
klassOop k = SystemDictionary::MethodType_klass();
if (k != NULL) {
compute_offset(_rtype_offset, k, vmSymbols::rtype_name(), vmSymbols::class_signature());
compute_offset(_ptypes_offset, k, vmSymbols::ptypes_name(), vmSymbols::class_array_signature());
- compute_offset(_form_offset, k, vmSymbols::form_name(), vmSymbols::java_lang_invoke_MethodTypeForm_signature());
}
}
@@ -2635,6 +2576,8 @@
}
bool java_lang_invoke_MethodType::equals(oop mt1, oop mt2) {
+ if (mt1 == mt2)
+ return true;
if (rtype(mt1) != rtype(mt2))
return false;
if (ptype_count(mt1) != ptype_count(mt2))
@@ -2656,11 +2599,6 @@
return (objArrayOop) mt->obj_field(_ptypes_offset);
}
-oop java_lang_invoke_MethodType::form(oop mt) {
- assert(is_instance(mt), "must be a MethodType");
- return mt->obj_field(_form_offset);
-}
-
oop java_lang_invoke_MethodType::ptype(oop mt, int idx) {
return ptypes(mt)->obj_at(idx);
}
@@ -2669,62 +2607,20 @@
return ptypes(mt)->length();
}
-
-
-// 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;
-
-void java_lang_invoke_MethodTypeForm::compute_offsets() {
- klassOop k = SystemDictionary::MethodTypeForm_klass();
- if (k != NULL) {
- compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
- compute_optional_offset(_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
- METHODTYPEFORM_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
+int java_lang_invoke_MethodType::ptype_slot_count(oop mt) {
+ objArrayOop pts = ptypes(mt);
+ int count = pts->length();
+ int slots = 0;
+ for (int i = 0; i < count; i++) {
+ BasicType bt = java_lang_Class::as_BasicType(pts->obj_at(i));
+ slots += type2size[bt];
}
-}
-
-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);
+ return slots;
}
-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);
-}
-
-oop java_lang_invoke_MethodTypeForm::genericInvoker(oop mtform) {
- assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
- return mtform->obj_field(_genericInvoker_offset);
+int java_lang_invoke_MethodType::rtype_slot_count(oop mt) {
+ BasicType bt = java_lang_Class::as_BasicType(rtype(mt));
+ return type2size[bt];
}
@@ -2798,10 +2694,26 @@
}
oop java_lang_ClassLoader::parent(oop loader) {
- assert(loader->is_oop(), "loader must be oop");
+ assert(is_instance(loader), "loader must be oop");
return loader->obj_field(parent_offset);
}
+bool java_lang_ClassLoader::isAncestor(oop loader, oop cl) {
+ assert(is_instance(loader), "loader must be oop");
+ assert(cl == NULL || is_instance(cl), "cl argument must be oop");
+ oop acl = loader;
+ debug_only(jint loop_count = 0);
+ // This loop taken verbatim from ClassLoader.java:
+ do {
+ acl = parent(acl);
+ if (cl == acl) {
+ return true;
+ }
+ assert(++loop_count > 0, "loop_count overflow");
+ } while (acl != NULL);
+ return false;
+}
+
// For class loader classes, parallelCapable defined
// based on non-null field
@@ -3061,13 +2973,9 @@
if (EnableInvokeDynamic) {
java_lang_invoke_MethodHandle::compute_offsets();
java_lang_invoke_MemberName::compute_offsets();
- java_lang_invoke_DirectMethodHandle::compute_offsets();
- java_lang_invoke_BoundMethodHandle::compute_offsets();
- java_lang_invoke_AdapterMethodHandle::compute_offsets();
+ java_lang_invoke_LambdaForm::compute_offsets();
java_lang_invoke_MethodType::compute_offsets();
- java_lang_invoke_MethodTypeForm::compute_offsets();
java_lang_invoke_CallSite::compute_offsets();
- java_lang_invoke_CountingMethodHandle::compute_offsets();
}
java_security_AccessControlContext::compute_offsets();
// Initialize reflection classes. The layouts of these classes
@@ -3295,7 +3203,14 @@
}
}
ResourceMark rm;
- tty->print_cr("Invalid layout of %s at %s", instanceKlass::cast(klass_oop)->external_name(), name()->as_C_string());
+ tty->print_cr("Invalid layout of %s at %s/%s%s", instanceKlass::cast(klass_oop)->external_name(), name()->as_C_string(), signature()->as_C_string(), may_be_java ? " (may_be_java)" : "");
+#ifndef PRODUCT
+ klass_oop->print();
+ tty->print_cr("all fields:");
+ for (AllFieldStream fs(instanceKlass::cast(klass_oop)); !fs.done(); fs.next()) {
+ tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int());
+ }
+#endif //PRODUCT
fatal("Invalid layout of preloaded class");
return -1;
}
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -883,19 +883,14 @@
// Interface to java.lang.invoke.MethodHandle objects
-#define METHODHANDLE_INJECTED_FIELDS(macro) \
- macro(java_lang_invoke_MethodHandle, vmentry, intptr_signature, false) \
- macro(java_lang_invoke_MethodHandle, vmtarget, object_signature, true)
-
class MethodHandleEntry;
class java_lang_invoke_MethodHandle: AllStatic {
friend class JavaClasses;
private:
- static int _vmentry_offset; // assembly code trampoline for MH
- static int _vmtarget_offset; // class-specific target reference
- static int _type_offset; // the MethodType of this MH
+ static int _type_offset; // the MethodType of this MH
+ static int _form_offset; // the LambdaForm of this MH
static void compute_offsets();
@@ -904,13 +899,8 @@
static oop type(oop mh);
static void set_type(oop mh, oop mtype);
- static oop vmtarget(oop mh);
- static void set_vmtarget(oop mh, oop target);
-
- static MethodHandleEntry* vmentry(oop mh);
- static void set_vmentry(oop mh, MethodHandleEntry* data);
-
- static int vmslots(oop mh);
+ static oop form(oop mh);
+ static void set_form(oop mh, oop lform);
// Testers
static bool is_subclass(klassOop klass) {
@@ -922,149 +912,45 @@
// Accessors for code generation:
static int type_offset_in_bytes() { return _type_offset; }
- static int vmtarget_offset_in_bytes() { return _vmtarget_offset; }
- static int vmentry_offset_in_bytes() { return _vmentry_offset; }
+ static int form_offset_in_bytes() { return _form_offset; }
};
-#define DIRECTMETHODHANDLE_INJECTED_FIELDS(macro) \
- macro(java_lang_invoke_DirectMethodHandle, vmindex, int_signature, true)
+// Interface to java.lang.invoke.LambdaForm objects
+// (These are a private interface for managing adapter code generation.)
-class java_lang_invoke_DirectMethodHandle: public java_lang_invoke_MethodHandle {
+class java_lang_invoke_LambdaForm: AllStatic {
friend class JavaClasses;
private:
- static int _vmindex_offset; // negative or vtable idx or itable idx
+ static int _vmentry_offset; // type is MemberName
+
static void compute_offsets();
public:
// Accessors
- static int vmindex(oop mh);
- static void set_vmindex(oop mh, int index);
+ static oop vmentry(oop lform);
+ static void set_vmentry(oop lform, oop invoker);
// Testers
static bool is_subclass(klassOop klass) {
- return Klass::cast(klass)->is_subclass_of(SystemDictionary::DirectMethodHandle_klass());
+ return SystemDictionary::LambdaForm_klass() != NULL &&
+ Klass::cast(klass)->is_subclass_of(SystemDictionary::LambdaForm_klass());
}
static bool is_instance(oop obj) {
return obj != NULL && is_subclass(obj->klass());
}
// Accessors for code generation:
- static int vmindex_offset_in_bytes() { return _vmindex_offset; }
-};
-
-class java_lang_invoke_BoundMethodHandle: public java_lang_invoke_MethodHandle {
- friend class JavaClasses;
-
- private:
- static int _argument_offset; // argument value bound into this MH
- static int _vmargslot_offset; // relevant argument slot (<= vmslots)
- static void compute_offsets();
-
-public:
- static oop argument(oop mh);
- static void set_argument(oop mh, oop ref);
-
- static jint vmargslot(oop mh);
- static void set_vmargslot(oop mh, jint slot);
-
- // Testers
- static bool is_subclass(klassOop klass) {
- return Klass::cast(klass)->is_subclass_of(SystemDictionary::BoundMethodHandle_klass());
- }
- static bool is_instance(oop obj) {
- return obj != NULL && is_subclass(obj->klass());
- }
-
- static int argument_offset_in_bytes() { return _argument_offset; }
- static int vmargslot_offset_in_bytes() { return _vmargslot_offset; }
+ static int vmentry_offset_in_bytes() { return _vmentry_offset; }
};
-class java_lang_invoke_AdapterMethodHandle: public java_lang_invoke_BoundMethodHandle {
- friend class JavaClasses;
-
- private:
- static int _conversion_offset; // type of conversion to apply
- static void compute_offsets();
-
- public:
- static int conversion(oop mh);
- static void set_conversion(oop mh, int conv);
-
- // Testers
- static bool is_subclass(klassOop klass) {
- return Klass::cast(klass)->is_subclass_of(SystemDictionary::AdapterMethodHandle_klass());
- }
- static bool is_instance(oop obj) {
- return obj != NULL && is_subclass(obj->klass());
- }
-
- // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants):
- enum {
- OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
- OP_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
- 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 arguments using an auxiliary function
- OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
- 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
- CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
- CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
- CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
- CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1
- };
-
- static int conversion_offset_in_bytes() { return _conversion_offset; }
-};
-
-
-// A simple class that maintains an invocation count
-class java_lang_invoke_CountingMethodHandle: public java_lang_invoke_MethodHandle {
- friend class JavaClasses;
-
- private:
- static int _vmcount_offset;
- static void compute_offsets();
-
- public:
- // Accessors
- static int vmcount(oop mh);
- static void set_vmcount(oop mh, int count);
-
- // Testers
- static bool is_subclass(klassOop klass) {
- return SystemDictionary::CountingMethodHandle_klass() != NULL &&
- Klass::cast(klass)->is_subclass_of(SystemDictionary::CountingMethodHandle_klass());
- }
- static bool is_instance(oop obj) {
- return obj != NULL && is_subclass(obj->klass());
- }
-
- // Accessors for code generation:
- static int vmcount_offset_in_bytes() { return _vmcount_offset; }
-};
-
-
// Interface to java.lang.invoke.MemberName objects
// (These are a private interface for Java code to query the class hierarchy.)
-#define MEMBERNAME_INJECTED_FIELDS(macro) \
- macro(java_lang_invoke_MemberName, vmtarget, object_signature, true)
+#define MEMBERNAME_INJECTED_FIELDS(macro) \
+ macro(java_lang_invoke_MemberName, vmindex, intptr_signature, false) \
+ macro(java_lang_invoke_MemberName, vmtarget, object_signature, false)
class java_lang_invoke_MemberName: AllStatic {
friend class JavaClasses;
@@ -1076,7 +962,7 @@
// private Object type; // may be null if not yet materialized
// private int flags; // modifier bits; see reflect.Modifier
// private Object vmtarget; // VM-specific target value
- // private int vmindex; // method index within class or interface
+ // private intptr_t vmindex; // member index within class or interface
static int _clazz_offset;
static int _name_offset;
static int _type_offset;
@@ -1100,15 +986,11 @@
static int flags(oop mname);
static void set_flags(oop mname, int flags);
- static int modifiers(oop mname) { return (u2) flags(mname); }
- static void set_modifiers(oop mname, int mods)
- { set_flags(mname, (flags(mname) &~ (u2)-1) | (u2)mods); }
-
static oop vmtarget(oop mname);
static void set_vmtarget(oop mname, oop target);
- static int vmindex(oop mname);
- static void set_vmindex(oop mname, int index);
+ static intptr_t vmindex(oop mname);
+ static void set_vmindex(oop mname, intptr_t index);
// Testers
static bool is_subclass(klassOop klass) {
@@ -1124,9 +1006,11 @@
MN_IS_CONSTRUCTOR = 0x00020000, // constructor
MN_IS_FIELD = 0x00040000, // field
MN_IS_TYPE = 0x00080000, // nested type
- MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
- MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
- VM_INDEX_UNINITIALIZED = -99
+ MN_REFERENCE_KIND_SHIFT = 24, // refKind
+ MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+ // The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
+ MN_SEARCH_SUPERCLASSES = 0x00100000, // walk super classes
+ MN_SEARCH_INTERFACES = 0x00200000 // walk implemented interfaces
};
// Accessors for code generation:
@@ -1147,7 +1031,6 @@
private:
static int _rtype_offset;
static int _ptypes_offset;
- static int _form_offset;
static void compute_offsets();
@@ -1155,11 +1038,13 @@
// Accessors
static oop rtype(oop mt);
static objArrayOop ptypes(oop mt);
- static oop form(oop mt);
static oop ptype(oop mt, int index);
static int ptype_count(oop mt);
+ static int ptype_slot_count(oop mt); // extra counts for long/double
+ static int rtype_slot_count(oop mt); // extra counts for long/double
+
static Symbol* as_signature(oop mt, bool intern_if_not_found, TRAPS);
static void print_signature(oop mt, outputStream* st);
@@ -1172,40 +1057,6 @@
// Accessors for code generation:
static int rtype_offset_in_bytes() { return _rtype_offset; }
static int ptypes_offset_in_bytes() { return _ptypes_offset; }
- static int form_offset_in_bytes() { return _form_offset; }
-};
-
-#define METHODTYPEFORM_INJECTED_FIELDS(macro) \
- macro(java_lang_invoke_MethodTypeForm, vmslots, int_signature, true) \
- macro(java_lang_invoke_MethodTypeForm, vmlayout, object_signature, true)
-
-class java_lang_invoke_MethodTypeForm: AllStatic {
- friend class JavaClasses;
-
- 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
-
- static void compute_offsets();
-
- public:
- // Accessors
- static int vmslots(oop mtform);
- static void set_vmslots(oop mtform, int vmslots);
-
- 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; }
};
@@ -1275,6 +1126,7 @@
public:
static oop parent(oop loader);
+ static bool isAncestor(oop loader, oop cl);
// Support for parallelCapable field
static bool parallelCapable(oop the_class_mirror);
@@ -1284,6 +1136,14 @@
// Fix for 4474172
static oop non_reflection_class_loader(oop loader);
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::ClassLoader_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
// Debugging
friend class JavaClasses;
};
@@ -1425,10 +1285,7 @@
#define ALL_INJECTED_FIELDS(macro) \
CLASS_INJECTED_FIELDS(macro) \
- METHODHANDLE_INJECTED_FIELDS(macro) \
- DIRECTMETHODHANDLE_INJECTED_FIELDS(macro) \
- MEMBERNAME_INJECTED_FIELDS(macro) \
- METHODTYPEFORM_INJECTED_FIELDS(macro)
+ MEMBERNAME_INJECTED_FIELDS(macro)
// Interface to hard-coded offset checking
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -57,12 +57,15 @@
// Operator= increments reference count.
void operator=(const TempNewSymbol &s) {
+ //clear(); //FIXME
_temp = s._temp;
if (_temp !=NULL) _temp->increment_refcount();
}
// Decrement reference counter so it can go away if it's unique
- ~TempNewSymbol() { if (_temp != NULL) _temp->decrement_refcount(); }
+ void clear() { if (_temp != NULL) _temp->decrement_refcount(); _temp = NULL; }
+
+ ~TempNewSymbol() { clear(); }
// Operators so they can be used like Symbols
Symbol* operator -> () const { return _temp; }
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -30,6 +30,7 @@
#include "classfile/resolutionErrors.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
+#include "compiler/compileBroker.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/gcLocker.hpp"
@@ -193,7 +194,10 @@
// Forwards to resolve_instance_class_or_null
klassOop SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
- assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread");
+ assert(!THREAD->is_Compiler_thread(),
+ err_msg("can not load classes with compiler thread: class=%s, classloader=%s",
+ class_name->as_C_string(),
+ class_loader.is_null() ? "null" : class_loader->klass()->klass_part()->name()->as_C_string()));
if (FieldType::is_array(class_name)) {
return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
} else if (FieldType::is_obj(class_name)) {
@@ -2358,72 +2362,134 @@
}
-methodOop SystemDictionary::find_method_handle_invoke(Symbol* name,
- Symbol* signature,
- KlassHandle accessing_klass,
- TRAPS) {
- if (!EnableInvokeDynamic) return NULL;
- vmSymbols::SID name_id = vmSymbols::find_sid(name);
- assert(name_id != vmSymbols::NO_SID, "must be a known name");
- unsigned int hash = invoke_method_table()->compute_hash(signature, name_id);
+methodHandle SystemDictionary::find_method_handle_intrinsic(vmIntrinsics::ID iid,
+ Symbol* signature,
+ TRAPS) {
+ methodHandle empty;
+ assert(EnableInvokeDynamic, "");
+ assert(MethodHandles::is_signature_polymorphic(iid) &&
+ MethodHandles::is_signature_polymorphic_intrinsic(iid) &&
+ iid != vmIntrinsics::_invokeGeneric,
+ err_msg("must be a known MH intrinsic iid=%d: %s", iid, vmIntrinsics::name_at(iid)));
+
+ unsigned int hash = invoke_method_table()->compute_hash(signature, iid);
int index = invoke_method_table()->hash_to_index(hash);
- SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature, name_id);
- methodHandle non_cached_result;
+ SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature, iid);
+ methodHandle m;
if (spe == NULL || spe->property_oop() == NULL) {
spe = NULL;
// Must create lots of stuff here, but outside of the SystemDictionary lock.
- if (THREAD->is_Compiler_thread())
- return NULL; // do not attempt from within compiler
- bool for_invokeGeneric = (name_id != vmSymbols::VM_SYMBOL_ENUM_NAME(invokeExact_name));
- bool found_on_bcp = false;
- Handle mt = find_method_handle_type(signature, accessing_klass,
- for_invokeGeneric,
- found_on_bcp, CHECK_NULL);
- KlassHandle mh_klass = SystemDictionaryHandles::MethodHandle_klass();
- methodHandle m = methodOopDesc::make_invoke_method(mh_klass, name, signature,
- mt, CHECK_NULL);
+ m = methodOopDesc::make_method_handle_intrinsic(iid, signature, CHECK_(empty));
+ CompileBroker::compile_method(m, InvocationEntryBci, CompLevel_highest_tier,
+ methodHandle(), CompileThreshold, "MH", CHECK_(empty));
+
// Now grab the lock. We might have to throw away the new method,
// if a racing thread has managed to install one at the same time.
- if (found_on_bcp) {
- MutexLocker ml(SystemDictionary_lock, Thread::current());
- spe = invoke_method_table()->find_entry(index, hash, signature, name_id);
+ {
+ MutexLocker ml(SystemDictionary_lock, THREAD);
+ spe = invoke_method_table()->find_entry(index, hash, signature, iid);
if (spe == NULL)
- spe = invoke_method_table()->add_entry(index, hash, signature, name_id);
- if (spe->property_oop() == NULL) {
+ spe = invoke_method_table()->add_entry(index, hash, signature, iid);
+ 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)
- // vmlayout must be an invokeExact:
- && name_id == vmSymbols::VM_SYMBOL_ENUM_NAME(invokeExact_name)
- && java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) {
- java_lang_invoke_MethodTypeForm::init_vmlayout(mtform, m());
- }
+ }
+ }
+
+ assert(spe != NULL && spe->property_oop() != NULL, "");
+ m = methodOop(spe->property_oop());
+ assert(m->is_method(), "");
+
+ return m;
+}
+
+// Helper for unpacking the return value from linkMethod and linkCallSite.
+static methodHandle unpack_method_and_appendix(Handle mname,
+ objArrayHandle appendix_box,
+ Handle* appendix_result,
+ TRAPS) {
+ methodHandle empty;
+ if (mname.not_null()) {
+ oop vmtarget = java_lang_invoke_MemberName::vmtarget(mname());
+ if (vmtarget != NULL && vmtarget->is_method()) {
+ methodOop m = methodOop(vmtarget);
+ oop appendix = appendix_box->obj_at(0);
+ if (TraceMethodHandles) {
+ #ifndef PRODUCT
+ tty->print("Linked method="INTPTR_FORMAT": ", m);
+ m->print();
+ if (appendix != NULL) { tty->print("appendix = "); appendix->print(); }
+ tty->cr();
+ #endif //PRODUCT
}
- } else {
- non_cached_result = m;
+ (*appendix_result) = Handle(THREAD, appendix);
+ return methodHandle(THREAD, m);
}
}
- if (spe != NULL && spe->property_oop() != NULL) {
- assert(spe->property_oop()->is_method(), "");
- return (methodOop) spe->property_oop();
- } else {
- return non_cached_result();
+ THROW_MSG_(vmSymbols::java_lang_LinkageError(), "bad value from MethodHandleNatives", empty);
+ return empty;
+}
+
+methodHandle SystemDictionary::find_method_handle_invoker(Symbol* name,
+ Symbol* signature,
+ KlassHandle accessing_klass,
+ Handle* appendix_result,
+ TRAPS) {
+ methodHandle empty;
+ assert(EnableInvokeDynamic, "");
+ assert(!THREAD->is_Compiler_thread(), "");
+ Handle method_type =
+ SystemDictionary::find_method_handle_type(signature, accessing_klass, CHECK_(empty));
+ if (false) { // FIXME: Decide if the Java upcall should resolve signatures.
+ method_type = java_lang_String::create_from_symbol(signature, CHECK_(empty));
}
+
+ KlassHandle mh_klass = SystemDictionaryHandles::MethodHandle_klass();
+ int ref_kind = JVM_REF_invokeVirtual;
+ Handle name_str = StringTable::intern(name, CHECK_(empty));
+ objArrayHandle appendix_box = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1, CHECK_(empty));
+ assert(appendix_box->obj_at(0) == NULL, "");
+
+ // call java.lang.invoke.MethodHandleNatives::linkMethod(... String, MethodType) -> MemberName
+ JavaCallArguments args;
+ args.push_oop(accessing_klass()->java_mirror());
+ args.push_int(ref_kind);
+ args.push_oop(mh_klass()->java_mirror());
+ args.push_oop(name_str());
+ args.push_oop(method_type());
+ args.push_oop(appendix_box());
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_static(&result,
+ SystemDictionary::MethodHandleNatives_klass(),
+ vmSymbols::linkMethod_name(),
+ vmSymbols::linkMethod_signature(),
+ &args, CHECK_(empty));
+ Handle mname(THREAD, (oop) result.get_jobject());
+ return unpack_method_and_appendix(mname, appendix_box, appendix_result, THREAD);
}
+
// Ask Java code to find or construct a java.lang.invoke.MethodType for the given
// signature, as interpreted relative to the given class loader.
// Because of class loader constraints, all method handle usage must be
// consistent with this loader.
Handle SystemDictionary::find_method_handle_type(Symbol* signature,
KlassHandle accessing_klass,
- bool for_invokeGeneric,
- bool& return_bcp_flag,
TRAPS) {
+ Handle empty;
+ vmIntrinsics::ID null_iid = vmIntrinsics::_none; // distinct from all method handle invoker intrinsics
+ unsigned int hash = invoke_method_table()->compute_hash(signature, null_iid);
+ int index = invoke_method_table()->hash_to_index(hash);
+ SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature, null_iid);
+ if (spe != NULL && spe->property_oop() != NULL) {
+ assert(java_lang_invoke_MethodType::is_instance(spe->property_oop()), "");
+ return Handle(THREAD, spe->property_oop());
+ } else if (THREAD->is_Compiler_thread()) {
+ warning("SystemDictionary::find_method_handle_type called from compiler thread"); // FIXME
+ return Handle(); // do not attempt from within compiler, unless it was cached
+ }
+
Handle class_loader, protection_domain;
bool is_on_bcp = true; // keep this true as long as we can materialize from the boot classloader
- Handle empty;
int npts = ArgumentCount(signature).size();
objArrayHandle pts = oopFactory::new_objArray(SystemDictionary::Class_klass(), npts, CHECK_(empty));
int arg = 0;
@@ -2432,6 +2498,7 @@
for (SignatureStream ss(signature); !ss.is_done(); ss.next()) {
oop mirror = NULL;
if (is_on_bcp) {
+ // Note: class_loader & protection_domain are both null at this point.
mirror = ss.as_java_mirror(class_loader, protection_domain,
SignatureStream::ReturnNull, CHECK_(empty));
if (mirror == NULL) {
@@ -2452,9 +2519,11 @@
rt = Handle(THREAD, mirror);
else
pts->obj_at_put(arg++, mirror);
+
// Check accessibility.
if (ss.is_object() && accessing_klass.not_null()) {
klassOop sel_klass = java_lang_Class::as_klassOop(mirror);
+ mirror = NULL; // safety
// Emulate constantPoolOopDesc::verify_constant_pool_resolve.
if (Klass::cast(sel_klass)->oop_is_objArray())
sel_klass = objArrayKlass::cast(sel_klass)->bottom_klass();
@@ -2477,23 +2546,18 @@
&args, CHECK_(empty));
Handle method_type(THREAD, (oop) result.get_jobject());
- if (for_invokeGeneric) {
- // call java.lang.invoke.MethodHandleNatives::notifyGenericMethodType(MethodType) -> void
- JavaCallArguments args(Handle(THREAD, method_type()));
- JavaValue no_result(T_VOID);
- JavaCalls::call_static(&no_result,
- SystemDictionary::MethodHandleNatives_klass(),
- vmSymbols::notifyGenericMethodType_name(),
- vmSymbols::notifyGenericMethodType_signature(),
- &args, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- // If the notification fails, just kill it.
- CLEAR_PENDING_EXCEPTION;
+ if (is_on_bcp) {
+ // We can cache this MethodType inside the JVM.
+ MutexLocker ml(SystemDictionary_lock, THREAD);
+ spe = invoke_method_table()->find_entry(index, hash, signature, null_iid);
+ if (spe == NULL)
+ spe = invoke_method_table()->add_entry(index, hash, signature, null_iid);
+ if (spe->property_oop() == NULL) {
+ spe->set_property_oop(method_type());
}
}
- // report back to the caller with the MethodType and the "on_bcp" flag
- return_bcp_flag = is_on_bcp;
+ // report back to the caller with the MethodType
return method_type;
}
@@ -2508,8 +2572,7 @@
Handle name = java_lang_String::create_from_symbol(name_sym, CHECK_(empty));
Handle type;
if (signature->utf8_length() > 0 && signature->byte_at(0) == '(') {
- bool ignore_is_on_bcp = false;
- type = find_method_handle_type(signature, caller, false, ignore_is_on_bcp, CHECK_(empty));
+ type = find_method_handle_type(signature, caller, CHECK_(empty));
} else {
ResourceMark rm(THREAD);
SignatureStream ss(signature, false);
@@ -2543,119 +2606,54 @@
// Ask Java code to find or construct a java.lang.invoke.CallSite for the given
// name and signature, as interpreted relative to the given class loader.
-Handle SystemDictionary::make_dynamic_call_site(Handle bootstrap_method,
- Symbol* name,
- methodHandle signature_invoker,
- Handle info,
- methodHandle caller_method,
- int caller_bci,
- TRAPS) {
- Handle empty;
- guarantee(bootstrap_method.not_null() &&
- java_lang_invoke_MethodHandle::is_instance(bootstrap_method()),
+methodHandle SystemDictionary::find_dynamic_call_site_invoker(KlassHandle caller,
+ Handle bootstrap_specifier,
+ Symbol* name,
+ Symbol* type,
+ Handle* appendix_result,
+ TRAPS) {
+ methodHandle empty;
+ Handle bsm, info;
+ if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
+ bsm = bootstrap_specifier;
+ } else {
+ assert(bootstrap_specifier->is_objArray(), "");
+ objArrayHandle args(THREAD, (objArrayOop) bootstrap_specifier());
+ int len = args->length();
+ assert(len >= 1, "");
+ bsm = Handle(THREAD, args->obj_at(0));
+ if (len > 1) {
+ objArrayOop args1 = oopFactory::new_objArray(SystemDictionary::Object_klass(), len-1, CHECK_(empty));
+ for (int i = 1; i < len; i++)
+ args1->obj_at_put(i-1, args->obj_at(i));
+ info = Handle(THREAD, args1);
+ }
+ }
+ guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
"caller must supply a valid BSM");
- Handle caller_mname = MethodHandles::new_MemberName(CHECK_(empty));
- MethodHandles::init_MemberName(caller_mname(), caller_method());
-
- // call java.lang.invoke.MethodHandleNatives::makeDynamicCallSite(bootm, name, mtype, info, caller_mname, caller_pos)
- oop name_str_oop = StringTable::intern(name, CHECK_(empty)); // not a handle!
- JavaCallArguments args(Handle(THREAD, bootstrap_method()));
- args.push_oop(name_str_oop);
- args.push_oop(signature_invoker->method_handle_type());
+ Handle method_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
+ Handle method_type = find_method_handle_type(type, caller, CHECK_(empty));
+
+ objArrayHandle appendix_box = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1, CHECK_(empty));
+ assert(appendix_box->obj_at(0) == NULL, "");
+
+ // call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, bsm, name, mtype, info, &appendix)
+ JavaCallArguments args;
+ args.push_oop(caller->java_mirror());
+ args.push_oop(bsm());
+ args.push_oop(method_name());
+ args.push_oop(method_type());
args.push_oop(info());
- args.push_oop(caller_mname());
- args.push_int(caller_bci);
+ args.push_oop(appendix_box);
JavaValue result(T_OBJECT);
JavaCalls::call_static(&result,
SystemDictionary::MethodHandleNatives_klass(),
- vmSymbols::makeDynamicCallSite_name(),
- vmSymbols::makeDynamicCallSite_signature(),
+ vmSymbols::linkCallSite_name(),
+ vmSymbols::linkCallSite_signature(),
&args, CHECK_(empty));
- oop call_site_oop = (oop) result.get_jobject();
- assert(call_site_oop->is_oop()
- /*&& java_lang_invoke_CallSite::is_instance(call_site_oop)*/, "must be sane");
- if (TraceMethodHandles) {
-#ifndef PRODUCT
- tty->print_cr("Linked invokedynamic bci=%d site="INTPTR_FORMAT":", caller_bci, call_site_oop);
- call_site_oop->print();
- tty->cr();
-#endif //PRODUCT
- }
- return call_site_oop;
-}
-
-Handle SystemDictionary::find_bootstrap_method(methodHandle caller_method, int caller_bci,
- int cache_index,
- Handle& argument_info_result,
- TRAPS) {
- Handle empty;
-
- constantPoolHandle pool;
- {
- klassOop caller = caller_method->method_holder();
- if (!Klass::cast(caller)->oop_is_instance()) return empty;
- pool = constantPoolHandle(THREAD, instanceKlass::cast(caller)->constants());
- }
-
- int constant_pool_index = pool->cache()->entry_at(cache_index)->constant_pool_index();
- constantTag tag = pool->tag_at(constant_pool_index);
-
- if (tag.is_invoke_dynamic()) {
- // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type], plus optional arguments
- // The bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
- int bsm_index = pool->invoke_dynamic_bootstrap_method_ref_index_at(constant_pool_index);
- if (bsm_index != 0) {
- int bsm_index_in_cache = pool->cache()->entry_at(cache_index)->bootstrap_method_index_in_cache();
- DEBUG_ONLY(int bsm_index_2 = pool->cache()->entry_at(bsm_index_in_cache)->constant_pool_index());
- assert(bsm_index == bsm_index_2, "BSM constant lifted to cache");
- if (TraceMethodHandles) {
- tty->print_cr("resolving bootstrap method for "PTR_FORMAT" at %d at cache[%d]CP[%d]...",
- (intptr_t) caller_method(), caller_bci, cache_index, constant_pool_index);
- }
- oop bsm_oop = pool->resolve_cached_constant_at(bsm_index_in_cache, CHECK_(empty));
- if (TraceMethodHandles) {
- tty->print_cr("bootstrap method for "PTR_FORMAT" at %d retrieved as "PTR_FORMAT":",
- (intptr_t) caller_method(), caller_bci, (intptr_t) bsm_oop);
- }
- assert(bsm_oop->is_oop(), "must be sane");
- // caller must verify that it is of type MethodHandle
- Handle bsm(THREAD, bsm_oop);
- bsm_oop = NULL; // safety
-
- // Extract the optional static arguments.
- Handle argument_info; // either null, or one arg, or Object[]{arg...}
- int argc = pool->invoke_dynamic_argument_count_at(constant_pool_index);
- if (TraceInvokeDynamic) {
- tty->print_cr("find_bootstrap_method: [%d/%d] CONSTANT_InvokeDynamic: %d[%d]",
- constant_pool_index, cache_index, bsm_index, argc);
- }
- if (argc > 0) {
- objArrayHandle arg_array;
- if (argc > 1) {
- objArrayOop arg_array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_(empty));
- arg_array = objArrayHandle(THREAD, arg_array_oop);
- argument_info = arg_array;
- }
- for (int arg_i = 0; arg_i < argc; arg_i++) {
- int arg_index = pool->invoke_dynamic_argument_index_at(constant_pool_index, arg_i);
- oop arg_oop = pool->resolve_possibly_cached_constant_at(arg_index, CHECK_(empty));
- if (arg_array.is_null()) {
- argument_info = Handle(THREAD, arg_oop);
- } else {
- arg_array->obj_at_put(arg_i, arg_oop);
- }
- }
- }
-
- argument_info_result = argument_info; // return argument_info to caller
- return bsm;
- }
- } else {
- ShouldNotReachHere(); // verifier does not allow this
- }
-
- return empty;
+ Handle mname(THREAD, (oop) result.get_jobject());
+ return unpack_method_and_appendix(mname, appendix_box, appendix_result, THREAD);
}
// Since the identity hash code for symbols changes when the symbols are
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -148,15 +148,10 @@
template(MethodHandle_klass, java_lang_invoke_MethodHandle, Pre_JSR292) \
template(MemberName_klass, java_lang_invoke_MemberName, Pre_JSR292) \
template(MethodHandleNatives_klass, java_lang_invoke_MethodHandleNatives, Pre_JSR292) \
- template(AdapterMethodHandle_klass, java_lang_invoke_AdapterMethodHandle, Pre_JSR292) \
- template(BoundMethodHandle_klass, java_lang_invoke_BoundMethodHandle, Pre_JSR292) \
- template(DirectMethodHandle_klass, java_lang_invoke_DirectMethodHandle, Pre_JSR292) \
+ template(LambdaForm_klass, java_lang_invoke_LambdaForm, Opt) \
template(MethodType_klass, java_lang_invoke_MethodType, Pre_JSR292) \
- template(MethodTypeForm_klass, java_lang_invoke_MethodTypeForm, Pre_JSR292) \
template(BootstrapMethodError_klass, java_lang_BootstrapMethodError, Pre_JSR292) \
- template(WrongMethodTypeException_klass, java_lang_invoke_WrongMethodTypeException, Pre_JSR292) \
template(CallSite_klass, java_lang_invoke_CallSite, Pre_JSR292) \
- template(CountingMethodHandle_klass, java_lang_invoke_CountingMethodHandle, Opt) \
template(ConstantCallSite_klass, java_lang_invoke_ConstantCallSite, Pre_JSR292) \
template(MutableCallSite_klass, java_lang_invoke_MutableCallSite, Pre_JSR292) \
template(VolatileCallSite_klass, java_lang_invoke_VolatileCallSite, Pre_JSR292) \
@@ -485,17 +480,24 @@
Handle loader2, bool is_method, TRAPS);
// JSR 292
- // find the java.lang.invoke.MethodHandles::invoke method for a given signature
- static methodOop find_method_handle_invoke(Symbol* name,
- Symbol* signature,
- KlassHandle accessing_klass,
- TRAPS);
- // ask Java to compute a java.lang.invoke.MethodType object for a given signature
+ // find a java.lang.invoke.MethodHandle.invoke* method for a given signature
+ // (asks Java to compute it if necessary, except in a compiler thread)
+ static methodHandle find_method_handle_invoker(Symbol* name,
+ Symbol* signature,
+ KlassHandle accessing_klass,
+ Handle *appendix_result,
+ TRAPS);
+ // for a given signature, find the internal MethodHandle method (linkTo* or invokeBasic)
+ // (does not ask Java, since this is a low-level intrinsic defined by the JVM)
+ static methodHandle find_method_handle_intrinsic(vmIntrinsics::ID iid,
+ Symbol* signature,
+ TRAPS);
+ // find a java.lang.invoke.MethodType object for a given signature
+ // (asks Java to compute it if necessary, except in a compiler thread)
static Handle find_method_handle_type(Symbol* signature,
KlassHandle accessing_klass,
- bool for_invokeGeneric,
- bool& return_bcp_flag,
TRAPS);
+
// ask Java to compute a java.lang.invoke.MethodHandle object for a given CP entry
static Handle link_method_handle_constant(KlassHandle caller,
int ref_kind, //e.g., JVM_REF_invokeVirtual
@@ -503,23 +505,14 @@
Symbol* name,
Symbol* signature,
TRAPS);
+
// ask Java to create a dynamic call site, while linking an invokedynamic op
- static Handle make_dynamic_call_site(Handle bootstrap_method,
- // Callee information:
- Symbol* name,
- methodHandle signature_invoker,
- Handle info,
- // Caller information:
- methodHandle caller_method,
- int caller_bci,
- TRAPS);
-
- // coordinate with Java about bootstrap methods
- static Handle find_bootstrap_method(methodHandle caller_method,
- int caller_bci, // N.B. must be an invokedynamic
- int cache_index, // must be corresponding main_entry
- Handle &argument_info_result, // static BSM arguments, if any
- TRAPS);
+ static methodHandle find_dynamic_call_site_invoker(KlassHandle caller,
+ Handle bootstrap_method,
+ Symbol* name,
+ Symbol* type,
+ Handle *appendix_result,
+ TRAPS);
// Utility for printing loader "name" as part of tracing constraints
static const char* loader_name(oop loader) {
--- a/hotspot/src/share/vm/classfile/verifier.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/verifier.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -327,7 +327,7 @@
const char* bad_type_msg = "Bad type on operand stack in %s";
- int32_t max_stack = m->max_stack();
+ int32_t max_stack = m->verifier_max_stack();
int32_t max_locals = m->max_locals();
constantPoolHandle cp(THREAD, m->constants());
--- a/hotspot/src/share/vm/classfile/vmSymbols.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/vmSymbols.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -332,7 +332,14 @@
if (cname == NULL || mname == NULL || msig == NULL) return NULL;
klassOop k = SystemDictionary::find_well_known_klass(cname);
if (k == NULL) return NULL;
- return instanceKlass::cast(k)->find_method(mname, msig);
+ methodOop m = instanceKlass::cast(k)->find_method(mname, msig);
+ if (m == NULL &&
+ cname == vmSymbols::java_lang_invoke_MethodHandle() &&
+ msig == vmSymbols::star_name()) {
+ // Any signature polymorphic method is represented by a fixed concrete signature:
+ m = instanceKlass::cast(k)->find_method(mname, vmSymbols::object_array_object_signature());
+ }
+ return m;
}
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -160,6 +160,7 @@
template(java_lang_NoSuchMethodException, "java/lang/NoSuchMethodException") \
template(java_lang_NullPointerException, "java/lang/NullPointerException") \
template(java_lang_StringIndexOutOfBoundsException, "java/lang/StringIndexOutOfBoundsException")\
+ template(java_lang_UnsupportedOperationException, "java/lang/UnsupportedOperationException") \
template(java_lang_InvalidClassException, "java/lang/InvalidClassException") \
template(java_lang_reflect_InvocationTargetException, "java/lang/reflect/InvocationTargetException") \
template(java_lang_Exception, "java/lang/Exception") \
@@ -212,10 +213,12 @@
template(newField_signature, "(Lsun/reflect/FieldInfo;)Ljava/lang/reflect/Field;") \
template(newMethod_name, "newMethod") \
template(newMethod_signature, "(Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Method;") \
- /* the following two names must be in order: */ \
- template(invokeExact_name, "invokeExact") \
- template(invokeGeneric_name, "invokeGeneric") \
- template(invokeVarargs_name, "invokeVarargs") \
+ template(invokeBasic_name, "invokeBasic") \
+ template(linkToVirtual_name, "linkToVirtual") \
+ template(linkToStatic_name, "linkToStatic") \
+ template(linkToSpecial_name, "linkToSpecial") \
+ template(linkToInterface_name, "linkToInterface") \
+ template(compiledLambdaForm_name, "<compiledLambdaForm>") /*fake name*/ \
template(star_name, "*") /*not really a name*/ \
template(invoke_name, "invoke") \
template(override_name, "override") \
@@ -236,37 +239,33 @@
template(base_name, "base") \
\
/* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \
- template(java_lang_invoke_InvokeDynamic, "java/lang/invoke/InvokeDynamic") \
- template(java_lang_invoke_Linkage, "java/lang/invoke/Linkage") \
template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \
template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \
template(java_lang_invoke_MutableCallSite, "java/lang/invoke/MutableCallSite") \
template(java_lang_invoke_VolatileCallSite, "java/lang/invoke/VolatileCallSite") \
template(java_lang_invoke_MethodHandle, "java/lang/invoke/MethodHandle") \
template(java_lang_invoke_MethodType, "java/lang/invoke/MethodType") \
- template(java_lang_invoke_WrongMethodTypeException, "java/lang/invoke/WrongMethodTypeException") \
template(java_lang_invoke_MethodType_signature, "Ljava/lang/invoke/MethodType;") \
+ template(java_lang_invoke_MemberName_signature, "Ljava/lang/invoke/MemberName;") \
+ template(java_lang_invoke_LambdaForm_signature, "Ljava/lang/invoke/LambdaForm;") \
template(java_lang_invoke_MethodHandle_signature, "Ljava/lang/invoke/MethodHandle;") \
/* internal classes known only to the JVM: */ \
- template(java_lang_invoke_MethodTypeForm, "java/lang/invoke/MethodTypeForm") \
- template(java_lang_invoke_MethodTypeForm_signature, "Ljava/lang/invoke/MethodTypeForm;") \
template(java_lang_invoke_MemberName, "java/lang/invoke/MemberName") \
template(java_lang_invoke_MethodHandleNatives, "java/lang/invoke/MethodHandleNatives") \
- template(java_lang_invoke_MethodHandleImpl, "java/lang/invoke/MethodHandleImpl") \
- template(java_lang_invoke_AdapterMethodHandle, "java/lang/invoke/AdapterMethodHandle") \
- template(java_lang_invoke_BoundMethodHandle, "java/lang/invoke/BoundMethodHandle") \
- template(java_lang_invoke_DirectMethodHandle, "java/lang/invoke/DirectMethodHandle") \
- template(java_lang_invoke_CountingMethodHandle, "java/lang/invoke/CountingMethodHandle") \
+ template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \
template(java_lang_invoke_ForceInline_signature, "Ljava/lang/invoke/ForceInline;") \
+ template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \
+ template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \
+ template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \
/* internal up-calls made only by the JVM, via class sun.invoke.MethodHandleNatives: */ \
template(findMethodHandleType_name, "findMethodHandleType") \
template(findMethodHandleType_signature, "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;") \
- template(notifyGenericMethodType_name, "notifyGenericMethodType") \
- template(notifyGenericMethodType_signature, "(Ljava/lang/invoke/MethodType;)V") \
template(linkMethodHandleConstant_name, "linkMethodHandleConstant") \
template(linkMethodHandleConstant_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;") \
- template(makeDynamicCallSite_name, "makeDynamicCallSite") \
- template(makeDynamicCallSite_signature, "(Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/invoke/MemberName;I)Ljava/lang/invoke/CallSite;") \
+ template(linkMethod_name, "linkMethod") \
+ template(linkMethod_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
+ template(linkCallSite_name, "linkCallSite") \
+ template(linkCallSite_signature, "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
template(setTargetNormal_name, "setTargetNormal") \
template(setTargetVolatile_name, "setTargetVolatile") \
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
@@ -359,22 +358,15 @@
template(toString_name, "toString") \
template(values_name, "values") \
template(receiver_name, "receiver") \
- template(vmmethod_name, "vmmethod") \
template(vmtarget_name, "vmtarget") \
- template(vmentry_name, "vmentry") \
+ template(vmindex_name, "vmindex") \
template(vmcount_name, "vmcount") \
- template(vmslots_name, "vmslots") \
- template(vmlayout_name, "vmlayout") \
- template(vmindex_name, "vmindex") \
- template(vmargslot_name, "vmargslot") \
+ template(vmentry_name, "vmentry") \
template(flags_name, "flags") \
- template(argument_name, "argument") \
- template(conversion_name, "conversion") \
template(rtype_name, "rtype") \
template(ptypes_name, "ptypes") \
template(form_name, "form") \
- template(erasedType_name, "erasedType") \
- template(genericInvoker_name, "genericInvoker") \
+ template(basicType_name, "basicType") \
template(append_name, "append") \
template(klass_name, "klass") \
template(resolved_constructor_name, "resolved_constructor") \
@@ -922,15 +914,15 @@
\
do_intrinsic(_invoke, java_lang_reflect_Method, invoke_name, object_object_array_object_signature, F_R) \
/* (symbols invoke_name and invoke_signature defined above) */ \
- do_intrinsic(_checkSpreadArgument, java_lang_invoke_MethodHandleNatives, checkSpreadArgument_name, checkSpreadArgument_signature, F_S) \
- do_name( checkSpreadArgument_name, "checkSpreadArgument") \
- do_name( checkSpreadArgument_signature, "(Ljava/lang/Object;I)V") \
- do_intrinsic(_invokeExact, java_lang_invoke_MethodHandle, invokeExact_name, object_array_object_signature, F_RN) \
- do_intrinsic(_invokeGeneric, java_lang_invoke_MethodHandle, invokeGeneric_name, object_array_object_signature, F_RN) \
- do_intrinsic(_invokeVarargs, java_lang_invoke_MethodHandle, invokeVarargs_name, object_array_object_signature, F_R) \
- do_intrinsic(_invokeDynamic, java_lang_invoke_InvokeDynamic, star_name, object_array_object_signature, F_SN) \
- \
- do_intrinsic(_selectAlternative, java_lang_invoke_MethodHandleImpl, selectAlternative_name, selectAlternative_signature, F_S) \
+ /* the polymorphic MH intrinsics must be in compact order, with _invokeGeneric first and _linkToInterface last */ \
+ do_intrinsic(_invokeGeneric, java_lang_invoke_MethodHandle, invoke_name, star_name, F_RN) \
+ do_intrinsic(_invokeBasic, java_lang_invoke_MethodHandle, invokeBasic_name, star_name, F_RN) \
+ do_intrinsic(_linkToVirtual, java_lang_invoke_MethodHandle, linkToVirtual_name, star_name, F_SN) \
+ do_intrinsic(_linkToStatic, java_lang_invoke_MethodHandle, linkToStatic_name, star_name, F_SN) \
+ do_intrinsic(_linkToSpecial, java_lang_invoke_MethodHandle, linkToSpecial_name, star_name, F_SN) \
+ do_intrinsic(_linkToInterface, java_lang_invoke_MethodHandle, linkToInterface_name, star_name, F_SN) \
+ /* special marker for bytecode generated for the JVM from a LambdaForm: */ \
+ do_intrinsic(_compiledLambdaForm, java_lang_invoke_MethodHandle, compiledLambdaForm_name, star_name, F_RN) \
\
/* unboxing methods: */ \
do_intrinsic(_booleanValue, java_lang_Boolean, booleanValue_name, void_boolean_signature, F_R) \
@@ -1063,6 +1055,10 @@
ID_LIMIT,
LAST_COMPILER_INLINE = _prefetchWriteStatic,
+ FIRST_MH_SIG_POLY = _invokeGeneric,
+ FIRST_MH_STATIC = _linkToVirtual,
+ LAST_MH_SIG_POLY = _linkToInterface,
+
FIRST_ID = _none + 1
};
--- a/hotspot/src/share/vm/code/codeBlob.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/codeBlob.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -359,43 +359,6 @@
//----------------------------------------------------------------------------------------------------
-// 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
DeoptimizationBlob::DeoptimizationBlob(
--- a/hotspot/src/share/vm/code/codeBlob.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/codeBlob.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -35,7 +35,6 @@
// 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
@@ -99,7 +98,6 @@
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; }
@@ -350,50 +348,6 @@
//----------------------------------------------------------------------------------------------------
-// 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 {
--- a/hotspot/src/share/vm/code/codeCache.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/codeCache.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -796,7 +796,6 @@
int nmethodCount = 0;
int runtimeStubCount = 0;
int adapterCount = 0;
- int ricochetStubCount = 0;
int deoptimizationStubCount = 0;
int uncommonTrapStubCount = 0;
int bufferBlobCount = 0;
@@ -841,8 +840,6 @@
}
} 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()) {
@@ -879,7 +876,6 @@
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/code/debugInfoRec.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/debugInfoRec.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -311,6 +311,7 @@
assert(method == NULL ||
(method->is_native() && bci == 0) ||
(!method->is_native() && 0 <= bci && bci < method->code_size()) ||
+ (method->is_compiled_lambda_form() && bci == -99) || // this might happen in C1
bci == -1, "illegal bci");
// serialize the locals/expressions/monitors
--- a/hotspot/src/share/vm/code/nmethod.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/nmethod.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -945,8 +945,12 @@
void nmethod::print_on(outputStream* st, const char* msg) const {
if (st != NULL) {
ttyLocker ttyl;
- CompileTask::print_compilation(st, this, msg);
- if (WizardMode) st->print(" (" INTPTR_FORMAT ")", this);
+ if (WizardMode) {
+ CompileTask::print_compilation(st, this, msg, /*short_form:*/ true);
+ st->print_cr(" (" INTPTR_FORMAT ")", this);
+ } else {
+ CompileTask::print_compilation(st, this, msg, /*short_form:*/ false);
+ }
}
}
@@ -964,7 +968,9 @@
if (printmethod) {
print_code();
print_pcs();
- oop_maps()->print();
+ if (oop_maps()) {
+ oop_maps()->print();
+ }
}
if (PrintDebugInfo) {
print_scopes();
--- a/hotspot/src/share/vm/code/vtableStubs.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/code/vtableStubs.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -55,6 +55,8 @@
int index() const { return _index; }
static VMReg receiver_location() { return _receiver_location; }
void set_next(VtableStub* n) { _next = n; }
+
+ public:
address code_begin() const { return (address)(this + 1); }
address code_end() const { return code_begin() + pd_code_size_limit(_is_vtable_stub); }
address entry_point() const { return code_begin(); }
@@ -65,6 +67,7 @@
}
bool contains(address pc) const { return code_begin() <= pc && pc < code_end(); }
+ private:
void set_exception_points(address npe_addr, address ame_addr) {
_npe_offset = npe_addr - code_begin();
_ame_offset = ame_addr - code_begin();
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -407,7 +407,10 @@
if (is_osr_method) {
st->print(" @ %d", osr_bci);
}
- st->print(" (%d bytes)", method->code_size());
+ if (method->is_native())
+ st->print(" (native)");
+ else
+ st->print(" (%d bytes)", method->code_size());
}
if (msg != NULL) {
@@ -427,12 +430,17 @@
st->print(" "); // print compilation number
// method attributes
- const char sync_char = method->is_synchronized() ? 's' : ' ';
- const char exception_char = method->has_exception_handlers() ? '!' : ' ';
- const char monitors_char = method->has_monitor_bytecodes() ? 'm' : ' ';
+ if (method->is_loaded()) {
+ const char sync_char = method->is_synchronized() ? 's' : ' ';
+ const char exception_char = method->has_exception_handlers() ? '!' : ' ';
+ const char monitors_char = method->has_monitor_bytecodes() ? 'm' : ' ';
- // print method attributes
- st->print(" %c%c%c ", sync_char, exception_char, monitors_char);
+ // print method attributes
+ st->print(" %c%c%c ", sync_char, exception_char, monitors_char);
+ } else {
+ // %s!bn
+ st->print(" "); // print method attributes
+ }
if (TieredCompilation) {
st->print(" ");
@@ -444,7 +452,10 @@
st->print("@ %d ", bci); // print bci
method->print_short_name(st);
- st->print(" (%d bytes)", method->code_size());
+ if (method->is_loaded())
+ st->print(" (%d bytes)", method->code_size());
+ else
+ st->print(" (not loaded)");
if (msg != NULL) {
st->print(" %s", msg);
@@ -1018,6 +1029,7 @@
"sanity check");
assert(!instanceKlass::cast(method->method_holder())->is_not_initialized(),
"method holder must be initialized");
+ assert(!method->is_method_handle_intrinsic(), "do not enqueue these guys");
if (CIPrintRequests) {
tty->print("request: ");
@@ -1231,7 +1243,7 @@
//
// Note: A native method implies non-osr compilation which is
// checked with an assertion at the entry of this method.
- if (method->is_native()) {
+ if (method->is_native() && !method->is_method_handle_intrinsic()) {
bool in_base_library;
address adr = NativeLookup::lookup(method, in_base_library, THREAD);
if (HAS_PENDING_EXCEPTION) {
@@ -1264,7 +1276,7 @@
// do the compilation
if (method->is_native()) {
- if (!PreferInterpreterNativeStubs) {
+ if (!PreferInterpreterNativeStubs || method->is_method_handle_intrinsic()) {
// Acquire our lock.
int compile_id;
{
--- a/hotspot/src/share/vm/compiler/compileBroker.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/compiler/compileBroker.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -104,10 +104,10 @@
public:
void print_compilation(outputStream* st = tty, bool short_form = false);
- static void print_compilation(outputStream* st, const nmethod* nm, const char* msg = NULL) {
+ static void print_compilation(outputStream* st, const nmethod* nm, const char* msg = NULL, bool short_form = false) {
print_compilation_impl(st, nm->method(), nm->compile_id(), nm->comp_level(),
nm->is_osr_method(), nm->is_osr_method() ? nm->osr_entry_bci() : -1, /*is_blocking*/ false,
- msg);
+ msg, short_form);
}
static void print_inlining(outputStream* st, ciMethod* method, int inline_level, int bci, const char* msg = NULL);
--- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -99,7 +99,10 @@
empty, // empty method (code: _return)
accessor, // accessor method (code: _aload_0, _getfield, _(a|i)return)
abstract, // abstract method (throws an AbstractMethodException)
- method_handle, // java.lang.invoke.MethodHandles::invoke
+ method_handle_invoke_FIRST, // java.lang.invoke.MethodHandles::invokeExact, etc.
+ method_handle_invoke_LAST = (method_handle_invoke_FIRST
+ + (vmIntrinsics::LAST_MH_SIG_POLY
+ - vmIntrinsics::FIRST_MH_SIG_POLY)),
java_lang_math_sin, // implementation of java.lang.Math.sin (x)
java_lang_math_cos, // implementation of java.lang.Math.cos (x)
java_lang_math_tan, // implementation of java.lang.Math.tan (x)
@@ -114,6 +117,14 @@
invalid = -1
};
+ // Conversion from the part of the above enum to vmIntrinsics::_invokeExact, etc.
+ static vmIntrinsics::ID method_handle_intrinsic(MethodKind kind) {
+ if (kind >= method_handle_invoke_FIRST && kind <= method_handle_invoke_LAST)
+ return (vmIntrinsics::ID)( vmIntrinsics::FIRST_MH_SIG_POLY + (kind - method_handle_invoke_FIRST) );
+ else
+ return vmIntrinsics::_none;
+ }
+
enum SomeConstants {
number_of_result_handlers = 10 // number of result handlers for native calls
};
@@ -148,6 +159,9 @@
static address entry_for_kind(MethodKind k) { assert(0 <= k && k < number_of_method_entries, "illegal kind"); return _entry_table[k]; }
static address entry_for_method(methodHandle m) { return entry_for_kind(method_kind(m)); }
+ // used for bootstrapping method handles:
+ static void set_entry_for_kind(MethodKind k, address e);
+
static void print_method_kind(MethodKind kind) PRODUCT_RETURN;
static bool can_be_compiled(methodHandle m);
--- a/hotspot/src/share/vm/interpreter/bytecode.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/bytecode.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -120,19 +120,22 @@
void Bytecode_invoke::verify() const {
assert(is_valid(), "check invoke");
- assert(method()->constants()->cache() != NULL, "do not call this from verifier or rewriter");
+ assert(cpcache() != NULL, "do not call this from verifier or rewriter");
+}
+
+
+Symbol* Bytecode_member_ref::klass() const {
+ return constants()->klass_ref_at_noresolve(index());
+}
+
+
+Symbol* Bytecode_member_ref::name() const {
+ return constants()->name_ref_at(index());
}
Symbol* Bytecode_member_ref::signature() const {
- constantPoolOop constants = method()->constants();
- return constants->signature_ref_at(index());
-}
-
-
-Symbol* Bytecode_member_ref::name() const {
- constantPoolOop constants = method()->constants();
- return constants->name_ref_at(index());
+ return constants()->signature_ref_at(index());
}
@@ -146,18 +149,19 @@
methodHandle Bytecode_invoke::static_target(TRAPS) {
methodHandle m;
KlassHandle resolved_klass;
- constantPoolHandle constants(THREAD, _method->constants());
+ constantPoolHandle constants(THREAD, this->constants());
- if (java_code() == Bytecodes::_invokedynamic) {
- LinkResolver::resolve_dynamic_method(m, resolved_klass, constants, index(), CHECK_(methodHandle()));
- } else if (java_code() != Bytecodes::_invokeinterface) {
- LinkResolver::resolve_method(m, resolved_klass, constants, index(), CHECK_(methodHandle()));
- } else {
- LinkResolver::resolve_interface_method(m, resolved_klass, constants, index(), CHECK_(methodHandle()));
- }
+ Bytecodes::Code bc = invoke_code();
+ LinkResolver::resolve_method_statically(m, resolved_klass, bc, constants, index(), CHECK_(methodHandle()));
return m;
}
+Handle Bytecode_invoke::appendix(TRAPS) {
+ ConstantPoolCacheEntry* cpce = cpcache_entry();
+ if (cpce->has_appendix())
+ return Handle(THREAD, cpce->f1_appendix());
+ return Handle(); // usual case
+}
int Bytecode_member_ref::index() const {
// Note: Rewriter::rewrite changes the Java_u2 of an invokedynamic to a native_u4,
@@ -170,12 +174,16 @@
}
int Bytecode_member_ref::pool_index() const {
+ return cpcache_entry()->constant_pool_index();
+}
+
+ConstantPoolCacheEntry* Bytecode_member_ref::cpcache_entry() const {
int index = this->index();
DEBUG_ONLY({
if (!has_index_u4(code()))
- index -= constantPoolOopDesc::CPCACHE_INDEX_TAG;
+ index = constantPoolOopDesc::get_cpcache_index(index);
});
- return _method->constants()->cache()->entry_at(index)->constant_pool_index();
+ return cpcache()->entry_at(index);
}
// Implementation of Bytecode_field
--- a/hotspot/src/share/vm/interpreter/bytecode.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/bytecode.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -80,6 +80,7 @@
Bytecodes::Code code() const { return _code; }
Bytecodes::Code java_code() const { return Bytecodes::java_code(code()); }
+ Bytecodes::Code invoke_code() const { return (code() == Bytecodes::_invokehandle) ? code() : java_code(); }
// Static functions for parsing bytecodes in place.
int get_index_u1(Bytecodes::Code bc) const {
@@ -195,10 +196,14 @@
Bytecode_member_ref(methodHandle method, int bci) : Bytecode(method(), method()->bcp_from(bci)), _method(method) {}
methodHandle method() const { return _method; }
+ constantPoolOop constants() const { return _method->constants(); }
+ constantPoolCacheOop cpcache() const { return _method->constants()->cache(); }
+ ConstantPoolCacheEntry* cpcache_entry() const;
public:
int index() const; // cache index (loaded from instruction)
int pool_index() const; // constant pool index
+ Symbol* klass() const; // returns the klass of the method or field
Symbol* name() const; // returns the name of the method or field
Symbol* signature() const; // returns the signature of the method or field
@@ -218,13 +223,15 @@
// Attributes
methodHandle static_target(TRAPS); // "specified" method (from constant pool)
+ Handle appendix(TRAPS); // if CPCE::has_appendix (from constant pool)
// Testers
- bool is_invokeinterface() const { return java_code() == Bytecodes::_invokeinterface; }
- bool is_invokevirtual() const { return java_code() == Bytecodes::_invokevirtual; }
- bool is_invokestatic() const { return java_code() == Bytecodes::_invokestatic; }
- bool is_invokespecial() const { return java_code() == Bytecodes::_invokespecial; }
- bool is_invokedynamic() const { return java_code() == Bytecodes::_invokedynamic; }
+ bool is_invokeinterface() const { return invoke_code() == Bytecodes::_invokeinterface; }
+ bool is_invokevirtual() const { return invoke_code() == Bytecodes::_invokevirtual; }
+ bool is_invokestatic() const { return invoke_code() == Bytecodes::_invokestatic; }
+ bool is_invokespecial() const { return invoke_code() == Bytecodes::_invokespecial; }
+ bool is_invokedynamic() const { return invoke_code() == Bytecodes::_invokedynamic; }
+ bool is_invokehandle() const { return invoke_code() == Bytecodes::_invokehandle; }
bool has_receiver() const { return !is_invokestatic() && !is_invokedynamic(); }
@@ -232,15 +239,12 @@
is_invokevirtual() ||
is_invokestatic() ||
is_invokespecial() ||
- is_invokedynamic(); }
+ is_invokedynamic() ||
+ is_invokehandle(); }
- bool is_method_handle_invoke() const {
- return (is_invokedynamic() ||
- (is_invokevirtual() &&
- method()->constants()->klass_ref_at_noresolve(index()) == vmSymbols::java_lang_invoke_MethodHandle() &&
- methodOopDesc::is_method_handle_invoke_name(name())));
- }
+ bool has_appendix() { return cpcache_entry()->has_appendix(); }
+ private:
// Helper to skip verification. Used is_valid() to check if the result is really an invoke
inline friend Bytecode_invoke Bytecode_invoke_check(methodHandle method, int bci);
};
--- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1774,7 +1774,7 @@
oop obj;
if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) {
- obj = (oop) cache->f1();
+ obj = (oop) cache->f1_as_instance();
MORE_STACK(1); // Assume single slot push
} else {
obj = (oop) STACK_OBJECT(-1);
@@ -1785,7 +1785,7 @@
// Now store the result on the stack
//
TosState tos_type = cache->flag_state();
- int field_offset = cache->f2();
+ int field_offset = cache->f2_as_index();
if (cache->is_volatile()) {
if (tos_type == atos) {
VERIFY_OOP(obj->obj_field_acquire(field_offset));
@@ -1885,7 +1885,7 @@
--count;
}
if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) {
- obj = (oop) cache->f1();
+ obj = (oop) cache->f1_as_instance();
} else {
--count;
obj = (oop) STACK_OBJECT(count);
@@ -1895,7 +1895,7 @@
//
// Now store the result
//
- int field_offset = cache->f2();
+ int field_offset = cache->f2_as_index();
if (cache->is_volatile()) {
if (tos_type == itos) {
obj->release_int_field_put(field_offset, STACK_INT(-1));
@@ -2177,13 +2177,15 @@
// This kind of CP cache entry does not need to match the flags byte, because
// there is a 1-1 relation between bytecode type and CP entry type.
ConstantPoolCacheEntry* cache = cp->entry_at(index);
- if (cache->is_f1_null()) {
+ oop result = cache->f1_as_instance();
+ if (result == NULL) {
CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode),
handle_exception);
+ result = cache->f1_as_instance();
}
- VERIFY_OOP(cache->f1());
- SET_STACK_OBJECT(cache->f1(), 0);
+ VERIFY_OOP(result);
+ SET_STACK_OBJECT(result, 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
}
@@ -2204,13 +2206,15 @@
// there is a 1-1 relation between bytecode type and CP entry type.
assert(constantPoolCacheOopDesc::is_secondary_index(index), "incorrect format");
ConstantPoolCacheEntry* cache = cp->secondary_entry_at(index);
- if (cache->is_f1_null()) {
+ oop result = cache->f1_as_instance();
+ if (result == NULL) {
CALL_VM(InterpreterRuntime::resolve_invokedynamic(THREAD),
handle_exception);
+ result = cache->f1_as_instance();
}
- VERIFY_OOP(cache->f1());
- oop method_handle = java_lang_invoke_CallSite::target(cache->f1());
+ VERIFY_OOP(result);
+ oop method_handle = java_lang_invoke_CallSite::target(result);
CHECK_NULL(method_handle);
istate->set_msg(call_method_handle);
@@ -2239,11 +2243,11 @@
// java.lang.Object. See cpCacheOop.cpp for details.
// This code isn't produced by javac, but could be produced by
// another compliant java compiler.
- if (cache->is_methodInterface()) {
+ if (cache->is_forced_virtual()) {
methodOop callee;
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
if (cache->is_vfinal()) {
- callee = (methodOop) cache->f2();
+ callee = cache->f2_as_vfinal_method();
} else {
// get receiver
int parms = cache->parameter_size();
@@ -2251,7 +2255,7 @@
VERIFY_OOP(STACK_OBJECT(-parms));
instanceKlass* rcvrKlass = (instanceKlass*)
STACK_OBJECT(-parms)->klass()->klass_part();
- callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2()];
+ callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
}
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
@@ -2266,7 +2270,7 @@
// this could definitely be cleaned up QQQ
methodOop callee;
- klassOop iclass = (klassOop)cache->f1();
+ klassOop iclass = cache->f1_as_klass();
// instanceKlass* interface = (instanceKlass*) iclass->klass_part();
// get receiver
int parms = cache->parameter_size();
@@ -2284,7 +2288,7 @@
if (i == int2->itable_length()) {
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "");
}
- int mindex = cache->f2();
+ int mindex = cache->f2_as_index();
itableMethodEntry* im = ki->first_method_entry(rcvr->klass());
callee = im[mindex].method();
if (callee == NULL) {
@@ -2322,12 +2326,12 @@
methodOop callee;
if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) {
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
- if (cache->is_vfinal()) callee = (methodOop) cache->f2();
+ if (cache->is_vfinal()) callee = cache->f2_as_vfinal_method();
else {
// get receiver
int parms = cache->parameter_size();
// this works but needs a resourcemark and seems to create a vtable on every call:
- // methodOop callee = rcvr->klass()->klass_part()->vtable()->method_at(cache->f2());
+ // methodOop callee = rcvr->klass()->klass_part()->vtable()->method_at(cache->f2_as_index());
//
// this fails with an assert
// instanceKlass* rcvrKlass = instanceKlass::cast(STACK_OBJECT(-parms)->klass());
@@ -2350,13 +2354,13 @@
However it seems to have a vtable in the right location. Huh?
*/
- callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2()];
+ callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
}
} else {
if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
}
- callee = (methodOop) cache->f1();
+ callee = cache->f1_as_method();
}
istate->set_callee(callee);
--- a/hotspot/src/share/vm/interpreter/bytecodes.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -534,6 +534,8 @@
def(_return_register_finalizer , "return_register_finalizer" , "b" , NULL , T_VOID , 0, true, _return);
+ def(_invokehandle , "invokehandle" , "bJJ" , NULL , T_ILLEGAL, -1, true, _invokevirtual );
+
def(_fast_aldc , "fast_aldc" , "bj" , NULL , T_OBJECT, 1, true, _ldc );
def(_fast_aldc_w , "fast_aldc_w" , "bJJ" , NULL , T_OBJECT, 1, true, _ldc_w );
--- a/hotspot/src/share/vm/interpreter/bytecodes.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/bytecodes.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -282,6 +282,9 @@
_return_register_finalizer ,
+ // special handling of signature-polymorphic methods:
+ _invokehandle ,
+
_shouldnotreachhere, // For debugging
// Platform specific JVM bytecodes
@@ -356,8 +359,8 @@
public:
// Conversion
- static void check (Code code) { assert(is_defined(code), "illegal code"); }
- static void wide_check (Code code) { assert(wide_is_defined(code), "illegal code"); }
+ static void check (Code code) { assert(is_defined(code), err_msg("illegal code: %d", (int)code)); }
+ static void wide_check (Code code) { assert(wide_is_defined(code), err_msg("illegal code: %d", (int)code)); }
static Code cast (int code) { return (Code)code; }
--- a/hotspot/src/share/vm/interpreter/interpreter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/interpreter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -37,6 +37,7 @@
#include "oops/oop.inline.hpp"
#include "prims/forte.hpp"
#include "prims/jvmtiExport.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
@@ -180,14 +181,21 @@
// Abstract method?
if (m->is_abstract()) return abstract;
- // Invoker for method handles?
- if (m->is_method_handle_invoke()) return method_handle;
+ // Method handle primitive?
+ if (m->is_method_handle_intrinsic()) {
+ vmIntrinsics::ID id = m->intrinsic_id();
+ assert(MethodHandles::is_signature_polymorphic(id), "must match an intrinsic");
+ MethodKind kind = (MethodKind)( method_handle_invoke_FIRST +
+ ((int)id - vmIntrinsics::FIRST_MH_SIG_POLY) );
+ assert(kind <= method_handle_invoke_LAST, "parallel enum ranges");
+ return kind;
+ }
// Native method?
// Note: This test must come _before_ the test for intrinsic
// methods. See also comments below.
if (m->is_native()) {
- assert(!m->is_method_handle_invoke(), "overlapping bits here, watch out");
+ assert(!m->is_method_handle_intrinsic(), "overlapping bits here, watch out");
return m->is_synchronized() ? native_synchronized : native;
}
@@ -239,6 +247,14 @@
}
+void AbstractInterpreter::set_entry_for_kind(AbstractInterpreter::MethodKind kind, address entry) {
+ assert(kind >= method_handle_invoke_FIRST &&
+ kind <= method_handle_invoke_LAST, "late initialization only for MH entry points");
+ assert(_entry_table[kind] == _entry_table[abstract], "previous value must be AME entry");
+ _entry_table[kind] = entry;
+}
+
+
// Return true if the interpreter can prove that the given bytecode has
// not yet been executed (in Java semantics, not in actual operation).
bool AbstractInterpreter::is_not_reached(methodHandle method, int bci) {
@@ -270,7 +286,6 @@
case empty : tty->print("empty" ); break;
case accessor : tty->print("accessor" ); break;
case abstract : tty->print("abstract" ); break;
- case method_handle : tty->print("method_handle" ); break;
case java_lang_math_sin : tty->print("java_lang_math_sin" ); break;
case java_lang_math_cos : tty->print("java_lang_math_cos" ); break;
case java_lang_math_tan : tty->print("java_lang_math_tan" ); break;
@@ -278,7 +293,16 @@
case java_lang_math_sqrt : tty->print("java_lang_math_sqrt" ); break;
case java_lang_math_log : tty->print("java_lang_math_log" ); break;
case java_lang_math_log10 : tty->print("java_lang_math_log10" ); break;
- default : ShouldNotReachHere();
+ default:
+ if (kind >= method_handle_invoke_FIRST &&
+ kind <= method_handle_invoke_LAST) {
+ const char* kind_name = vmIntrinsics::name_at(method_handle_intrinsic(kind));
+ if (kind_name[0] == '_') kind_name = &kind_name[1]; // '_invokeExact' => 'invokeExact'
+ tty->print("method_handle_%s", kind_name);
+ break;
+ }
+ ShouldNotReachHere();
+ break;
}
}
#endif // PRODUCT
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -145,7 +145,7 @@
// The bytecode wrappers aren't GC-safe so construct a new one
Bytecode_loadconstant ldc2(m, bci(thread));
ConstantPoolCacheEntry* cpce = m->constants()->cache()->entry_at(ldc2.cache_index());
- assert(result == cpce->f1(), "expected result for assembly code");
+ assert(result == cpce->f1_as_instance(), "expected result for assembly code");
}
#endif
}
@@ -656,7 +656,7 @@
JvmtiExport::post_raw_breakpoint(thread, method, bcp);
IRT_END
-IRT_ENTRY(void, InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode))
+IRT_ENTRY(void, InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode)) {
// extract receiver from the outgoing argument list if necessary
Handle receiver(thread, NULL);
if (bytecode == Bytecodes::_invokevirtual || bytecode == Bytecodes::_invokeinterface) {
@@ -724,86 +724,54 @@
info.resolved_method(),
info.vtable_index());
}
+}
+IRT_END
+
+
+// First time execution: Resolve symbols, create a permanent MethodType object.
+IRT_ENTRY(void, InterpreterRuntime::resolve_invokehandle(JavaThread* thread)) {
+ assert(EnableInvokeDynamic, "");
+ const Bytecodes::Code bytecode = Bytecodes::_invokehandle;
+
+ // resolve method
+ CallInfo info;
+ constantPoolHandle pool(thread, method(thread)->constants());
+
+ {
+ JvmtiHideSingleStepping jhss(thread);
+ LinkResolver::resolve_invoke(info, Handle(), pool,
+ get_index_u2_cpcache(thread, bytecode), bytecode, CHECK);
+ } // end JvmtiHideSingleStepping
+
+ cache_entry(thread)->set_method_handle(
+ info.resolved_method(),
+ info.resolved_appendix());
+}
IRT_END
// First time execution: Resolve symbols, create a permanent CallSite object.
IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) {
- ResourceMark rm(thread);
-
assert(EnableInvokeDynamic, "");
-
const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;
- methodHandle caller_method(thread, method(thread));
-
- constantPoolHandle pool(thread, caller_method->constants());
- pool->set_invokedynamic(); // mark header to flag active call sites
+ //TO DO: consider passing BCI to Java.
+ // int caller_bci = method(thread)->bci_from(bcp(thread));
- int caller_bci = 0;
- int site_index = 0;
- { address caller_bcp = bcp(thread);
- caller_bci = caller_method->bci_from(caller_bcp);
- site_index = Bytes::get_native_u4(caller_bcp+1);
- }
- assert(site_index == InterpreterRuntime::bytecode(thread).get_index_u4(bytecode), "");
- assert(constantPoolCacheOopDesc::is_secondary_index(site_index), "proper format");
- // there is a second CPC entries that is of interest; it caches signature info:
- int main_index = pool->cache()->secondary_entry_at(site_index)->main_entry_index();
- int pool_index = pool->cache()->entry_at(main_index)->constant_pool_index();
-
- // first resolve the signature to a MH.invoke methodOop
- if (!pool->cache()->entry_at(main_index)->is_resolved(bytecode)) {
- JvmtiHideSingleStepping jhss(thread);
- CallInfo callinfo;
- LinkResolver::resolve_invoke(callinfo, Handle(), pool,
- site_index, bytecode, CHECK);
- // The main entry corresponds to a JVM_CONSTANT_InvokeDynamic, and serves
- // as a common reference point for all invokedynamic call sites with
- // that exact call descriptor. We will link it in the CP cache exactly
- // as if it were an invokevirtual of MethodHandle.invoke.
- pool->cache()->entry_at(main_index)->set_method(
- bytecode,
- callinfo.resolved_method(),
- callinfo.vtable_index());
- }
+ // resolve method
+ CallInfo info;
+ constantPoolHandle pool(thread, method(thread)->constants());
+ int index = get_index_u4(thread, bytecode);
- // The method (f2 entry) of the main entry is the MH.invoke for the
- // invokedynamic target call signature.
- oop f1_value = pool->cache()->entry_at(main_index)->f1();
- methodHandle signature_invoker(THREAD, (methodOop) f1_value);
- assert(signature_invoker.not_null() && signature_invoker->is_method() && signature_invoker->is_method_handle_invoke(),
- "correct result from LinkResolver::resolve_invokedynamic");
-
- Handle info; // optional argument(s) in JVM_CONSTANT_InvokeDynamic
- Handle bootm = SystemDictionary::find_bootstrap_method(caller_method, caller_bci,
- main_index, info, CHECK);
- if (!java_lang_invoke_MethodHandle::is_instance(bootm())) {
- THROW_MSG(vmSymbols::java_lang_IllegalStateException(),
- "no bootstrap method found for invokedynamic");
- }
+ {
+ JvmtiHideSingleStepping jhss(thread);
+ LinkResolver::resolve_invoke(info, Handle(), pool,
+ index, bytecode, CHECK);
+ } // end JvmtiHideSingleStepping
- // Short circuit if CallSite has been bound already:
- if (!pool->cache()->secondary_entry_at(site_index)->is_f1_null())
- return;
-
- Symbol* call_site_name = pool->name_ref_at(site_index);
-
- Handle call_site
- = SystemDictionary::make_dynamic_call_site(bootm,
- // Callee information:
- call_site_name,
- signature_invoker,
- info,
- // Caller information:
- caller_method,
- caller_bci,
- CHECK);
-
- // In the secondary entry, the f1 field is the call site, and the f2 (index)
- // field is some data about the invoke site. Currently, it is just the BCI.
- // Later, it might be changed to help manage inlining dependencies.
- pool->cache()->secondary_entry_at(site_index)->set_dynamic_call(call_site, signature_invoker);
+ pool->cache()->secondary_entry_at(index)->set_dynamic_call(
+ info.resolved_method(),
+ info.resolved_appendix());
}
IRT_END
@@ -975,7 +943,7 @@
// check the access_flags for the field in the klass
- instanceKlass* ik = instanceKlass::cast(java_lang_Class::as_klassOop(cp_entry->f1()));
+ instanceKlass* ik = instanceKlass::cast(java_lang_Class::as_klassOop(cp_entry->f1_as_klass_mirror()));
int index = cp_entry->field_index();
if ((ik->field_access_flags(index) & JVM_ACC_FIELD_ACCESS_WATCHED) == 0) return;
@@ -998,15 +966,15 @@
// non-static field accessors have an object, but we need a handle
h_obj = Handle(thread, obj);
}
- instanceKlassHandle h_cp_entry_f1(thread, java_lang_Class::as_klassOop(cp_entry->f1()));
- jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_cp_entry_f1, cp_entry->f2(), is_static);
+ instanceKlassHandle h_cp_entry_f1(thread, java_lang_Class::as_klassOop(cp_entry->f1_as_klass_mirror()));
+ jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_cp_entry_f1, cp_entry->f2_as_index(), is_static);
JvmtiExport::post_field_access(thread, method(thread), bcp(thread), h_cp_entry_f1, h_obj, fid);
IRT_END
IRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread *thread,
oopDesc* obj, ConstantPoolCacheEntry *cp_entry, jvalue *value))
- klassOop k = java_lang_Class::as_klassOop(cp_entry->f1());
+ klassOop k = java_lang_Class::as_klassOop(cp_entry->f1_as_klass_mirror());
// check the access_flags for the field in the klass
instanceKlass* ik = instanceKlass::cast(k);
@@ -1031,7 +999,7 @@
HandleMark hm(thread);
instanceKlassHandle h_klass(thread, k);
- jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_klass, cp_entry->f2(), is_static);
+ jfieldID fid = jfieldIDWorkaround::to_jfieldID(h_klass, cp_entry->f2_as_index(), is_static);
jvalue fvalue;
#ifdef _LP64
fvalue = *value;
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -71,6 +71,8 @@
{ return bytecode(thread).get_index_u2(bc); }
static int get_index_u2_cpcache(JavaThread *thread, Bytecodes::Code bc)
{ return bytecode(thread).get_index_u2_cpcache(bc); }
+ static int get_index_u4(JavaThread *thread, Bytecodes::Code bc)
+ { return bytecode(thread).get_index_u4(bc); }
static int number_of_dimensions(JavaThread *thread) { return bcp(thread)[3]; }
static ConstantPoolCacheEntry* cache_entry_at(JavaThread *thread, int i) { return method(thread)->constants()->cache()->entry_at(i); }
@@ -118,6 +120,7 @@
// Calls
static void resolve_invoke (JavaThread* thread, Bytecodes::Code bytecode);
+ static void resolve_invokehandle (JavaThread* thread);
static void resolve_invokedynamic(JavaThread* thread);
// Breakpoints
--- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -96,15 +96,21 @@
void CallInfo::set_virtual(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) {
assert(vtable_index >= 0 || vtable_index == methodOopDesc::nonvirtual_vtable_index, "valid index");
set_common(resolved_klass, selected_klass, resolved_method, selected_method, vtable_index, CHECK);
+ assert(!resolved_method->is_compiled_lambda_form(), "these must be handled via an invokehandle call");
}
-void CallInfo::set_dynamic(methodHandle resolved_method, TRAPS) {
- assert(resolved_method->is_method_handle_invoke(), "");
+void CallInfo::set_handle(methodHandle resolved_method, Handle resolved_appendix, TRAPS) {
+ if (resolved_method.is_null()) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "resolved method is null");
+ }
KlassHandle resolved_klass = SystemDictionaryHandles::MethodHandle_klass();
- assert(resolved_klass == resolved_method->method_holder(), "");
+ assert(resolved_method->intrinsic_id() == vmIntrinsics::_invokeBasic ||
+ resolved_method->is_compiled_lambda_form(),
+ "linkMethod must return one of these");
int vtable_index = methodOopDesc::nonvirtual_vtable_index;
assert(resolved_method->vtable_index() == vtable_index, "");
- set_common(resolved_klass, KlassHandle(), resolved_method, resolved_method, vtable_index, CHECK);
+ set_common(resolved_klass, resolved_klass, resolved_method, resolved_method, vtable_index, CHECK);
+ _resolved_appendix = resolved_appendix;
}
void CallInfo::set_common(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) {
@@ -114,6 +120,7 @@
_resolved_method = resolved_method;
_selected_method = selected_method;
_vtable_index = vtable_index;
+ _resolved_appendix = Handle();
if (CompilationPolicy::must_be_compiled(selected_method)) {
// This path is unusual, mostly used by the '-Xcomp' stress test mode.
@@ -180,11 +187,9 @@
void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS) {
methodOop result_oop = klass->uncached_lookup_method(name, signature);
if (EnableInvokeDynamic && result_oop != NULL) {
- switch (result_oop->intrinsic_id()) {
- case vmIntrinsics::_invokeExact:
- case vmIntrinsics::_invokeGeneric:
- case vmIntrinsics::_invokeDynamic:
- // Do not link directly to these. The VM must produce a synthetic one using lookup_implicit_method.
+ vmIntrinsics::ID iid = result_oop->intrinsic_id();
+ if (MethodHandles::is_signature_polymorphic(iid)) {
+ // Do not link directly to these. The VM must produce a synthetic one using lookup_polymorphic_method.
return;
}
}
@@ -213,31 +218,97 @@
result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature));
}
-void LinkResolver::lookup_implicit_method(methodHandle& result,
- KlassHandle klass, Symbol* name, Symbol* signature,
- KlassHandle current_klass,
- TRAPS) {
+void LinkResolver::lookup_polymorphic_method(methodHandle& result,
+ KlassHandle klass, Symbol* name, Symbol* full_signature,
+ KlassHandle current_klass,
+ Handle* appendix_result_or_null,
+ TRAPS) {
+ vmIntrinsics::ID iid = MethodHandles::signature_polymorphic_name_id(name);
+ if (TraceMethodHandles) {
+ tty->print_cr("lookup_polymorphic_method iid=%s %s.%s%s",
+ vmIntrinsics::name_at(iid), klass->external_name(),
+ name->as_C_string(), full_signature->as_C_string());
+ }
if (EnableInvokeDynamic &&
klass() == SystemDictionary::MethodHandle_klass() &&
- methodOopDesc::is_method_handle_invoke_name(name)) {
- if (!THREAD->is_Compiler_thread() && !MethodHandles::enabled()) {
- // Make sure the Java part of the runtime has been booted up.
- klassOop natives = SystemDictionary::MethodHandleNatives_klass();
- if (natives == NULL || instanceKlass::cast(natives)->is_not_initialized()) {
- SystemDictionary::resolve_or_fail(vmSymbols::java_lang_invoke_MethodHandleNatives(),
- Handle(),
- Handle(),
- true,
- CHECK);
+ iid != vmIntrinsics::_none) {
+ if (MethodHandles::is_signature_polymorphic_intrinsic(iid)) {
+ // Most of these do not need an up-call to Java to resolve, so can be done anywhere.
+ // Do not erase last argument type (MemberName) if it is a static linkTo method.
+ bool keep_last_arg = MethodHandles::is_signature_polymorphic_static(iid);
+ TempNewSymbol basic_signature =
+ MethodHandles::lookup_basic_type_signature(full_signature, keep_last_arg, CHECK);
+ if (TraceMethodHandles) {
+ tty->print_cr("lookup_polymorphic_method %s %s => basic %s",
+ name->as_C_string(),
+ full_signature->as_C_string(),
+ basic_signature->as_C_string());
+ }
+ result = SystemDictionary::find_method_handle_intrinsic(iid,
+ basic_signature,
+ CHECK);
+ if (result.not_null()) {
+ assert(result->is_method_handle_intrinsic(), "MH.invokeBasic or MH.linkTo* intrinsic");
+ assert(result->intrinsic_id() != vmIntrinsics::_invokeGeneric, "wrong place to find this");
+ assert(basic_signature == result->signature(), "predict the result signature");
+ if (TraceMethodHandles) {
+ tty->print("lookup_polymorphic_method => intrinsic ");
+ result->print_on(tty);
+ }
+ return;
}
- }
- methodOop result_oop = SystemDictionary::find_method_handle_invoke(name,
- signature,
- current_klass,
- CHECK);
- if (result_oop != NULL) {
- assert(result_oop->is_method_handle_invoke() && result_oop->signature() == signature, "consistent");
- result = methodHandle(THREAD, result_oop);
+ } else if (iid == vmIntrinsics::_invokeGeneric
+ && !THREAD->is_Compiler_thread()
+ && appendix_result_or_null != NULL) {
+ // This is a method with type-checking semantics.
+ // We will ask Java code to spin an adapter method for it.
+ if (!MethodHandles::enabled()) {
+ // Make sure the Java part of the runtime has been booted up.
+ klassOop natives = SystemDictionary::MethodHandleNatives_klass();
+ if (natives == NULL || instanceKlass::cast(natives)->is_not_initialized()) {
+ SystemDictionary::resolve_or_fail(vmSymbols::java_lang_invoke_MethodHandleNatives(),
+ Handle(),
+ Handle(),
+ true,
+ CHECK);
+ }
+ }
+
+ Handle appendix;
+ result = SystemDictionary::find_method_handle_invoker(name,
+ full_signature,
+ current_klass,
+ &appendix,
+ CHECK);
+ if (TraceMethodHandles) {
+ tty->print("lookup_polymorphic_method => (via Java) ");
+ result->print_on(tty);
+ tty->print(" lookup_polymorphic_method => appendix = ");
+ if (appendix.is_null()) tty->print_cr("(none)");
+ else appendix->print_on(tty);
+ }
+ if (result.not_null()) {
+#ifdef ASSERT
+ TempNewSymbol basic_signature =
+ MethodHandles::lookup_basic_type_signature(full_signature, CHECK);
+ int actual_size_of_params = result->size_of_parameters();
+ int expected_size_of_params = ArgumentSizeComputer(basic_signature).size();
+ // +1 for MethodHandle.this, +1 for trailing MethodType
+ if (!MethodHandles::is_signature_polymorphic_static(iid)) expected_size_of_params += 1;
+ if (appendix.not_null()) expected_size_of_params += 1;
+ if (actual_size_of_params != expected_size_of_params) {
+ tty->print_cr("*** basic_signature=%s", basic_signature->as_C_string());
+ tty->print_cr("*** result for %s: ", vmIntrinsics::name_at(iid));
+ result->print();
+ }
+ assert(actual_size_of_params == expected_size_of_params,
+ err_msg("%d != %d", actual_size_of_params, expected_size_of_params));
+#endif //ASSERT
+
+ assert(appendix_result_or_null != NULL, "");
+ (*appendix_result_or_null) = appendix;
+ return;
+ }
}
}
}
@@ -267,6 +338,7 @@
new_flags = new_flags | JVM_ACC_PUBLIC;
flags.set_flags(new_flags);
}
+// assert(extra_arg_result_or_null != NULL, "must be able to return extra argument");
if (!Reflection::verify_field_access(ref_klass->as_klassOop(),
resolved_klass->as_klassOop(),
@@ -287,10 +359,19 @@
}
}
-void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle& resolved_klass,
- constantPoolHandle pool, int index, TRAPS) {
+void LinkResolver::resolve_method_statically(methodHandle& resolved_method, KlassHandle& resolved_klass,
+ Bytecodes::Code code, constantPoolHandle pool, int index, TRAPS) {
// resolve klass
+ if (code == Bytecodes::_invokedynamic) {
+ resolved_klass = SystemDictionaryHandles::MethodHandle_klass();
+ Symbol* method_name = vmSymbols::invoke_name();
+ Symbol* method_signature = pool->signature_ref_at(index);
+ KlassHandle current_klass(THREAD, pool->pool_holder());
+ resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
+ return;
+ }
+
resolve_klass(resolved_klass, pool, index, CHECK);
Symbol* method_name = pool->name_ref_at(index);
@@ -299,7 +380,7 @@
if (pool->has_preresolution()
|| (resolved_klass() == SystemDictionary::MethodHandle_klass() &&
- methodOopDesc::is_method_handle_invoke_name(method_name))) {
+ MethodHandles::is_signature_polymorphic_name(resolved_klass(), method_name))) {
methodOop result_oop = constantPoolOopDesc::method_at_if_loaded(pool, index);
if (result_oop != NULL) {
resolved_method = methodHandle(THREAD, result_oop);
@@ -307,33 +388,13 @@
}
}
- resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
+ if (code == Bytecodes::_invokeinterface) {
+ resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
+ } else {
+ resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
+ }
}
-void LinkResolver::resolve_dynamic_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) {
- // The class is java.lang.invoke.MethodHandle
- resolved_klass = SystemDictionaryHandles::MethodHandle_klass();
-
- Symbol* method_name = vmSymbols::invokeExact_name();
-
- Symbol* method_signature = pool->signature_ref_at(index);
- KlassHandle current_klass (THREAD, pool->pool_holder());
-
- resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
-}
-
-void LinkResolver::resolve_interface_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) {
-
- // resolve klass
- resolve_klass(resolved_klass, pool, index, CHECK);
- Symbol* method_name = pool->name_ref_at(index);
- Symbol* method_signature = pool->signature_ref_at(index);
- KlassHandle current_klass(THREAD, pool->pool_holder());
-
- resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK);
-}
-
-
void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle resolved_klass,
Symbol* method_name, Symbol* method_signature,
KlassHandle current_klass, bool check_access, TRAPS) {
@@ -346,6 +407,8 @@
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
}
+ Handle nested_exception;
+
// 2. lookup method in resolved klass and its super klasses
lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK);
@@ -354,17 +417,23 @@
lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
if (resolved_method.is_null()) {
- // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...)
- lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, CHECK);
+ // JSR 292: see if this is an implicitly generated method MethodHandle.linkToVirtual(*...), etc
+ lookup_polymorphic_method(resolved_method, resolved_klass, method_name, method_signature,
+ current_klass, (Handle*)NULL, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ nested_exception = Handle(THREAD, PENDING_EXCEPTION);
+ CLEAR_PENDING_EXCEPTION;
+ }
}
if (resolved_method.is_null()) {
// 4. method lookup failed
ResourceMark rm(THREAD);
- THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(),
- methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),
- method_name,
- method_signature));
+ THROW_MSG_CAUSE(vmSymbols::java_lang_NoSuchMethodError(),
+ methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),
+ method_name,
+ method_signature),
+ nested_exception);
}
}
@@ -1053,6 +1122,7 @@
case Bytecodes::_invokestatic : resolve_invokestatic (result, pool, index, CHECK); break;
case Bytecodes::_invokespecial : resolve_invokespecial (result, pool, index, CHECK); break;
case Bytecodes::_invokevirtual : resolve_invokevirtual (result, recv, pool, index, CHECK); break;
+ case Bytecodes::_invokehandle : resolve_invokehandle (result, pool, index, CHECK); break;
case Bytecodes::_invokedynamic : resolve_invokedynamic (result, pool, index, CHECK); break;
case Bytecodes::_invokeinterface: resolve_invokeinterface(result, recv, pool, index, CHECK); break;
}
@@ -1116,22 +1186,91 @@
}
-void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int raw_index, TRAPS) {
+void LinkResolver::resolve_invokehandle(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {
assert(EnableInvokeDynamic, "");
+ // This guy is reached from InterpreterRuntime::resolve_invokehandle.
+ KlassHandle resolved_klass;
+ Symbol* method_name = NULL;
+ Symbol* method_signature = NULL;
+ KlassHandle current_klass;
+ resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK);
+ if (TraceMethodHandles)
+ tty->print_cr("resolve_invokehandle %s %s", method_name->as_C_string(), method_signature->as_C_string());
+ resolve_handle_call(result, resolved_klass, method_name, method_signature, current_klass, CHECK);
+}
- // This guy is reached from InterpreterRuntime::resolve_invokedynamic.
+void LinkResolver::resolve_handle_call(CallInfo& result, KlassHandle resolved_klass,
+ Symbol* method_name, Symbol* method_signature,
+ KlassHandle current_klass,
+ TRAPS) {
+ // JSR 292: this must be an implicitly generated method MethodHandle.invokeExact(*...) or similar
+ assert(resolved_klass() == SystemDictionary::MethodHandle_klass(), "");
+ assert(MethodHandles::is_signature_polymorphic_name(method_name), "");
+ methodHandle resolved_method;
+ Handle resolved_appendix;
+ lookup_polymorphic_method(resolved_method, resolved_klass,
+ method_name, method_signature,
+ current_klass, &resolved_appendix, CHECK);
+ result.set_handle(resolved_method, resolved_appendix, CHECK);
+}
+
+
+void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {
+ assert(EnableInvokeDynamic, "");
+ pool->set_invokedynamic(); // mark header to flag active call sites
+
+ //resolve_pool(<resolved_klass>, method_name, method_signature, current_klass, pool, index, CHECK);
+ Symbol* method_name = pool->name_ref_at(index);
+ Symbol* method_signature = pool->signature_ref_at(index);
+ KlassHandle current_klass = KlassHandle(THREAD, pool->pool_holder());
- // At this point, we only need the signature, and can ignore the name.
- Symbol* method_signature = pool->signature_ref_at(raw_index); // raw_index works directly
- Symbol* method_name = vmSymbols::invokeExact_name();
- KlassHandle resolved_klass = SystemDictionaryHandles::MethodHandle_klass();
+ // Resolve the bootstrap specifier (BSM + optional arguments).
+ Handle bootstrap_specifier;
+ // Check if CallSite has been bound already:
+ ConstantPoolCacheEntry* cpce = pool->cache()->secondary_entry_at(index);
+ if (cpce->is_f1_null()) {
+ int pool_index = pool->cache()->main_entry_at(index)->constant_pool_index();
+ oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK);
+ assert(bsm_info != NULL, "");
+ // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic.
+ bootstrap_specifier = Handle(THREAD, bsm_info);
+ }
+ if (!cpce->is_f1_null()) {
+ methodHandle method(THREAD, cpce->f2_as_vfinal_method());
+ Handle appendix(THREAD, cpce->has_appendix() ? cpce->f1_appendix() : (oop)NULL);
+ result.set_handle(method, appendix, CHECK);
+ return;
+ }
- // JSR 292: this must be an implicitly generated method MethodHandle.invokeExact(*...)
- // The extra MH receiver will be inserted into the stack on every call.
- methodHandle resolved_method;
- KlassHandle current_klass(THREAD, pool->pool_holder());
- lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, THREAD);
+ if (TraceMethodHandles) {
+ tty->print_cr("resolve_invokedynamic #%d %s %s",
+ constantPoolCacheOopDesc::decode_secondary_index(index),
+ method_name->as_C_string(), method_signature->as_C_string());
+ tty->print(" BSM info: "); bootstrap_specifier->print();
+ }
+
+ resolve_dynamic_call(result, bootstrap_specifier, method_name, method_signature, current_klass, CHECK);
+}
+
+void LinkResolver::resolve_dynamic_call(CallInfo& result,
+ Handle bootstrap_specifier,
+ Symbol* method_name, Symbol* method_signature,
+ KlassHandle current_klass,
+ TRAPS) {
+ // JSR 292: this must resolve to an implicitly generated method MH.linkToCallSite(*...)
+ // The appendix argument is likely to be a freshly-created CallSite.
+ Handle resolved_appendix;
+ methodHandle resolved_method =
+ SystemDictionary::find_dynamic_call_site_invoker(current_klass,
+ bootstrap_specifier,
+ method_name, method_signature,
+ &resolved_appendix,
+ THREAD);
if (HAS_PENDING_EXCEPTION) {
+ if (TraceMethodHandles) {
+ tty->print_cr("invokedynamic throws BSME for "INTPTR_FORMAT, PENDING_EXCEPTION);
+ PENDING_EXCEPTION->print();
+ }
if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) {
// throw these guys, since they are already wrapped
return;
@@ -1141,17 +1280,11 @@
return;
}
// See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS.
- Handle ex(THREAD, PENDING_EXCEPTION);
+ Handle nested_exception(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
- oop bsme = Klass::cast(SystemDictionary::BootstrapMethodError_klass())->java_mirror();
- MethodHandles::raise_exception(Bytecodes::_athrow, ex(), bsme, CHECK);
- // java code should not return, but if it does throw out anyway
- THROW(vmSymbols::java_lang_InternalError());
+ THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
}
- if (resolved_method.is_null()) {
- THROW(vmSymbols::java_lang_InternalError());
- }
- result.set_dynamic(resolved_method, CHECK);
+ result.set_handle(resolved_method, resolved_appendix, CHECK);
}
//------------------------------------------------------------------------------------------------------------------------
--- a/hotspot/src/share/vm/interpreter/linkResolver.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/linkResolver.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -75,11 +75,12 @@
methodHandle _resolved_method; // static target method
methodHandle _selected_method; // dynamic (actual) target method
int _vtable_index; // vtable index of selected method
+ Handle _resolved_appendix; // extra argument in constant pool (if CPCE::has_appendix)
void set_static( KlassHandle resolved_klass, methodHandle resolved_method , TRAPS);
void set_interface(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method , TRAPS);
void set_virtual( KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS);
- void set_dynamic( methodHandle resolved_method, TRAPS);
+ void set_handle( methodHandle resolved_method, Handle resolved_appendix, TRAPS);
void set_common( KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS);
friend class LinkResolver;
@@ -89,6 +90,7 @@
KlassHandle selected_klass() const { return _selected_klass; }
methodHandle resolved_method() const { return _resolved_method; }
methodHandle selected_method() const { return _selected_method; }
+ Handle resolved_appendix() const { return _resolved_appendix; }
BasicType result_type() const { return selected_method()->result_type(); }
bool has_vtable_index() const { return _vtable_index >= 0; }
@@ -110,8 +112,8 @@
static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
- static void lookup_implicit_method (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature,
- KlassHandle current_klass, TRAPS);
+ static void lookup_polymorphic_method (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature,
+ KlassHandle current_klass, Handle* appendix_result_or_null, TRAPS);
static int vtable_index_of_miranda_method(KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
@@ -139,10 +141,9 @@
// constant pool resolving
static void check_klass_accessability(KlassHandle ref_klass, KlassHandle sel_klass, TRAPS);
- // static resolving for all calls except interface calls
- static void resolve_method (methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS);
- static void resolve_dynamic_method (methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS);
- static void resolve_interface_method(methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS);
+ // static resolving calls (will not run any Java code); used only from Bytecode_invoke::static_target
+ static void resolve_method_statically(methodHandle& method_result, KlassHandle& klass_result,
+ Bytecodes::Code code, constantPoolHandle pool, int index, TRAPS);
// runtime/static resolving for fields
static void resolve_field(FieldAccessInfo& result, constantPoolHandle pool, int index, Bytecodes::Code byte, bool check_only, TRAPS);
@@ -156,6 +157,8 @@
static void resolve_special_call (CallInfo& result, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS);
static void resolve_virtual_call (CallInfo& result, Handle recv, KlassHandle recv_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, bool check_null_and_abstract, TRAPS);
static void resolve_interface_call(CallInfo& result, Handle recv, KlassHandle recv_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, bool check_null_and_abstract, TRAPS);
+ static void resolve_handle_call (CallInfo& result, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, TRAPS);
+ static void resolve_dynamic_call (CallInfo& result, Handle bootstrap_specifier, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, TRAPS);
// same as above for compile-time resolution; but returns null handle instead of throwing an exception on error
// also, does not initialize klass (i.e., no side effects)
@@ -177,6 +180,7 @@
static void resolve_invokevirtual (CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS);
static void resolve_invokeinterface(CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS);
static void resolve_invokedynamic (CallInfo& result, constantPoolHandle pool, int index, TRAPS);
+ static void resolve_invokehandle (CallInfo& result, constantPoolHandle pool, int index, TRAPS);
static void resolve_invoke (CallInfo& result, Handle recv, constantPoolHandle pool, int index, Bytecodes::Code byte, TRAPS);
};
--- a/hotspot/src/share/vm/interpreter/rewriter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/rewriter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -33,6 +33,7 @@
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "prims/methodComparator.hpp"
+#include "prims/methodHandles.hpp"
// Computes a CPC map (new_index -> original_index) for constant pool entries
// that are referred to by the interpreter at runtime via the constant pool cache.
@@ -41,10 +42,9 @@
void Rewriter::compute_index_maps() {
const int length = _pool->length();
init_cp_map(length);
- jint tag_mask = 0;
+ bool saw_mh_symbol = false;
for (int i = 0; i < length; i++) {
int tag = _pool->tag_at(i).value();
- tag_mask |= (1 << tag);
switch (tag) {
case JVM_CONSTANT_InterfaceMethodref:
case JVM_CONSTANT_Fieldref : // fall through
@@ -54,13 +54,18 @@
case JVM_CONSTANT_InvokeDynamic : // fall through
add_cp_cache_entry(i);
break;
+ case JVM_CONSTANT_Utf8:
+ if (_pool->symbol_at(i) == vmSymbols::java_lang_invoke_MethodHandle())
+ saw_mh_symbol = true;
+ break;
}
}
guarantee((int)_cp_cache_map.length()-1 <= (int)((u2)-1),
"all cp cache indexes fit in a u2");
- _have_invoke_dynamic = ((tag_mask & (1 << JVM_CONSTANT_InvokeDynamic)) != 0);
+ if (saw_mh_symbol)
+ _method_handle_invokers.initialize(length, (int)0);
}
// Unrewrite the bytecodes if an error occurs.
@@ -80,22 +85,6 @@
oopFactory::new_constantPoolCache(length, CHECK);
No_Safepoint_Verifier nsv;
cache->initialize(_cp_cache_map);
-
- // Don't bother with the next pass if there is no JVM_CONSTANT_InvokeDynamic.
- if (_have_invoke_dynamic) {
- for (int i = 0; i < length; i++) {
- int pool_index = cp_cache_entry_pool_index(i);
- if (pool_index >= 0 &&
- _pool->tag_at(pool_index).is_invoke_dynamic()) {
- int bsm_index = _pool->invoke_dynamic_bootstrap_method_ref_index_at(pool_index);
- assert(_pool->tag_at(bsm_index).is_method_handle(), "must be a MH constant");
- // There is a CP cache entry holding the BSM for these calls.
- int bsm_cache_index = cp_entry_to_cp_cache(bsm_index);
- cache->entry_at(i)->initialize_bootstrap_method_index_in_cache(bsm_cache_index);
- }
- }
- }
-
_pool->set_cache(cache);
cache->set_constant_pool(_pool());
}
@@ -148,10 +137,53 @@
int cp_index = Bytes::get_Java_u2(p);
int cache_index = cp_entry_to_cp_cache(cp_index);
Bytes::put_native_u2(p, cache_index);
+ if (!_method_handle_invokers.is_empty())
+ maybe_rewrite_invokehandle(p - 1, cp_index, reverse);
} else {
int cache_index = Bytes::get_native_u2(p);
int pool_index = cp_cache_entry_pool_index(cache_index);
Bytes::put_Java_u2(p, pool_index);
+ if (!_method_handle_invokers.is_empty())
+ maybe_rewrite_invokehandle(p - 1, pool_index, reverse);
+ }
+}
+
+
+// Adjust the invocation bytecode for a signature-polymorphic method (MethodHandle.invoke, etc.)
+void Rewriter::maybe_rewrite_invokehandle(address opc, int cp_index, bool reverse) {
+ if (!reverse) {
+ if ((*opc) == (u1)Bytecodes::_invokevirtual ||
+ // allow invokespecial as an alias, although it would be very odd:
+ (*opc) == (u1)Bytecodes::_invokespecial) {
+ assert(_pool->tag_at(cp_index).is_method(), "wrong index");
+ // Determine whether this is a signature-polymorphic method.
+ if (cp_index >= _method_handle_invokers.length()) return;
+ int status = _method_handle_invokers[cp_index];
+ assert(status >= -1 && status <= 1, "oob tri-state");
+ if (status == 0) {
+ if (_pool->klass_ref_at_noresolve(cp_index) == vmSymbols::java_lang_invoke_MethodHandle() &&
+ MethodHandles::is_signature_polymorphic_name(SystemDictionary::MethodHandle_klass(),
+ _pool->name_ref_at(cp_index)))
+ status = +1;
+ else
+ status = -1;
+ _method_handle_invokers[cp_index] = status;
+ }
+ // We use a special internal bytecode for such methods (if non-static).
+ // The basic reason for this is that such methods need an extra "appendix" argument
+ // to transmit the call site's intended call type.
+ if (status > 0) {
+ (*opc) = (u1)Bytecodes::_invokehandle;
+ }
+ }
+ } else {
+ // Do not need to look at cp_index.
+ if ((*opc) == (u1)Bytecodes::_invokehandle) {
+ (*opc) = (u1)Bytecodes::_invokevirtual;
+ // Ignore corner case of original _invokespecial instruction.
+ // This is safe because (a) the signature polymorphic method was final, and
+ // (b) the implementation of MethodHandle will not call invokespecial on it.
+ }
}
}
@@ -297,17 +329,18 @@
case Bytecodes::_invokespecial : // fall through
case Bytecodes::_invokestatic :
case Bytecodes::_invokeinterface:
+ case Bytecodes::_invokehandle : // if reverse=true
rewrite_member_reference(bcp, prefix_length+1, reverse);
break;
case Bytecodes::_invokedynamic:
rewrite_invokedynamic(bcp, prefix_length+1, reverse);
break;
case Bytecodes::_ldc:
- case Bytecodes::_fast_aldc:
+ case Bytecodes::_fast_aldc: // if reverse=true
maybe_rewrite_ldc(bcp, prefix_length+1, false, reverse);
break;
case Bytecodes::_ldc_w:
- case Bytecodes::_fast_aldc_w:
+ case Bytecodes::_fast_aldc_w: // if reverse=true
maybe_rewrite_ldc(bcp, prefix_length+1, true, reverse);
break;
case Bytecodes::_jsr : // fall through
--- a/hotspot/src/share/vm/interpreter/rewriter.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/rewriter.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -39,7 +39,7 @@
objArrayHandle _methods;
intArray _cp_map;
intStack _cp_cache_map;
- bool _have_invoke_dynamic;
+ intArray _method_handle_invokers;
void init_cp_map(int length) {
_cp_map.initialize(length, -1);
@@ -88,6 +88,7 @@
void scan_method(methodOop m, bool reverse = false);
void rewrite_Object_init(methodHandle m, TRAPS);
void rewrite_member_reference(address bcp, int offset, bool reverse = false);
+ void maybe_rewrite_invokehandle(address opc, int cp_index, bool reverse = false);
void rewrite_invokedynamic(address bcp, int offset, bool reverse = false);
void maybe_rewrite_ldc(address bcp, int offset, bool is_wide, bool reverse = false);
// Revert bytecodes in case of an exception.
--- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -362,7 +362,6 @@
method_entry(empty)
method_entry(accessor)
method_entry(abstract)
- method_entry(method_handle)
method_entry(java_lang_math_sin )
method_entry(java_lang_math_cos )
method_entry(java_lang_math_tan )
@@ -374,6 +373,12 @@
method_entry(java_lang_math_pow )
method_entry(java_lang_ref_reference_get)
+ // method handle entry kinds are generated later in MethodHandlesAdapterGenerator::generate:
+ for (int i = Interpreter::method_handle_invoke_FIRST; i <= Interpreter::method_handle_invoke_LAST; i++) {
+ Interpreter::MethodKind kind = (Interpreter::MethodKind) i;
+ Interpreter::_entry_table[kind] = Interpreter::_entry_table[Interpreter::abstract];
+ }
+
// all native method kinds (must be one contiguous block)
Interpreter::_native_entry_begin = Interpreter::code()->code_end();
method_entry(native)
--- a/hotspot/src/share/vm/interpreter/templateTable.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/templateTable.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -444,7 +444,7 @@
def(Bytecodes::_invokespecial , ubcp|disp|clvm|____, vtos, vtos, invokespecial , f1_byte );
def(Bytecodes::_invokestatic , ubcp|disp|clvm|____, vtos, vtos, invokestatic , f1_byte );
def(Bytecodes::_invokeinterface , ubcp|disp|clvm|____, vtos, vtos, invokeinterface , f1_byte );
- def(Bytecodes::_invokedynamic , ubcp|disp|clvm|____, vtos, vtos, invokedynamic , f1_oop );
+ def(Bytecodes::_invokedynamic , ubcp|disp|clvm|____, vtos, vtos, invokedynamic , f12_oop );
def(Bytecodes::_new , ubcp|____|clvm|____, vtos, atos, _new , _ );
def(Bytecodes::_newarray , ubcp|____|clvm|____, itos, atos, newarray , _ );
def(Bytecodes::_anewarray , ubcp|____|clvm|____, itos, atos, anewarray , _ );
@@ -514,6 +514,8 @@
def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return , vtos );
+ def(Bytecodes::_invokehandle , ubcp|disp|clvm|____, vtos, vtos, invokehandle , f12_oop );
+
def(Bytecodes::_shouldnotreachhere , ____|____|____|____, vtos, vtos, shouldnotreachhere , _ );
// platform specific bytecodes
pd_initialize();
--- a/hotspot/src/share/vm/interpreter/templateTable.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/interpreter/templateTable.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -98,7 +98,7 @@
public:
enum Operation { add, sub, mul, div, rem, _and, _or, _xor, shl, shr, ushr };
enum Condition { equal, not_equal, less, less_equal, greater, greater_equal };
- enum CacheByte { f1_byte = 1, f2_byte = 2, f1_oop = 0x11 }; // byte_no codes
+ enum CacheByte { f1_byte = 1, f2_byte = 2, f12_oop = 0x12 }; // byte_no codes
private:
static bool _is_initialized; // true if TemplateTable has been initialized
@@ -294,6 +294,7 @@
static void invokestatic(int byte_no);
static void invokeinterface(int byte_no);
static void invokedynamic(int byte_no);
+ static void invokehandle(int byte_no);
static void fast_invokevfinal(int byte_no);
static void getfield_or_static(int byte_no, bool is_static);
--- a/hotspot/src/share/vm/oops/constantPoolOop.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/constantPoolOop.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -267,25 +267,61 @@
methodOop constantPoolOopDesc::method_at_if_loaded(constantPoolHandle cpool,
- int which, Bytecodes::Code invoke_code) {
+ int which) {
assert(!constantPoolCacheOopDesc::is_secondary_index(which), "no indy instruction here");
if (cpool->cache() == NULL) return NULL; // nothing to load yet
- int cache_index = which - CPCACHE_INDEX_TAG;
+ int cache_index = get_cpcache_index(which);
if (!(cache_index >= 0 && cache_index < cpool->cache()->length())) {
if (PrintMiscellaneous && (Verbose||WizardMode)) {
- tty->print_cr("bad operand %d for %d in:", which, invoke_code); cpool->print();
+ tty->print_cr("bad operand %d in:", which); cpool->print();
}
return NULL;
}
ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index);
- if (invoke_code != Bytecodes::_illegal)
- return e->get_method_if_resolved(invoke_code, cpool);
- Bytecodes::Code bc;
- if ((bc = e->bytecode_1()) != (Bytecodes::Code)0)
- return e->get_method_if_resolved(bc, cpool);
- if ((bc = e->bytecode_2()) != (Bytecodes::Code)0)
- return e->get_method_if_resolved(bc, cpool);
- return NULL;
+ return e->method_if_resolved(cpool);
+}
+
+
+bool constantPoolOopDesc::has_appendix_at_if_loaded(constantPoolHandle cpool, int which) {
+ if (cpool->cache() == NULL) return false; // nothing to load yet
+ // XXX Is there a simpler way to get to the secondary entry?
+ ConstantPoolCacheEntry* e;
+ if (constantPoolCacheOopDesc::is_secondary_index(which)) {
+ e = cpool->cache()->secondary_entry_at(which);
+ } else {
+ int cache_index = get_cpcache_index(which);
+ if (!(cache_index >= 0 && cache_index < cpool->cache()->length())) {
+ if (PrintMiscellaneous && (Verbose||WizardMode)) {
+ tty->print_cr("bad operand %d in:", which); cpool->print();
+ }
+ return false;
+ }
+ e = cpool->cache()->entry_at(cache_index);
+ }
+ return e->has_appendix();
+}
+
+
+oop constantPoolOopDesc::appendix_at_if_loaded(constantPoolHandle cpool, int which) {
+ if (cpool->cache() == NULL) return NULL; // nothing to load yet
+ // XXX Is there a simpler way to get to the secondary entry?
+ ConstantPoolCacheEntry* e;
+ if (constantPoolCacheOopDesc::is_secondary_index(which)) {
+ e = cpool->cache()->secondary_entry_at(which);
+ } else {
+ int cache_index = get_cpcache_index(which);
+ if (!(cache_index >= 0 && cache_index < cpool->cache()->length())) {
+ if (PrintMiscellaneous && (Verbose||WizardMode)) {
+ tty->print_cr("bad operand %d in:", which); cpool->print();
+ }
+ return NULL;
+ }
+ e = cpool->cache()->entry_at(cache_index);
+ }
+ if (!e->has_appendix()) {
+ return NULL;
+ }
+ return e->f1_as_instance();
}
@@ -481,7 +517,7 @@
if (cache_index >= 0) {
assert(index == _no_index_sentinel, "only one kind of index at a time");
ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index);
- result_oop = cpc_entry->f1();
+ result_oop = cpc_entry->f1_as_instance();
if (result_oop != NULL) {
return decode_exception_from_f1(result_oop, THREAD);
// That was easy...
@@ -553,12 +589,7 @@
index, this_oop->method_type_index_at(index),
signature->as_C_string());
KlassHandle klass(THREAD, this_oop->pool_holder());
- bool ignore_is_on_bcp = false;
- Handle value = SystemDictionary::find_method_handle_type(signature,
- klass,
- false,
- ignore_is_on_bcp,
- THREAD);
+ Handle value = SystemDictionary::find_method_handle_type(signature, klass, THREAD);
if (HAS_PENDING_EXCEPTION) {
throw_exception = Handle(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
@@ -608,7 +639,7 @@
result_oop = NULL; // safety
ObjectLocker ol(this_oop, THREAD);
ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index);
- result_oop = cpc_entry->f1();
+ result_oop = cpc_entry->f1_as_instance();
// Benign race condition: f1 may already be filled in while we were trying to lock.
// The important thing here is that all threads pick up the same result.
// It doesn't matter which racing thread wins, as long as only one
@@ -627,6 +658,45 @@
}
}
+
+oop constantPoolOopDesc::resolve_bootstrap_specifier_at_impl(constantPoolHandle this_oop, int index, TRAPS) {
+ assert(this_oop->tag_at(index).is_invoke_dynamic(), "Corrupted constant pool");
+
+ Handle bsm;
+ int argc;
+ {
+ // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type], plus optional arguments
+ // The bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
+ // It is accompanied by the optional arguments.
+ int bsm_index = this_oop->invoke_dynamic_bootstrap_method_ref_index_at(index);
+ oop bsm_oop = this_oop->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL);
+ if (!java_lang_invoke_MethodHandle::is_instance(bsm_oop)) {
+ THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "BSM not an MethodHandle");
+ }
+
+ // Extract the optional static arguments.
+ argc = this_oop->invoke_dynamic_argument_count_at(index);
+ if (argc == 0) return bsm_oop;
+
+ bsm = Handle(THREAD, bsm_oop);
+ }
+
+ objArrayHandle info;
+ {
+ objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1+argc, CHECK_NULL);
+ info = objArrayHandle(THREAD, info_oop);
+ }
+
+ info->obj_at_put(0, bsm());
+ for (int i = 0; i < argc; i++) {
+ int arg_index = this_oop->invoke_dynamic_argument_index_at(index, i);
+ oop arg_oop = this_oop->resolve_possibly_cached_constant_at(arg_index, CHECK_NULL);
+ info->obj_at_put(1+i, arg_oop);
+ }
+
+ return info();
+}
+
oop constantPoolOopDesc::string_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
oop str = NULL;
CPSlot entry = this_oop->slot_at(which);
--- a/hotspot/src/share/vm/oops/constantPoolOop.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/constantPoolOop.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -642,6 +642,11 @@
return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, THREAD);
}
+ oop resolve_bootstrap_specifier_at(int index, TRAPS) {
+ constantPoolHandle h_this(THREAD, this);
+ return resolve_bootstrap_specifier_at_impl(h_this, index, THREAD);
+ }
+
// Klass name matches name at offset
bool klass_name_at_matches(instanceKlassHandle k, int which);
@@ -666,12 +671,13 @@
friend class SystemDictionary;
// Used by compiler to prevent classloading.
- static methodOop method_at_if_loaded (constantPoolHandle this_oop, int which,
- Bytecodes::Code bc = Bytecodes::_illegal);
- static klassOop klass_at_if_loaded (constantPoolHandle this_oop, int which);
- static klassOop klass_ref_at_if_loaded (constantPoolHandle this_oop, int which);
+ static methodOop method_at_if_loaded (constantPoolHandle this_oop, int which);
+ static bool has_appendix_at_if_loaded (constantPoolHandle this_oop, int which);
+ static oop appendix_at_if_loaded (constantPoolHandle this_oop, int which);
+ static klassOop klass_at_if_loaded (constantPoolHandle this_oop, int which);
+ static klassOop klass_ref_at_if_loaded (constantPoolHandle this_oop, int which);
// Same as above - but does LinkResolving.
- static klassOop klass_ref_at_if_loaded_check(constantPoolHandle this_oop, int which, TRAPS);
+ static klassOop klass_ref_at_if_loaded_check(constantPoolHandle this_oop, int which, TRAPS);
// Routines currently used for annotations (only called by jvm.cpp) but which might be used in the
// future by other Java code. These take constant pool indices rather than
@@ -697,6 +703,8 @@
enum { CPCACHE_INDEX_TAG = 0 }; // in product mode, this zero value is a no-op
#endif //ASSERT
+ static int get_cpcache_index(int index) { return index - CPCACHE_INDEX_TAG; }
+
private:
Symbol* impl_name_ref_at(int which, bool uncached);
@@ -729,6 +737,7 @@
static void resolve_string_constants_impl(constantPoolHandle this_oop, TRAPS);
static oop resolve_constant_at_impl(constantPoolHandle this_oop, int index, int cache_index, TRAPS);
+ static oop resolve_bootstrap_specifier_at_impl(constantPoolHandle this_oop, int index, TRAPS);
public:
// Merging constantPoolOop support:
--- a/hotspot/src/share/vm/oops/cpCacheOop.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/cpCacheOop.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -31,6 +31,7 @@
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiRedefineClassesTrace.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/handles.inline.hpp"
@@ -44,68 +45,61 @@
void ConstantPoolCacheEntry::initialize_secondary_entry(int main_index) {
assert(0 <= main_index && main_index < 0x10000, "sanity check");
- _indices = (main_index << 16);
+ _indices = (main_index << main_cp_index_bits);
assert(main_entry_index() == main_index, "");
}
-int ConstantPoolCacheEntry::as_flags(TosState state, bool is_final,
- bool is_vfinal, bool is_volatile,
- bool is_method_interface, bool is_method) {
- int f = state;
-
- assert( state < number_of_states, "Invalid state in as_flags");
-
- f <<= 1;
- if (is_final) f |= 1;
- f <<= 1;
- if (is_vfinal) f |= 1;
- f <<= 1;
- if (is_volatile) f |= 1;
- f <<= 1;
- if (is_method_interface) f |= 1;
- f <<= 1;
- if (is_method) f |= 1;
- f <<= ConstantPoolCacheEntry::hotSwapBit;
+int ConstantPoolCacheEntry::make_flags(TosState state,
+ int option_bits,
+ int field_index_or_method_params) {
+ assert(state < number_of_states, "Invalid state in make_flags");
+ int f = ((int)state << tos_state_shift) | option_bits | field_index_or_method_params;
// Preserve existing flag bit values
+ // The low bits are a field offset, or else the method parameter size.
#ifdef ASSERT
- int old_state = ((_flags >> tosBits) & 0x0F);
- assert(old_state == 0 || old_state == state,
+ TosState old_state = flag_state();
+ assert(old_state == (TosState)0 || old_state == state,
"inconsistent cpCache flags state");
#endif
return (_flags | f) ;
}
void ConstantPoolCacheEntry::set_bytecode_1(Bytecodes::Code code) {
+ assert(!is_secondary_entry(), "must not overwrite main_entry_index");
#ifdef ASSERT
// Read once.
volatile Bytecodes::Code c = bytecode_1();
assert(c == 0 || c == code || code == 0, "update must be consistent");
#endif
// Need to flush pending stores here before bytecode is written.
- OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << 16));
+ OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << bytecode_1_shift));
}
void ConstantPoolCacheEntry::set_bytecode_2(Bytecodes::Code code) {
+ assert(!is_secondary_entry(), "must not overwrite main_entry_index");
#ifdef ASSERT
// Read once.
volatile Bytecodes::Code c = bytecode_2();
assert(c == 0 || c == code || code == 0, "update must be consistent");
#endif
// Need to flush pending stores here before bytecode is written.
- OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << 24));
+ OrderAccess::release_store_ptr(&_indices, _indices | ((u_char)code << bytecode_2_shift));
}
-// Atomically sets f1 if it is still NULL, otherwise it keeps the
-// current value.
-void ConstantPoolCacheEntry::set_f1_if_null_atomic(oop f1) {
+// Sets f1, ordering with previous writes.
+void ConstantPoolCacheEntry::release_set_f1(oop f1) {
// Use barriers as in oop_store
+ assert(f1 != NULL, "");
oop* f1_addr = (oop*) &_f1;
update_barrier_set_pre(f1_addr, f1);
- void* result = Atomic::cmpxchg_ptr(f1, f1_addr, NULL);
- bool success = (result == NULL);
- if (success) {
- update_barrier_set((void*) f1_addr, f1);
- }
+ OrderAccess::release_store_ptr((intptr_t*)f1_addr, f1);
+ update_barrier_set((void*) f1_addr, f1);
+}
+
+// Sets flags, but only if the value was previously zero.
+bool ConstantPoolCacheEntry::init_flags_atomic(intptr_t flags) {
+ intptr_t result = Atomic::cmpxchg_ptr(flags, &_flags, 0);
+ return (result == 0);
}
#ifdef ASSERT
@@ -135,17 +129,32 @@
bool is_volatile) {
set_f1(field_holder()->java_mirror());
set_f2(field_offset);
- assert(field_index <= field_index_mask,
+ assert((field_index & field_index_mask) == field_index,
"field index does not fit in low flag bits");
- set_flags(as_flags(field_type, is_final, false, is_volatile, false, false) |
- (field_index & field_index_mask));
+ set_field_flags(field_type,
+ ((is_volatile ? 1 : 0) << is_volatile_shift) |
+ ((is_final ? 1 : 0) << is_final_shift),
+ field_index);
set_bytecode_1(get_code);
set_bytecode_2(put_code);
NOT_PRODUCT(verify(tty));
}
-int ConstantPoolCacheEntry::field_index() const {
- return (_flags & field_index_mask);
+void ConstantPoolCacheEntry::set_parameter_size(int value) {
+ // This routine is called only in corner cases where the CPCE is not yet initialized.
+ // See AbstractInterpreter::deopt_continue_after_entry.
+ assert(_flags == 0 || parameter_size() == 0 || parameter_size() == value,
+ err_msg("size must not change: parameter_size=%d, value=%d", parameter_size(), value));
+ // Setting the parameter size by itself is only safe if the
+ // current value of _flags is 0, otherwise another thread may have
+ // updated it and we don't want to overwrite that value. Don't
+ // bother trying to update it once it's nonzero but always make
+ // sure that the final parameter size agrees with what was passed.
+ if (_flags == 0) {
+ Atomic::cmpxchg_ptr((value & parameter_size_mask), &_flags, 0);
+ }
+ guarantee(parameter_size() == value,
+ err_msg("size must not change: parameter_size=%d, value=%d", parameter_size(), value));
}
void ConstantPoolCacheEntry::set_method(Bytecodes::Code invoke_code,
@@ -154,51 +163,51 @@
assert(!is_secondary_entry(), "");
assert(method->interpreter_entry() != NULL, "should have been set at this point");
assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache");
- bool change_to_virtual = (invoke_code == Bytecodes::_invokeinterface);
int byte_no = -1;
- bool needs_vfinal_flag = false;
+ bool change_to_virtual = false;
+
switch (invoke_code) {
+ case Bytecodes::_invokeinterface:
+ // We get here from InterpreterRuntime::resolve_invoke when an invokeinterface
+ // instruction somehow links to a non-interface method (in Object).
+ // In that case, the method has no itable index and must be invoked as a virtual.
+ // Set a flag to keep track of this corner case.
+ change_to_virtual = true;
+
+ // ...and fall through as if we were handling invokevirtual:
case Bytecodes::_invokevirtual:
- case Bytecodes::_invokeinterface: {
+ {
if (method->can_be_statically_bound()) {
- set_f2((intptr_t)method());
- needs_vfinal_flag = true;
+ // set_f2_as_vfinal_method checks if is_vfinal flag is true.
+ set_method_flags(as_TosState(method->result_type()),
+ ( 1 << is_vfinal_shift) |
+ ((method->is_final_method() ? 1 : 0) << is_final_shift) |
+ ((change_to_virtual ? 1 : 0) << is_forced_virtual_shift),
+ method()->size_of_parameters());
+ set_f2_as_vfinal_method(method());
} else {
assert(vtable_index >= 0, "valid index");
+ assert(!method->is_final_method(), "sanity");
+ set_method_flags(as_TosState(method->result_type()),
+ ((change_to_virtual ? 1 : 0) << is_forced_virtual_shift),
+ method()->size_of_parameters());
set_f2(vtable_index);
}
byte_no = 2;
break;
- }
-
- case Bytecodes::_invokedynamic: // similar to _invokevirtual
- if (TraceInvokeDynamic) {
- tty->print_cr("InvokeDynamic set_method%s method="PTR_FORMAT" index=%d",
- (is_secondary_entry() ? " secondary" : ""),
- (intptr_t)method(), vtable_index);
- method->print();
- this->print(tty, 0);
}
- assert(method->can_be_statically_bound(), "must be a MH invoker method");
- assert(_f2 >= constantPoolOopDesc::CPCACHE_INDEX_TAG, "BSM index initialized");
- // SystemDictionary::find_method_handle_invoke only caches
- // methods which signature classes are on the boot classpath,
- // otherwise the newly created method is returned. To avoid
- // races in that case we store the first one coming in into the
- // cp-cache atomically if it's still unset.
- set_f1_if_null_atomic(method());
- needs_vfinal_flag = false; // _f2 is not an oop
- assert(!is_vfinal(), "f2 not an oop");
- byte_no = 1; // coordinate this with bytecode_number & is_resolved
- break;
case Bytecodes::_invokespecial:
- // Preserve the value of the vfinal flag on invokevirtual bytecode
- // which may be shared with this constant pool cache entry.
- needs_vfinal_flag = is_resolved(Bytecodes::_invokevirtual) && is_vfinal();
- // fall through
case Bytecodes::_invokestatic:
+ // Note: Read and preserve the value of the is_vfinal flag on any
+ // invokevirtual bytecode shared with this constant pool cache entry.
+ // It is cheap and safe to consult is_vfinal() at all times.
+ // Once is_vfinal is set, it must stay that way, lest we get a dangling oop.
+ set_method_flags(as_TosState(method->result_type()),
+ ((is_vfinal() ? 1 : 0) << is_vfinal_shift) |
+ ((method->is_final_method() ? 1 : 0) << is_final_shift),
+ method()->size_of_parameters());
set_f1(method());
byte_no = 1;
break;
@@ -207,19 +216,14 @@
break;
}
- set_flags(as_flags(as_TosState(method->result_type()),
- method->is_final_method(),
- needs_vfinal_flag,
- false,
- change_to_virtual,
- true)|
- method()->size_of_parameters());
-
// Note: byte_no also appears in TemplateTable::resolve.
if (byte_no == 1) {
+ assert(invoke_code != Bytecodes::_invokevirtual &&
+ invoke_code != Bytecodes::_invokeinterface, "");
set_bytecode_1(invoke_code);
} else if (byte_no == 2) {
if (change_to_virtual) {
+ assert(invoke_code == Bytecodes::_invokeinterface, "");
// NOTE: THIS IS A HACK - BE VERY CAREFUL!!!
//
// Workaround for the case where we encounter an invokeinterface, but we
@@ -235,10 +239,11 @@
// Otherwise, the method needs to be reresolved with caller for each
// interface call.
if (method->is_public()) set_bytecode_1(invoke_code);
- set_bytecode_2(Bytecodes::_invokevirtual);
} else {
- set_bytecode_2(invoke_code);
+ assert(invoke_code == Bytecodes::_invokevirtual, "");
}
+ // set up for invokevirtual, even if linking for invokeinterface also:
+ set_bytecode_2(Bytecodes::_invokevirtual);
} else {
ShouldNotReachHere();
}
@@ -250,73 +255,129 @@
assert(!is_secondary_entry(), "");
klassOop interf = method->method_holder();
assert(instanceKlass::cast(interf)->is_interface(), "must be an interface");
+ assert(!method->is_final_method(), "interfaces do not have final methods; cannot link to one here");
set_f1(interf);
set_f2(index);
- set_flags(as_flags(as_TosState(method->result_type()), method->is_final_method(), false, false, false, true) | method()->size_of_parameters());
+ set_method_flags(as_TosState(method->result_type()),
+ 0, // no option bits
+ method()->size_of_parameters());
set_bytecode_1(Bytecodes::_invokeinterface);
}
-void ConstantPoolCacheEntry::initialize_bootstrap_method_index_in_cache(int bsm_cache_index) {
- assert(!is_secondary_entry(), "only for JVM_CONSTANT_InvokeDynamic main entry");
- assert(_f2 == 0, "initialize once");
- assert(bsm_cache_index == (int)(u2)bsm_cache_index, "oob");
- set_f2(bsm_cache_index + constantPoolOopDesc::CPCACHE_INDEX_TAG);
+void ConstantPoolCacheEntry::set_method_handle(methodHandle adapter, Handle appendix) {
+ assert(!is_secondary_entry(), "");
+ set_method_handle_common(Bytecodes::_invokehandle, adapter, appendix);
}
-int ConstantPoolCacheEntry::bootstrap_method_index_in_cache() {
- assert(!is_secondary_entry(), "only for JVM_CONSTANT_InvokeDynamic main entry");
- intptr_t bsm_cache_index = (intptr_t) _f2 - constantPoolOopDesc::CPCACHE_INDEX_TAG;
- assert(bsm_cache_index == (intptr_t)(u2)bsm_cache_index, "oob");
- return (int) bsm_cache_index;
+void ConstantPoolCacheEntry::set_dynamic_call(methodHandle adapter, Handle appendix) {
+ assert(is_secondary_entry(), "");
+ set_method_handle_common(Bytecodes::_invokedynamic, adapter, appendix);
}
-void ConstantPoolCacheEntry::set_dynamic_call(Handle call_site, methodHandle signature_invoker) {
- assert(is_secondary_entry(), "");
- // NOTE: it's important that all other values are set before f1 is
- // set since some users short circuit on f1 being set
- // (i.e. non-null) and that may result in uninitialized values for
- // other racing threads (e.g. flags).
- int param_size = signature_invoker->size_of_parameters();
- assert(param_size >= 1, "method argument size must include MH.this");
- param_size -= 1; // do not count MH.this; it is not stacked for invokedynamic
- bool is_final = true;
- assert(signature_invoker->is_final_method(), "is_final");
- int flags = as_flags(as_TosState(signature_invoker->result_type()), is_final, false, false, false, true) | param_size;
- assert(_flags == 0 || _flags == flags, "flags should be the same");
- set_flags(flags);
- // do not do set_bytecode on a secondary CP cache entry
- //set_bytecode_1(Bytecodes::_invokedynamic);
- set_f1_if_null_atomic(call_site()); // This must be the last one to set (see NOTE above)!
+void ConstantPoolCacheEntry::set_method_handle_common(Bytecodes::Code invoke_code, methodHandle adapter, Handle appendix) {
+ // NOTE: This CPCE can be the subject of data races.
+ // There are three words to update: flags, f2, f1 (in that order).
+ // Writers must store all other values before f1.
+ // Readers must test f1 first for non-null before reading other fields.
+ // Competing writers must acquire exclusive access on the first
+ // write, to flags, using a compare/exchange.
+ // A losing writer must spin until the winner writes f1,
+ // so that when he returns, he can use the linked cache entry.
+
+ bool has_appendix = appendix.not_null();
+ if (!has_appendix) {
+ // The extra argument is not used, but we need a non-null value to signify linkage state.
+ // Set it to something benign that will never leak memory.
+ appendix = Universe::void_mirror();
+ }
+
+ bool owner =
+ init_method_flags_atomic(as_TosState(adapter->result_type()),
+ ((has_appendix ? 1 : 0) << has_appendix_shift) |
+ ( 1 << is_vfinal_shift) |
+ ( 1 << is_final_shift),
+ adapter->size_of_parameters());
+ if (!owner) {
+ while (is_f1_null()) {
+ // Pause momentarily on a low-level lock, to allow racing thread to win.
+ MutexLockerEx mu(Patching_lock, Mutex::_no_safepoint_check_flag);
+ os::yield();
+ }
+ return;
+ }
+
+ if (TraceInvokeDynamic) {
+ tty->print_cr("set_method_handle bc=%d appendix="PTR_FORMAT"%s method="PTR_FORMAT" ",
+ invoke_code,
+ (intptr_t)appendix(), (has_appendix ? "" : " (unused)"),
+ (intptr_t)adapter());
+ adapter->print();
+ if (has_appendix) appendix()->print();
+ }
+
+ // Method handle invokes and invokedynamic sites use both cp cache words.
+ // f1, if not null, contains a value passed as a trailing argument to the adapter.
+ // In the general case, this could be the call site's MethodType,
+ // for use with java.lang.Invokers.checkExactType, or else a CallSite object.
+ // f2 contains the adapter method which manages the actual call.
+ // In the general case, this is a compiled LambdaForm.
+ // (The Java code is free to optimize these calls by binding other
+ // sorts of methods and appendices to call sites.)
+ // JVM-level linking is via f2, as if for invokevfinal, and signatures are erased.
+ // The appendix argument (if any) is added to the signature, and is counted in the parameter_size bits.
+ // In principle this means that the method (with appendix) could take up to 256 parameter slots.
+ //
+ // This means that given a call site like (List)mh.invoke("foo"),
+ // the f2 method has signature '(Ljl/Object;Ljl/invoke/MethodType;)Ljl/Object;',
+ // not '(Ljava/lang/String;)Ljava/util/List;'.
+ // The fact that String and List are involved is encoded in the MethodType in f1.
+ // This allows us to create fewer method oops, while keeping type safety.
+ //
+ set_f2_as_vfinal_method(adapter());
+ assert(appendix.not_null(), "needed for linkage state");
+ release_set_f1(appendix()); // This must be the last one to set (see NOTE above)!
+ if (!is_secondary_entry()) {
+ // The interpreter assembly code does not check byte_2,
+ // but it is used by is_resolved, method_if_resolved, etc.
+ set_bytecode_2(invoke_code);
+ }
+ NOT_PRODUCT(verify(tty));
+ if (TraceInvokeDynamic) {
+ this->print(tty, 0);
+ }
}
-
-methodOop ConstantPoolCacheEntry::get_method_if_resolved(Bytecodes::Code invoke_code, constantPoolHandle cpool) {
- assert(invoke_code > (Bytecodes::Code)0, "bad query");
+methodOop ConstantPoolCacheEntry::method_if_resolved(constantPoolHandle cpool) {
if (is_secondary_entry()) {
- return cpool->cache()->entry_at(main_entry_index())->get_method_if_resolved(invoke_code, cpool);
+ if (!is_f1_null())
+ return f2_as_vfinal_method();
+ return NULL;
}
// Decode the action of set_method and set_interface_call
- if (bytecode_1() == invoke_code) {
+ Bytecodes::Code invoke_code = bytecode_1();
+ if (invoke_code != (Bytecodes::Code)0) {
oop f1 = _f1;
if (f1 != NULL) {
switch (invoke_code) {
case Bytecodes::_invokeinterface:
assert(f1->is_klass(), "");
- return klassItable::method_for_itable_index(klassOop(f1), (int) f2());
+ return klassItable::method_for_itable_index(klassOop(f1), f2_as_index());
case Bytecodes::_invokestatic:
case Bytecodes::_invokespecial:
+ assert(!has_appendix(), "");
assert(f1->is_method(), "");
return methodOop(f1);
}
}
}
- if (bytecode_2() == invoke_code) {
+ invoke_code = bytecode_2();
+ if (invoke_code != (Bytecodes::Code)0) {
switch (invoke_code) {
case Bytecodes::_invokevirtual:
if (is_vfinal()) {
// invokevirtual
- methodOop m = methodOop((intptr_t) f2());
+ methodOop m = f2_as_vfinal_method();
assert(m->is_method(), "");
return m;
} else {
@@ -325,16 +386,19 @@
klassOop klass = cpool->resolved_klass_at(holder_index);
if (!Klass::cast(klass)->oop_is_instance())
klass = SystemDictionary::Object_klass();
- return instanceKlass::cast(klass)->method_at_vtable((int) f2());
+ return instanceKlass::cast(klass)->method_at_vtable(f2_as_index());
}
}
+ break;
+ case Bytecodes::_invokehandle:
+ case Bytecodes::_invokedynamic:
+ return f2_as_vfinal_method();
}
}
return NULL;
}
-
class LocalOopClosure: public OopClosure {
private:
void (*_f)(oop*);
@@ -419,9 +483,10 @@
methodOop new_method, bool * trace_name_printed) {
if (is_vfinal()) {
- // virtual and final so f2() contains method ptr instead of vtable index
- if (f2() == (intptr_t)old_method) {
+ // virtual and final so _f2 contains method ptr instead of vtable index
+ if (f2_as_vfinal_method() == old_method) {
// match old_method so need an update
+ // NOTE: can't use set_f2_as_vfinal_method as it asserts on different values
_f2 = (intptr_t)new_method;
if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) {
if (!(*trace_name_printed)) {
@@ -479,16 +544,17 @@
methodOop m = NULL;
if (is_vfinal()) {
// virtual and final so _f2 contains method ptr instead of vtable index
- m = (methodOop)_f2;
- } else if ((oop)_f1 == NULL) {
+ m = f2_as_vfinal_method();
+ } else if (is_f1_null()) {
// NULL _f1 means this is a virtual entry so also not interesting
return false;
} else {
- if (!((oop)_f1)->is_method()) {
+ oop f1 = _f1; // _f1 is volatile
+ if (!f1->is_method()) {
// _f1 can also contain a klassOop for an interface
return false;
}
- m = (methodOop)_f1;
+ m = f1_as_method();
}
assert(m != NULL && m->is_method(), "sanity check");
--- a/hotspot/src/share/vm/oops/cpCacheOop.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/cpCacheOop.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -38,13 +38,14 @@
// bit number |31 0|
// bit length |-8--|-8--|---16----|
// --------------------------------
-// _indices [ b2 | b1 | index ]
-// _f1 [ entry specific ]
-// _f2 [ entry specific ]
-// _flags [t|f|vf|v|m|h|unused|field_index] (for field entries)
-// bit length |4|1|1 |1|1|0|---7--|----16-----]
-// _flags [t|f|vf|v|m|h|unused|eidx|psze] (for method entries)
-// bit length |4|1|1 |1|1|1|---7--|-8--|-8--]
+// _indices [ b2 | b1 | index ] index = constant_pool_index (!= 0, normal entries only)
+// _indices [ index | 00000 ] index = main_entry_index (secondary entries only)
+// _f1 [ entry specific ] method, klass, or oop (MethodType or CallSite)
+// _f2 [ entry specific ] vtable index or vfinal method
+// _flags [tos|0|00|00|00|f|v|f2|unused|field_index] (for field entries)
+// bit length [ 4 |1|1 |1 | 1|1|1| 1|---5--|----16-----]
+// _flags [tos|M|vf|fv|ea|f|0|f2|unused|00000|psize] (for method entries)
+// bit length [ 4 |1|1 |1 | 1|1|1| 1|---5--|--8--|--8--]
// --------------------------------
//
@@ -52,24 +53,23 @@
// index = original constant pool index
// b1 = bytecode 1
// b2 = bytecode 2
-// psze = parameters size (method entries only)
-// eidx = interpreter entry index (method entries only)
+// psize = parameters size (method entries only)
// field_index = index into field information in holder instanceKlass
// The index max is 0xffff (max number of fields in constant pool)
// and is multiplied by (instanceKlass::next_offset) when accessing.
// t = TosState (see below)
// f = field is marked final (see below)
-// vf = virtual, final (method entries only : is_vfinal())
+// f2 = virtual but final (method entries only: is_vfinal())
// v = field is volatile (see below)
// m = invokeinterface used for method in class Object (see below)
// h = RedefineClasses/Hotswap bit (see below)
//
// The flags after TosState have the following interpretation:
-// bit 27: f flag true if field is marked final
-// bit 26: vf flag true if virtual final method
-// bit 25: v flag true if field is volatile (only for fields)
-// bit 24: m flag true if invokeinterface used for method in class Object
-// bit 23: 0 for fields, 1 for methods
+// bit 27: 0 for fields, 1 for methods
+// f flag true if field is marked final
+// v flag true if field is volatile (only for fields)
+// f2 flag true if f2 contains an oop (e.g., virtual final method)
+// fv flag true if invokeinterface used for method in class Object
//
// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 8 with the
// following mapping to the TosState states:
@@ -86,25 +86,26 @@
//
// Entry specific: field entries:
// _indices = get (b1 section) and put (b2 section) bytecodes, original constant pool index
-// _f1 = field holder
-// _f2 = field offset in words
-// _flags = field type information, original field index in field holder
+// _f1 = field holder (as a java.lang.Class, not a klassOop)
+// _f2 = field offset in bytes
+// _flags = field type information, original FieldInfo index in field holder
// (field_index section)
//
// Entry specific: method entries:
// _indices = invoke code for f1 (b1 section), invoke code for f2 (b2 section),
// original constant pool index
-// _f1 = method for all but virtual calls, unused by virtual calls
-// (note: for interface calls, which are essentially virtual,
-// contains klassOop for the corresponding interface.
-// for invokedynamic, f1 contains the CallSite object for the invocation
-// _f2 = method/vtable index for virtual calls only, unused by all other
-// calls. The vf flag indicates this is a method pointer not an
-// index.
-// _flags = field type info (f section),
-// virtual final entry (vf),
-// interpreter entry index (eidx section),
-// parameter size (psze section)
+// _f1 = methodOop for non-virtual calls, unused by virtual calls.
+// for interface calls, which are essentially virtual but need a klass,
+// contains klassOop for the corresponding interface.
+// for invokedynamic, f1 contains a site-specific CallSite object (as an appendix)
+// for invokehandle, f1 contains a site-specific MethodType object (as an appendix)
+// (upcoming metadata changes will move the appendix to a separate array)
+// _f2 = vtable/itable index (or final methodOop) for virtual calls only,
+// unused by non-virtual. The is_vfinal flag indicates this is a
+// method pointer for a final method, not an index.
+// _flags = method type info (t section),
+// virtual final bit (vfinal),
+// parameter size (psize section)
//
// Note: invokevirtual & invokespecial bytecodes can share the same constant
// pool entry and thus the same constant pool cache entry. All invoke
@@ -138,30 +139,61 @@
assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change");
oop_store(&_f1, f1);
}
- void set_f1_if_null_atomic(oop f1);
- void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; }
- int as_flags(TosState state, bool is_final, bool is_vfinal, bool is_volatile,
- bool is_method_interface, bool is_method);
+ void release_set_f1(oop f1);
+ void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; }
+ void set_f2_as_vfinal_method(methodOop f2) { assert(_f2 == 0 || _f2 == (intptr_t) f2, "illegal field change"); assert(is_vfinal(), "flags must be set"); _f2 = (intptr_t) f2; }
+ int make_flags(TosState state, int option_bits, int field_index_or_method_params);
void set_flags(intx flags) { _flags = flags; }
+ bool init_flags_atomic(intx flags);
+ void set_field_flags(TosState field_type, int option_bits, int field_index) {
+ assert((field_index & field_index_mask) == field_index, "field_index in range");
+ set_flags(make_flags(field_type, option_bits | (1 << is_field_entry_shift), field_index));
+ }
+ void set_method_flags(TosState return_type, int option_bits, int method_params) {
+ assert((method_params & parameter_size_mask) == method_params, "method_params in range");
+ set_flags(make_flags(return_type, option_bits, method_params));
+ }
+ bool init_method_flags_atomic(TosState return_type, int option_bits, int method_params) {
+ assert((method_params & parameter_size_mask) == method_params, "method_params in range");
+ return init_flags_atomic(make_flags(return_type, option_bits, method_params));
+ }
public:
- // specific bit values in flag field
- // Note: the interpreter knows this layout!
- enum FlagBitValues {
- hotSwapBit = 23,
- methodInterface = 24,
- volatileField = 25,
- vfinalMethod = 26,
- finalField = 27
+ // specific bit definitions for the flags field:
+ // (Note: the interpreter must use these definitions to access the CP cache.)
+ enum {
+ // high order bits are the TosState corresponding to field type or method return type
+ tos_state_bits = 4,
+ tos_state_mask = right_n_bits(tos_state_bits),
+ tos_state_shift = BitsPerInt - tos_state_bits, // see verify_tos_state_shift below
+ // misc. option bits; can be any bit position in [16..27]
+ is_vfinal_shift = 21,
+ is_volatile_shift = 22,
+ is_final_shift = 23,
+ has_appendix_shift = 24,
+ is_forced_virtual_shift = 25,
+ is_field_entry_shift = 26,
+ // low order bits give field index (for FieldInfo) or method parameter size:
+ field_index_bits = 16,
+ field_index_mask = right_n_bits(field_index_bits),
+ parameter_size_bits = 8, // subset of field_index_mask, range is 0..255
+ parameter_size_mask = right_n_bits(parameter_size_bits),
+ option_bits_mask = ~(((-1) << tos_state_shift) | (field_index_mask | parameter_size_mask))
};
- enum { field_index_mask = 0xFFFF };
+ // specific bit definitions for the indices field:
+ enum {
+ main_cp_index_bits = 2*BitsPerByte,
+ main_cp_index_mask = right_n_bits(main_cp_index_bits),
+ bytecode_1_shift = main_cp_index_bits,
+ bytecode_1_mask = right_n_bits(BitsPerByte), // == (u1)0xFF
+ bytecode_2_shift = main_cp_index_bits + BitsPerByte,
+ bytecode_2_mask = right_n_bits(BitsPerByte), // == (u1)0xFF
+ // the secondary cp index overlaps with bytecodes 1 and 2:
+ secondary_cp_index_shift = bytecode_1_shift,
+ secondary_cp_index_bits = BitsPerInt - main_cp_index_bits
+ };
- // start of type bits in flags
- // Note: the interpreter knows this layout!
- enum FlagValues {
- tosBits = 28
- };
// Initialization
void initialize_entry(int original_index); // initialize primary entry
@@ -189,30 +221,40 @@
int index // Method index into interface
);
+ void set_method_handle(
+ methodHandle method, // adapter for invokeExact, etc.
+ Handle appendix // stored in f1; could be a java.lang.invoke.MethodType
+ );
+
void set_dynamic_call(
- Handle call_site, // Resolved java.lang.invoke.CallSite (f1)
- methodHandle signature_invoker // determines signature information
+ methodHandle method, // adapter for this call site
+ Handle appendix // stored in f1; could be a java.lang.invoke.CallSite
);
- methodOop get_method_if_resolved(Bytecodes::Code invoke_code, constantPoolHandle cpool);
-
- // For JVM_CONSTANT_InvokeDynamic cache entries:
- void initialize_bootstrap_method_index_in_cache(int bsm_cache_index);
- int bootstrap_method_index_in_cache();
+ // Common code for invokedynamic and MH invocations.
- void set_parameter_size(int value) {
- assert(parameter_size() == 0 || parameter_size() == value,
- "size must not change");
- // Setting the parameter size by itself is only safe if the
- // current value of _flags is 0, otherwise another thread may have
- // updated it and we don't want to overwrite that value. Don't
- // bother trying to update it once it's nonzero but always make
- // sure that the final parameter size agrees with what was passed.
- if (_flags == 0) {
- Atomic::cmpxchg_ptr((value & 0xFF), &_flags, 0);
- }
- guarantee(parameter_size() == value, "size must not change");
- }
+ // The "appendix" is an optional call-site-specific parameter which is
+ // pushed by the JVM at the end of the argument list. This argument may
+ // be a MethodType for the MH.invokes and a CallSite for an invokedynamic
+ // instruction. However, its exact type and use depends on the Java upcall,
+ // which simply returns a compiled LambdaForm along with any reference
+ // that LambdaForm needs to complete the call. If the upcall returns a
+ // null appendix, the argument is not passed at all.
+ //
+ // The appendix is *not* represented in the signature of the symbolic
+ // reference for the call site, but (if present) it *is* represented in
+ // the methodOop bound to the site. This means that static and dynamic
+ // resolution logic needs to make slightly different assessments about the
+ // number and types of arguments.
+ void set_method_handle_common(
+ Bytecodes::Code invoke_code, // _invokehandle or _invokedynamic
+ methodHandle adapter, // invoker method (f2)
+ Handle appendix // appendix such as CallSite, MethodType, etc. (f1)
+ );
+
+ methodOop method_if_resolved(constantPoolHandle cpool);
+
+ void set_parameter_size(int value);
// Which bytecode number (1 or 2) in the index field is valid for this bytecode?
// Returns -1 if neither is valid.
@@ -222,10 +264,11 @@
case Bytecodes::_getfield : // fall through
case Bytecodes::_invokespecial : // fall through
case Bytecodes::_invokestatic : // fall through
- case Bytecodes::_invokedynamic : // fall through
case Bytecodes::_invokeinterface : return 1;
case Bytecodes::_putstatic : // fall through
case Bytecodes::_putfield : // fall through
+ case Bytecodes::_invokehandle : // fall through
+ case Bytecodes::_invokedynamic : // fall through
case Bytecodes::_invokevirtual : return 2;
default : break;
}
@@ -242,31 +285,43 @@
}
// Accessors
- bool is_secondary_entry() const { return (_indices & 0xFFFF) == 0; }
- int constant_pool_index() const { assert((_indices & 0xFFFF) != 0, "must be main entry");
- return (_indices & 0xFFFF); }
- int main_entry_index() const { assert((_indices & 0xFFFF) == 0, "must be secondary entry");
- return ((uintx)_indices >> 16); }
- Bytecodes::Code bytecode_1() const { return Bytecodes::cast((_indices >> 16) & 0xFF); }
- Bytecodes::Code bytecode_2() const { return Bytecodes::cast((_indices >> 24) & 0xFF); }
- volatile oop f1() const { return _f1; }
- bool is_f1_null() const { return (oop)_f1 == NULL; } // classifies a CPC entry as unbound
- intx f2() const { return _f2; }
- int field_index() const;
- int parameter_size() const { return _flags & 0xFF; }
- bool is_vfinal() const { return ((_flags & (1 << vfinalMethod)) == (1 << vfinalMethod)); }
- bool is_volatile() const { return ((_flags & (1 << volatileField)) == (1 << volatileField)); }
- bool is_methodInterface() const { return ((_flags & (1 << methodInterface)) == (1 << methodInterface)); }
- bool is_byte() const { return (((uintx) _flags >> tosBits) == btos); }
- bool is_char() const { return (((uintx) _flags >> tosBits) == ctos); }
- bool is_short() const { return (((uintx) _flags >> tosBits) == stos); }
- bool is_int() const { return (((uintx) _flags >> tosBits) == itos); }
- bool is_long() const { return (((uintx) _flags >> tosBits) == ltos); }
- bool is_float() const { return (((uintx) _flags >> tosBits) == ftos); }
- bool is_double() const { return (((uintx) _flags >> tosBits) == dtos); }
- bool is_object() const { return (((uintx) _flags >> tosBits) == atos); }
- TosState flag_state() const { assert( ( (_flags >> tosBits) & 0x0F ) < number_of_states, "Invalid state in as_flags");
- return (TosState)((_flags >> tosBits) & 0x0F); }
+ bool is_secondary_entry() const { return (_indices & main_cp_index_mask) == 0; }
+ int main_entry_index() const { assert(is_secondary_entry(), "must be secondary entry");
+ return ((uintx)_indices >> secondary_cp_index_shift); }
+ int primary_entry_indices() const { assert(!is_secondary_entry(), "must be main entry");
+ return _indices; }
+ int constant_pool_index() const { return (primary_entry_indices() & main_cp_index_mask); }
+ Bytecodes::Code bytecode_1() const { return Bytecodes::cast((primary_entry_indices() >> bytecode_1_shift)
+ & bytecode_1_mask); }
+ Bytecodes::Code bytecode_2() const { return Bytecodes::cast((primary_entry_indices() >> bytecode_2_shift)
+ & bytecode_2_mask); }
+ methodOop f1_as_method() const { oop f1 = _f1; assert(f1 == NULL || f1->is_method(), ""); return methodOop(f1); }
+ klassOop f1_as_klass() const { oop f1 = _f1; assert(f1 == NULL || f1->is_klass(), ""); return klassOop(f1); }
+ oop f1_as_klass_mirror() const { oop f1 = f1_as_instance(); return f1; } // i.e., return a java_mirror
+ oop f1_as_instance() const { oop f1 = _f1; assert(f1 == NULL || f1->is_instance() || f1->is_array(), ""); return f1; }
+ oop f1_appendix() const { assert(has_appendix(), ""); return f1_as_instance(); }
+ bool is_f1_null() const { oop f1 = _f1; return f1 == NULL; } // classifies a CPC entry as unbound
+ int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; }
+ methodOop f2_as_vfinal_method() const { assert(is_vfinal(), ""); return methodOop(_f2); }
+ int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); }
+ int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); }
+ bool is_volatile() const { return (_flags & (1 << is_volatile_shift)) != 0; }
+ bool is_final() const { return (_flags & (1 << is_final_shift)) != 0; }
+ bool has_appendix() const { return (_flags & (1 << has_appendix_shift)) != 0; }
+ bool is_forced_virtual() const { return (_flags & (1 << is_forced_virtual_shift)) != 0; }
+ bool is_vfinal() const { return (_flags & (1 << is_vfinal_shift)) != 0; }
+ bool is_method_entry() const { return (_flags & (1 << is_field_entry_shift)) == 0; }
+ bool is_field_entry() const { return (_flags & (1 << is_field_entry_shift)) != 0; }
+ bool is_byte() const { return flag_state() == btos; }
+ bool is_char() const { return flag_state() == ctos; }
+ bool is_short() const { return flag_state() == stos; }
+ bool is_int() const { return flag_state() == itos; }
+ bool is_long() const { return flag_state() == ltos; }
+ bool is_float() const { return flag_state() == ftos; }
+ bool is_double() const { return flag_state() == dtos; }
+ bool is_object() const { return flag_state() == atos; }
+ TosState flag_state() const { assert((uint)number_of_states <= (uint)tos_state_mask+1, "");
+ return (TosState)((_flags >> tos_state_shift) & tos_state_mask); }
// Code generation support
static WordSize size() { return in_WordSize(sizeof(ConstantPoolCacheEntry) / HeapWordSize); }
@@ -299,15 +354,14 @@
bool adjust_method_entry(methodOop old_method, methodOop new_method,
bool * trace_name_printed);
bool is_interesting_method_entry(klassOop k);
- bool is_field_entry() const { return (_flags & (1 << hotSwapBit)) == 0; }
- bool is_method_entry() const { return (_flags & (1 << hotSwapBit)) != 0; }
// Debugging & Printing
void print (outputStream* st, int index) const;
void verify(outputStream* st) const;
- static void verify_tosBits() {
- assert(tosBits == 28, "interpreter now assumes tosBits is 28");
+ static void verify_tos_state_shift() {
+ // When shifting flags as a 32-bit int, make sure we don't need an extra mask for tos_state:
+ assert((((u4)-1 >> tos_state_shift) & ~tos_state_mask) == 0, "no need for tos_state mask");
}
};
--- a/hotspot/src/share/vm/oops/generateOopMap.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/generateOopMap.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -31,6 +31,7 @@
#include "runtime/java.hpp"
#include "runtime/relocator.hpp"
#include "utilities/bitMap.inline.hpp"
+#include "prims/methodHandles.hpp"
//
//
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -2429,6 +2429,22 @@
} else if (java_lang_boxing_object::is_instance(obj)) {
st->print(" = ");
java_lang_boxing_object::print(obj, st);
+ } else if (as_klassOop() == SystemDictionary::LambdaForm_klass()) {
+ oop vmentry = java_lang_invoke_LambdaForm::vmentry(obj);
+ if (vmentry != NULL) {
+ st->print(" => ");
+ vmentry->print_value_on(st);
+ }
+ } else if (as_klassOop() == SystemDictionary::MemberName_klass()) {
+ oop vmtarget = java_lang_invoke_MemberName::vmtarget(obj);
+ if (vmtarget != NULL) {
+ st->print(" = ");
+ vmtarget->print_value_on(st);
+ } else {
+ java_lang_invoke_MemberName::clazz(obj)->print_value_on(st);
+ st->print(".");
+ java_lang_invoke_MemberName::name(obj)->print_value_on(st);
+ }
}
}
--- a/hotspot/src/share/vm/oops/methodKlass.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/methodKlass.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -238,7 +238,11 @@
st->print_cr(" - highest level: %d", m->highest_comp_level());
st->print_cr(" - vtable index: %d", m->_vtable_index);
st->print_cr(" - i2i entry: " INTPTR_FORMAT, m->interpreter_entry());
- st->print_cr(" - adapter: " INTPTR_FORMAT, m->adapter());
+ st->print( " - adapters: ");
+ if (m->adapter() == NULL)
+ st->print_cr(INTPTR_FORMAT, m->adapter());
+ else
+ m->adapter()->print_adapter_on(st);
st->print_cr(" - compiled entry " INTPTR_FORMAT, m->from_compiled_entry());
st->print_cr(" - code size: %d", m->code_size());
if (m->code_size() != 0) {
@@ -286,13 +290,8 @@
if (m->code() != NULL) {
st->print (" - compiled code: ");
m->code()->print_value_on(st);
- st->cr();
}
- if (m->is_method_handle_invoke()) {
- st->print_cr(" - invoke method type: " INTPTR_FORMAT, (address) m->method_handle_type());
- // m is classified as native, but it does not have an interesting
- // native_function or signature handler
- } else if (m->is_native()) {
+ if (m->is_native()) {
st->print_cr(" - native function: " INTPTR_FORMAT, m->native_function());
st->print_cr(" - signature handler: " INTPTR_FORMAT, m->signature_handler());
}
--- a/hotspot/src/share/vm/oops/methodOop.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/methodOop.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -40,7 +40,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "prims/jvmtiExport.hpp"
-#include "prims/methodHandleWalk.hpp"
+#include "prims/methodHandles.hpp"
#include "prims/nativeLookup.hpp"
#include "runtime/arguments.hpp"
#include "runtime/compilationPolicy.hpp"
@@ -552,6 +552,7 @@
void methodOopDesc::set_native_function(address function, bool post_event_flag) {
assert(function != NULL, "use clear_native_function to unregister natives");
+ assert(!is_method_handle_intrinsic() || function == SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), "");
address* native_function = native_function_addr();
// We can see racers trying to place the same native function into place. Once
@@ -581,12 +582,15 @@
bool methodOopDesc::has_native_function() const {
+ if (is_method_handle_intrinsic())
+ return false; // special-cased in SharedRuntime::generate_native_wrapper
address func = native_function();
return (func != NULL && func != SharedRuntime::native_method_throw_unsatisfied_link_error_entry());
}
void methodOopDesc::clear_native_function() {
+ // Note: is_method_handle_intrinsic() is allowed here.
set_native_function(
SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
!native_bind_event_is_interesting);
@@ -606,12 +610,11 @@
bool methodOopDesc::is_not_compilable(int comp_level) const {
- if (is_method_handle_invoke()) {
- // compilers must recognize this method specially, or not at all
+ if (number_of_breakpoints() > 0) {
return true;
}
- if (number_of_breakpoints() > 0) {
- return true;
+ if (is_method_handle_intrinsic()) {
+ return !is_synthetic(); // the generated adapters must be compiled
}
if (comp_level == CompLevel_any) {
return is_not_c1_compilable() || is_not_c2_compilable();
@@ -709,7 +712,7 @@
assert(entry != NULL, "interpreter entry must be non-null");
// Sets both _i2i_entry and _from_interpreted_entry
set_interpreter_entry(entry);
- if (is_native() && !is_method_handle_invoke()) {
+ if (is_native() && !is_method_handle_intrinsic()) {
set_native_function(
SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
!native_bind_event_is_interesting);
@@ -797,13 +800,13 @@
OrderAccess::storestore();
#ifdef SHARK
mh->_from_interpreted_entry = code->insts_begin();
-#else
+#else //!SHARK
mh->_from_compiled_entry = code->verified_entry_point();
OrderAccess::storestore();
// Instantly compiled code can execute.
- mh->_from_interpreted_entry = mh->get_i2c_entry();
-#endif // SHARK
-
+ if (!mh->is_method_handle_intrinsic())
+ mh->_from_interpreted_entry = mh->get_i2c_entry();
+#endif //!SHARK
}
@@ -855,106 +858,51 @@
return false;
}
-bool methodOopDesc::is_method_handle_invoke_name(vmSymbols::SID name_sid) {
- switch (name_sid) {
- case vmSymbols::VM_SYMBOL_ENUM_NAME(invokeExact_name):
- case vmSymbols::VM_SYMBOL_ENUM_NAME(invoke_name):
- return true;
- }
- if (AllowInvokeGeneric
- && name_sid == vmSymbols::VM_SYMBOL_ENUM_NAME(invokeGeneric_name))
- return true;
- return false;
-}
-
// Constant pool structure for invoke methods:
enum {
- _imcp_invoke_name = 1, // utf8: 'invokeExact' or 'invokeGeneric'
+ _imcp_invoke_name = 1, // utf8: 'invokeExact', etc.
_imcp_invoke_signature, // utf8: (variable Symbol*)
- _imcp_method_type_value, // string: (variable java/lang/invoke/MethodType, sic)
_imcp_limit
};
-oop methodOopDesc::method_handle_type() const {
- if (!is_method_handle_invoke()) { assert(false, "caller resp."); return NULL; }
- oop mt = constants()->resolved_string_at(_imcp_method_type_value);
- assert(mt->klass() == SystemDictionary::MethodType_klass(), "");
- return mt;
-}
-
-jint* methodOopDesc::method_type_offsets_chain() {
- static jint pchase[] = { -1, -1, -1, -1 };
- if (pchase[0] == -1) {
- jint step0 = in_bytes(const_offset());
- jint step1 = in_bytes(constMethodOopDesc::constants_offset());
- jint step2 = (constantPoolOopDesc::header_size() + _imcp_method_type_value) * HeapWordSize;
- // do this in reverse to avoid races:
- OrderAccess::release_store(&pchase[2], step2);
- OrderAccess::release_store(&pchase[1], step1);
- OrderAccess::release_store(&pchase[0], step0);
- }
- return pchase;
+// Test if this method is an MH adapter frame generated by Java code.
+// Cf. java/lang/invoke/InvokerBytecodeGenerator
+bool methodOopDesc::is_compiled_lambda_form() const {
+ return intrinsic_id() == vmIntrinsics::_compiledLambdaForm;
}
-//------------------------------------------------------------------------------
-// methodOopDesc::is_method_handle_adapter
-//
-// Tests if this method is an internal adapter frame from the
-// MethodHandleCompiler.
-// Must be consistent with MethodHandleCompiler::get_method_oop().
-bool methodOopDesc::is_method_handle_adapter() const {
- if (is_synthetic() &&
- !is_native() && // has code from MethodHandleCompiler
- is_method_handle_invoke_name(name()) &&
- MethodHandleCompiler::klass_is_method_handle_adapter_holder(method_holder())) {
- assert(!is_method_handle_invoke(), "disjoint");
- return true;
- } else {
- return false;
- }
+// Test if this method is an internal MH primitive method.
+bool methodOopDesc::is_method_handle_intrinsic() const {
+ vmIntrinsics::ID iid = intrinsic_id();
+ return (MethodHandles::is_signature_polymorphic(iid) &&
+ MethodHandles::is_signature_polymorphic_intrinsic(iid));
}
-methodHandle methodOopDesc::make_invoke_method(KlassHandle holder,
- Symbol* name,
- Symbol* signature,
- Handle method_type, TRAPS) {
+bool methodOopDesc::has_member_arg() const {
+ vmIntrinsics::ID iid = intrinsic_id();
+ return (MethodHandles::is_signature_polymorphic(iid) &&
+ MethodHandles::has_member_arg(iid));
+}
+
+// Make an instance of a signature-polymorphic internal MH primitive.
+methodHandle methodOopDesc::make_method_handle_intrinsic(vmIntrinsics::ID iid,
+ Symbol* signature,
+ TRAPS) {
ResourceMark rm;
methodHandle empty;
- assert(holder() == SystemDictionary::MethodHandle_klass(),
- "must be a JSR 292 magic type");
-
+ KlassHandle holder = SystemDictionary::MethodHandle_klass();
+ Symbol* name = MethodHandles::signature_polymorphic_intrinsic_name(iid);
+ assert(iid == MethodHandles::signature_polymorphic_name_id(name), "");
if (TraceMethodHandles) {
- tty->print("Creating invoke method for ");
- signature->print_value();
- tty->cr();
+ tty->print_cr("make_method_handle_intrinsic MH.%s%s", name->as_C_string(), signature->as_C_string());
}
// invariant: cp->symbol_at_put is preceded by a refcount increment (more usually a lookup)
name->increment_refcount();
signature->increment_refcount();
- // record non-BCP method types in the constant pool
- GrowableArray<KlassHandle>* extra_klasses = NULL;
- for (int i = -1, len = java_lang_invoke_MethodType::ptype_count(method_type()); i < len; i++) {
- oop ptype = (i == -1
- ? java_lang_invoke_MethodType::rtype(method_type())
- : java_lang_invoke_MethodType::ptype(method_type(), i));
- klassOop klass = check_non_bcp_klass(java_lang_Class::as_klassOop(ptype));
- if (klass != NULL) {
- if (extra_klasses == NULL)
- extra_klasses = new GrowableArray<KlassHandle>(len+1);
- bool dup = false;
- for (int j = 0; j < extra_klasses->length(); j++) {
- if (extra_klasses->at(j) == klass) { dup = true; break; }
- }
- if (!dup)
- extra_klasses->append(KlassHandle(THREAD, klass));
- }
- }
-
- int extra_klass_count = (extra_klasses == NULL ? 0 : extra_klasses->length());
- int cp_length = _imcp_limit + extra_klass_count;
+ int cp_length = _imcp_limit;
constantPoolHandle cp;
{
constantPoolOop cp_oop = oopFactory::new_constantPool(cp_length, IsSafeConc, CHECK_(empty));
@@ -962,19 +910,17 @@
}
cp->symbol_at_put(_imcp_invoke_name, name);
cp->symbol_at_put(_imcp_invoke_signature, signature);
- cp->string_at_put(_imcp_method_type_value, Universe::the_null_string());
- for (int j = 0; j < extra_klass_count; j++) {
- KlassHandle klass = extra_klasses->at(j);
- cp->klass_at_put(_imcp_limit + j, klass());
- }
cp->set_preresolution();
cp->set_pool_holder(holder());
- // set up the fancy stuff:
- cp->pseudo_string_at_put(_imcp_method_type_value, method_type());
+ // decide on access bits: public or not?
+ int flags_bits = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_FINAL);
+ bool must_be_static = MethodHandles::is_signature_polymorphic_static(iid);
+ if (must_be_static) flags_bits |= JVM_ACC_STATIC;
+ assert((flags_bits & JVM_ACC_PUBLIC) == 0, "do not expose these methods");
+
methodHandle m;
{
- int flags_bits = (JVM_MH_INVOKE_BITS | JVM_ACC_PUBLIC | JVM_ACC_FINAL);
methodOop m_oop = oopFactory::new_method(0, accessFlags_from(flags_bits),
0, 0, 0, 0, IsSafeConc, CHECK_(empty));
m = methodHandle(THREAD, m_oop);
@@ -982,33 +928,26 @@
m->set_constants(cp());
m->set_name_index(_imcp_invoke_name);
m->set_signature_index(_imcp_invoke_signature);
- assert(is_method_handle_invoke_name(m->name()), "");
+ assert(MethodHandles::is_signature_polymorphic_name(m->name()), "");
assert(m->signature() == signature, "");
- assert(m->is_method_handle_invoke(), "");
#ifdef CC_INTERP
ResultTypeFinder rtf(signature);
m->set_result_index(rtf.type());
#endif
m->compute_size_of_parameters(THREAD);
m->init_intrinsic_id();
- assert(m->intrinsic_id() == vmIntrinsics::_invokeExact ||
- m->intrinsic_id() == vmIntrinsics::_invokeGeneric, "must be an invoker");
+ assert(m->is_method_handle_intrinsic(), "");
+#ifdef ASSERT
+ if (!MethodHandles::is_signature_polymorphic(m->intrinsic_id())) m->print();
+ assert(MethodHandles::is_signature_polymorphic(m->intrinsic_id()), "must be an invoker");
+ assert(m->intrinsic_id() == iid, "correctly predicted iid");
+#endif //ASSERT
// Finally, set up its entry points.
- assert(m->method_handle_type() == method_type(), "");
assert(m->can_be_statically_bound(), "");
m->set_vtable_index(methodOopDesc::nonvirtual_vtable_index);
m->link_method(m, CHECK_(empty));
-#ifdef ASSERT
- // Make sure the pointer chase works.
- address p = (address) m();
- for (jint* pchase = method_type_offsets_chain(); (*pchase) != -1; pchase++) {
- p = *(address*)(p + (*pchase));
- }
- assert((oop)p == method_type(), "pointer chase is correct");
-#endif
-
if (TraceMethodHandles && (Verbose || WizardMode))
m->print_on(tty);
@@ -1025,7 +964,7 @@
}
-methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length,
+methodHandle methodOopDesc::clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length,
u_char* new_compressed_linenumber_table, int new_compressed_linenumber_size, TRAPS) {
// Code below does not work for native methods - they should never get rewritten anyway
assert(!m->is_native(), "cannot rewrite native methods");
@@ -1142,7 +1081,9 @@
// ditto for method and signature:
vmSymbols::SID name_id = vmSymbols::find_sid(name());
- if (name_id == vmSymbols::NO_SID) return;
+ if (klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle)
+ && name_id == vmSymbols::NO_SID)
+ return;
vmSymbols::SID sig_id = vmSymbols::find_sid(signature());
if (klass_id != vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle)
&& sig_id == vmSymbols::NO_SID) return;
@@ -1171,21 +1112,10 @@
// Signature-polymorphic methods: MethodHandle.invoke*, InvokeDynamic.*.
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_MethodHandle):
- if (is_static() || !is_native()) break;
- switch (name_id) {
- case vmSymbols::VM_SYMBOL_ENUM_NAME(invokeGeneric_name):
- if (!AllowInvokeGeneric) break;
- case vmSymbols::VM_SYMBOL_ENUM_NAME(invoke_name):
- id = vmIntrinsics::_invokeGeneric;
- break;
- case vmSymbols::VM_SYMBOL_ENUM_NAME(invokeExact_name):
- id = vmIntrinsics::_invokeExact;
- break;
- }
- break;
- case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_InvokeDynamic):
- if (!is_static() || !is_native()) break;
- id = vmIntrinsics::_invokeDynamic;
+ if (!is_native()) break;
+ id = MethodHandles::signature_polymorphic_name_id(method_holder(), name());
+ if (is_static() != MethodHandles::is_signature_polymorphic_static(id))
+ id = vmIntrinsics::_none;
break;
}
@@ -1198,6 +1128,12 @@
// These two methods are static since a GC may move the methodOopDesc
bool methodOopDesc::load_signature_classes(methodHandle m, TRAPS) {
+ if (THREAD->is_Compiler_thread()) {
+ // There is nothing useful this routine can do from within the Compile thread.
+ // Hopefully, the signature contains only well-known classes.
+ // We could scan for this and return true/false, but the caller won't care.
+ return false;
+ }
bool sig_is_loaded = true;
Handle class_loader(THREAD, instanceKlass::cast(m->method_holder())->class_loader());
Handle protection_domain(THREAD, Klass::cast(m->method_holder())->protection_domain());
@@ -1251,6 +1187,8 @@
#endif
name()->print_symbol_on(st);
if (WizardMode) signature()->print_symbol_on(st);
+ else if (MethodHandles::is_signature_polymorphic(intrinsic_id()))
+ MethodHandles::print_as_basic_type_signature_on(st, signature(), true);
}
// This is only done during class loading, so it is OK to assume method_idnum matches the methods() array
--- a/hotspot/src/share/vm/oops/methodOop.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/methodOop.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -124,7 +124,9 @@
u1 _intrinsic_id; // vmSymbols::intrinsic_id (0 == _none)
u1 _jfr_towrite : 1, // Flags
_force_inline : 1,
- : 6;
+ _hidden : 1,
+ _dont_inline : 1,
+ : 4;
u2 _interpreter_throwout_count; // Count of times method was exited via exception while interpreting
u2 _number_of_breakpoints; // fullspeed debugging support
InvocationCounter _invocation_counter; // Incremented before each activation of the method - used to trigger frequency-based optimizations
@@ -245,8 +247,10 @@
void set_constants(constantPoolOop c) { constMethod()->set_constants(c); }
// max stack
- int max_stack() const { return _max_stack; }
- void set_max_stack(int size) { _max_stack = size; }
+ // return original max stack size for method verification
+ int verifier_max_stack() const { return _max_stack; }
+ int max_stack() const { return _max_stack + extra_stack_entries(); }
+ void set_max_stack(int size) { _max_stack = size; }
// max locals
int max_locals() const { return _max_locals; }
@@ -590,28 +594,19 @@
bool is_overridden_in(klassOop k) const;
// JSR 292 support
- bool is_method_handle_invoke() const { return access_flags().is_method_handle_invoke(); }
- static bool is_method_handle_invoke_name(vmSymbols::SID name_sid);
- static bool is_method_handle_invoke_name(Symbol* name) {
- return is_method_handle_invoke_name(vmSymbols::find_sid(name));
- }
- // Tests if this method is an internal adapter frame from the
- // MethodHandleCompiler.
- bool is_method_handle_adapter() const;
- static methodHandle make_invoke_method(KlassHandle holder,
- Symbol* name, //invokeExact or invokeGeneric
- Symbol* signature, //anything at all
- Handle method_type,
- TRAPS);
+ bool is_method_handle_intrinsic() const; // MethodHandles::is_signature_polymorphic_intrinsic(intrinsic_id)
+ bool is_compiled_lambda_form() const; // intrinsic_id() == vmIntrinsics::_compiledLambdaForm
+ bool has_member_arg() const; // intrinsic_id() == vmIntrinsics::_linkToSpecial, etc.
+ static methodHandle make_method_handle_intrinsic(vmIntrinsics::ID iid, // _invokeBasic, _linkToVirtual
+ Symbol* signature, //anything at all
+ TRAPS);
static klassOop check_non_bcp_klass(klassOop klass);
// these operate only on invoke methods:
- oop method_handle_type() const;
- static jint* method_type_offsets_chain(); // series of pointer-offsets, terminated by -1
// presize interpreter frames for extra interpreter stack entries, if needed
// method handles want to be able to push a few extra values (e.g., a bound receiver), and
// invokedynamic sometimes needs to push a bootstrap method, call site, and arglist,
// all without checking for a stack overflow
- static int extra_stack_entries() { return EnableInvokeDynamic ? (int) MethodHandlePushLimit + 3 : 0; }
+ static int extra_stack_entries() { return EnableInvokeDynamic ? 2 : 0; }
static int extra_stack_words(); // = extra_stack_entries() * Interpreter::stackElementSize()
// RedefineClasses() support:
@@ -656,8 +651,12 @@
bool jfr_towrite() { return _jfr_towrite; }
void set_jfr_towrite(bool towrite) { _jfr_towrite = towrite; }
- bool force_inline() { return _force_inline; }
- void set_force_inline(bool fi) { _force_inline = fi; }
+ bool force_inline() { return _force_inline; }
+ void set_force_inline(bool x) { _force_inline = x; }
+ bool dont_inline() { return _dont_inline; }
+ void set_dont_inline(bool x) { _dont_inline = x; }
+ bool is_hidden() { return _hidden; }
+ void set_hidden(bool x) { _hidden = x; }
// On-stack replacement support
bool has_osr_nmethod(int level, bool match_level) {
@@ -704,8 +703,8 @@
static bool has_unloaded_classes_in_signature(methodHandle m, TRAPS);
// Printing
- void print_short_name(outputStream* st) /*PRODUCT_RETURN*/; // prints as klassname::methodname; Exposed so field engineers can debug VM
- void print_name(outputStream* st) PRODUCT_RETURN; // prints as "virtual void foo(int)"
+ void print_short_name(outputStream* st = tty) /*PRODUCT_RETURN*/; // prints as klassname::methodname; Exposed so field engineers can debug VM
+ void print_name(outputStream* st = tty) PRODUCT_RETURN; // prints as "virtual void foo(int)"
// Helper routine used for method sorting
static void sort_methods(objArrayOop methods,
--- a/hotspot/src/share/vm/oops/symbol.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/oops/symbol.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -96,7 +96,7 @@
address scan = bytes + i;
if (scan > limit)
return -1;
- for (;;) {
+ for (; scan <= limit; scan++) {
scan = (address) memchr(scan, first_char, (limit + 1 - scan));
if (scan == NULL)
return -1; // not found
@@ -104,6 +104,7 @@
if (memcmp(scan, str, len) == 0)
return (int)(scan - bytes);
}
+ return -1;
}
--- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -93,7 +93,7 @@
);
}
-// positive filter: should send be inlined? returns NULL, if yes, or rejection msg
+// positive filter: should callee be inlined? returns NULL, if yes, or rejection msg
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()) {
@@ -131,33 +131,6 @@
int call_site_count = method()->scale_count(profile.count());
int invoke_count = method()->interpreter_invocation_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();
- if (invoke_count == 0) {
- return "method handle not reached";
- }
-
- if (_caller_jvms != NULL && _caller_jvms->method() != NULL &&
- _caller_jvms->method()->method_data() != NULL &&
- !_caller_jvms->method()->method_data()->is_empty()) {
- ciMethodData* mdo = _caller_jvms->method()->method_data();
- ciProfileData* mha_profile = mdo->bci_to_data(_caller_jvms->bci());
- assert(mha_profile, "must exist");
- CounterData* cd = mha_profile->as_CounterData();
- call_site_count = cd->count();
- } else {
- 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;
@@ -189,15 +162,16 @@
}
-// negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg
+// negative filter: should callee NOT be inlined? returns NULL, ok to inline, or rejection msg
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;
- if (callee_method->is_abstract()) fail = "abstract method";
+ if ( callee_method->is_abstract()) fail = "abstract method";
// note: we allow ik->is_abstract()
- if (!callee_method->holder()->is_initialized()) fail = "method holder not initialized";
- if (callee_method->is_native()) fail = "native method";
+ if (!callee_method->holder()->is_initialized()) fail = "method holder not initialized";
+ if ( callee_method->is_native()) fail = "native method";
+ if ( callee_method->dont_inline()) fail = "don't inline by annotation";
if (fail) {
*wci_result = *(WarmCallInfo::always_cold());
@@ -217,7 +191,8 @@
}
}
- if (callee_method->has_compiled_code() && callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) {
+ if (callee_method->has_compiled_code() &&
+ callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) {
wci_result->set_profit(wci_result->profit() * 0.1);
// %%% adjust wci_result->size()?
}
@@ -225,26 +200,25 @@
return NULL;
}
- // Always inline MethodHandle methods and generated MethodHandle adapters.
- if (callee_method->is_method_handle_invoke() || callee_method->is_method_handle_adapter())
- return NULL;
+ // First check all inlining restrictions which are required for correctness
+ if ( callee_method->is_abstract()) return "abstract method";
+ // note: we allow ik->is_abstract()
+ if (!callee_method->holder()->is_initialized()) return "method holder not initialized";
+ if ( callee_method->is_native()) return "native method";
+ if ( callee_method->dont_inline()) return "don't inline by annotation";
+ if ( callee_method->has_unloaded_classes_in_signature()) return "unloaded signature classes";
- // First check all inlining restrictions which are required for correctness
- if (callee_method->is_abstract()) return "abstract method";
- // note: we allow ik->is_abstract()
- if (!callee_method->holder()->is_initialized()) return "method holder not initialized";
- if (callee_method->is_native()) return "native method";
- if (callee_method->has_unloaded_classes_in_signature()) return "unloaded signature classes";
-
- if (callee_method->should_inline()) {
+ if (callee_method->force_inline() || callee_method->should_inline()) {
// ignore heuristic controls on inlining
return NULL;
}
// Now perform checks which are heuristic
- if( callee_method->has_compiled_code() && callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode )
+ if (callee_method->has_compiled_code() &&
+ callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) {
return "already compiled into a big method";
+ }
// don't inline exception code unless the top method belongs to an
// exception class
@@ -270,7 +244,7 @@
}
// use frequency-based objections only for non-trivial methods
- if (callee_method->code_size_for_inlining() <= MaxTrivialSize) return NULL;
+ if (callee_method->code_size() <= MaxTrivialSize) return NULL;
// don't use counts with -Xcomp or CTW
if (UseInterpreter && !CompileTheWorld) {
@@ -319,7 +293,7 @@
}
// suppress a few checks for accessors and trivial methods
- if (callee_method->code_size_for_inlining() > MaxTrivialSize) {
+ if (callee_method->code_size() > MaxTrivialSize) {
// don't inline into giant methods
if (C->unique() > (uint)NodeCountInliningCutoff) {
@@ -346,7 +320,7 @@
}
// detect direct and indirect recursive inlining
- {
+ if (!callee_method->is_compiled_lambda_form()) {
// count the current method and the callee
int inline_level = (method() == callee_method) ? 1 : 0;
if (inline_level > MaxRecursiveInlineLevel)
@@ -412,6 +386,7 @@
const char* InlineTree::check_can_parse(ciMethod* callee) {
// Certain methods cannot be parsed at all:
if ( callee->is_native()) return "native method";
+ if ( callee->is_abstract()) return "abstract method";
if (!callee->can_be_compiled()) return "not compilable (disabled)";
if (!callee->has_balanced_monitors()) return "not compilable (unbalanced monitors)";
if ( callee->get_flow_analysis()->failing()) return "not compilable (flow analysis failed)";
@@ -426,7 +401,7 @@
if (Verbose && callee_method) {
const InlineTree *top = this;
while( top->caller_tree() != NULL ) { top = top->caller_tree(); }
- tty->print(" bcs: %d+%d invoked: %d", top->count_inline_bcs(), callee_method->code_size(), callee_method->interpreter_invocation_count());
+ //tty->print(" bcs: %d+%d invoked: %d", top->count_inline_bcs(), callee_method->code_size(), callee_method->interpreter_invocation_count());
}
}
@@ -449,10 +424,7 @@
// Do some initial checks.
if (!pass_initial_checks(caller_method, caller_bci, callee_method)) {
- if (PrintInlining) {
- failure_msg = "failed_initial_checks";
- print_inlining(callee_method, caller_bci, failure_msg);
- }
+ if (PrintInlining) print_inlining(callee_method, caller_bci, "failed initial checks");
return NULL;
}
@@ -539,9 +511,10 @@
}
int max_inline_level_adjust = 0;
if (caller_jvms->method() != NULL) {
- if (caller_jvms->method()->is_method_handle_adapter())
+ if (caller_jvms->method()->is_compiled_lambda_form())
max_inline_level_adjust += 1; // don't count actions in MH or indy adapter frames
- else if (callee_method->is_method_handle_invoke()) {
+ else if (callee_method->is_method_handle_intrinsic() ||
+ callee_method->is_compiled_lambda_form()) {
max_inline_level_adjust += 1; // don't count method handle calls from java.lang.invoke implem
}
if (max_inline_level_adjust != 0 && PrintInlining && (Verbose || WizardMode)) {
@@ -590,7 +563,7 @@
// Given a jvms, which determines a call chain from the root method,
// find the corresponding inline tree.
// Note: This method will be removed or replaced as InlineTree goes away.
-InlineTree* InlineTree::find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee, bool create_if_not_found) {
+InlineTree* InlineTree::find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee) {
InlineTree* iltp = root;
uint depth = jvms && jvms->has_method() ? jvms->depth() : 0;
for (uint d = 1; d <= depth; d++) {
@@ -599,12 +572,12 @@
assert(jvmsp->method() == iltp->method(), "tree still in sync");
ciMethod* d_callee = (d == depth) ? callee : jvms->of_depth(d+1)->method();
InlineTree* sub = iltp->callee_at(jvmsp->bci(), d_callee);
- if (!sub) {
- if (create_if_not_found && d == depth) {
- return iltp->build_inline_tree_for_callee(d_callee, jvmsp, jvmsp->bci());
+ if (sub == NULL) {
+ if (d == depth) {
+ sub = iltp->build_inline_tree_for_callee(d_callee, jvmsp, jvmsp->bci());
}
- assert(sub != NULL, "should be a sub-ilt here");
- return NULL;
+ guarantee(sub != NULL, "should be a sub-ilt here");
+ return sub;
}
iltp = sub;
}
--- a/hotspot/src/share/vm/opto/callGenerator.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/callGenerator.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, 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
@@ -26,6 +26,7 @@
#include "ci/bcEscapeAnalyzer.hpp"
#include "ci/ciCallSite.hpp"
#include "ci/ciCPCache.hpp"
+#include "ci/ciMemberName.hpp"
#include "ci/ciMethodHandle.hpp"
#include "classfile/javaClasses.hpp"
#include "compiler/compileLog.hpp"
@@ -39,9 +40,6 @@
#include "opto/runtime.hpp"
#include "opto/subnode.hpp"
-CallGenerator::CallGenerator(ciMethod* method) {
- _method = method;
-}
// Utility function.
const TypeFunc* CallGenerator::tf() const {
@@ -148,7 +146,8 @@
}
// Mark the call node as virtual, sort of:
call->set_optimized_virtual(true);
- if (method()->is_method_handle_invoke()) {
+ if (method()->is_method_handle_intrinsic() ||
+ method()->is_compiled_lambda_form()) {
call->set_method_handle_invoke(true);
}
}
@@ -325,12 +324,13 @@
CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) {
assert(!m->is_static(), "for_virtual_call mismatch");
- assert(!m->is_method_handle_invoke(), "should be a direct call");
+ assert(!m->is_method_handle_intrinsic(), "should be a direct call");
return new VirtualCallGenerator(m, vtable_index);
}
CallGenerator* CallGenerator::for_dynamic_call(ciMethod* m) {
- assert(m->is_method_handle_invoke() || m->is_method_handle_adapter(), "for_dynamic_call mismatch");
+ assert(m->is_compiled_lambda_form(), "for_dynamic_call mismatch");
+ //@@ FIXME: this should be done via a direct call
return new DynamicCallGenerator(m);
}
@@ -654,272 +654,96 @@
}
-//------------------------PredictedDynamicCallGenerator-----------------------
-// Internal class which handles all out-of-line calls checking receiver type.
-class PredictedDynamicCallGenerator : public CallGenerator {
- ciMethodHandle* _predicted_method_handle;
- CallGenerator* _if_missed;
- CallGenerator* _if_hit;
- float _hit_prob;
-
-public:
- PredictedDynamicCallGenerator(ciMethodHandle* predicted_method_handle,
- CallGenerator* if_missed,
- CallGenerator* if_hit,
- float hit_prob)
- : CallGenerator(if_missed->method()),
- _predicted_method_handle(predicted_method_handle),
- _if_missed(if_missed),
- _if_hit(if_hit),
- _hit_prob(hit_prob)
- {}
-
- virtual bool is_inline() const { return _if_hit->is_inline(); }
- virtual bool is_deferred() const { return _if_hit->is_deferred(); }
-
- virtual JVMState* generate(JVMState* jvms);
-};
-
-
-CallGenerator* CallGenerator::for_predicted_dynamic_call(ciMethodHandle* predicted_method_handle,
- CallGenerator* if_missed,
- CallGenerator* if_hit,
- float hit_prob) {
- return new PredictedDynamicCallGenerator(predicted_method_handle, if_missed, if_hit, hit_prob);
-}
-
-
-CallGenerator* CallGenerator::for_method_handle_call(Node* method_handle, JVMState* jvms,
- ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
- assert(callee->is_method_handle_invoke() || callee->is_method_handle_adapter(), "for_method_handle_call mismatch");
- CallGenerator* cg = CallGenerator::for_method_handle_inline(method_handle, jvms, caller, callee, profile);
+CallGenerator* CallGenerator::for_method_handle_call(JVMState* jvms, ciMethod* caller, ciMethod* callee) {
+ assert(callee->is_method_handle_intrinsic() ||
+ callee->is_compiled_lambda_form(), "for_method_handle_call mismatch");
+ CallGenerator* cg = CallGenerator::for_method_handle_inline(jvms, caller, callee);
if (cg != NULL)
return cg;
return CallGenerator::for_direct_call(callee);
}
-CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMState* jvms,
- ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
- if (method_handle->Opcode() == Op_ConP) {
- const TypeOopPtr* oop_ptr = method_handle->bottom_type()->is_oopptr();
- ciObject* const_oop = oop_ptr->const_oop();
- ciMethodHandle* method_handle = const_oop->as_method_handle();
-
- // Set the callee to have access to the class and signature in
- // the MethodHandleCompiler.
- method_handle->set_callee(callee);
- 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();
- if (target_method != NULL) {
- CallGenerator* cg = Compile::current()->call_generator(target_method, -1, false, jvms, true, PROB_ALWAYS);
- if (cg != NULL && cg->is_inline())
- return cg;
- }
- } else if (method_handle->Opcode() == Op_Phi && method_handle->req() == 3 &&
- method_handle->in(1)->Opcode() == Op_ConP && method_handle->in(2)->Opcode() == Op_ConP) {
- float prob = PROB_FAIR;
- Node* meth_region = method_handle->in(0);
- if (meth_region->is_Region() &&
- meth_region->in(1)->is_Proj() && meth_region->in(2)->is_Proj() &&
- meth_region->in(1)->in(0) == meth_region->in(2)->in(0) &&
- meth_region->in(1)->in(0)->is_If()) {
- // If diamond, so grab the probability of the test to drive the inlining below
- prob = meth_region->in(1)->in(0)->as_If()->_prob;
- if (meth_region->in(1)->is_IfTrue()) {
- prob = 1 - prob;
+CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod* caller, ciMethod* callee) {
+ GraphKit kit(jvms);
+ PhaseGVN& gvn = kit.gvn();
+ Compile* C = kit.C;
+ vmIntrinsics::ID iid = callee->intrinsic_id();
+ switch (iid) {
+ case vmIntrinsics::_invokeBasic:
+ {
+ // get MethodHandle receiver
+ Node* receiver = kit.argument(0);
+ if (receiver->Opcode() == Op_ConP) {
+ const TypeOopPtr* oop_ptr = receiver->bottom_type()->is_oopptr();
+ ciMethod* target = oop_ptr->const_oop()->as_method_handle()->get_vmtarget();
+ guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove
+ const int vtable_index = methodOopDesc::invalid_vtable_index;
+ CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS);
+ if (cg != NULL && cg->is_inline())
+ return cg;
+ } else {
+ if (PrintInlining) CompileTask::print_inlining(callee, jvms->depth() - 1, jvms->bci(), "receiver not constant");
}
}
+ break;
- // selectAlternative idiom merging two constant MethodHandles.
- // Generate a guard so that each can be inlined. We might want to
- // do more inputs at later point but this gets the most common
- // case.
- CallGenerator* cg1 = for_method_handle_call(method_handle->in(1), jvms, caller, callee, profile.rescale(1.0 - prob));
- CallGenerator* cg2 = for_method_handle_call(method_handle->in(2), jvms, caller, callee, profile.rescale(prob));
- if (cg1 != NULL && cg2 != NULL) {
- const TypeOopPtr* oop_ptr = method_handle->in(1)->bottom_type()->is_oopptr();
- ciObject* const_oop = oop_ptr->const_oop();
- ciMethodHandle* mh = const_oop->as_method_handle();
- return new PredictedDynamicCallGenerator(mh, cg2, cg1, prob);
- }
- }
- return NULL;
-}
+ case vmIntrinsics::_linkToVirtual:
+ case vmIntrinsics::_linkToStatic:
+ case vmIntrinsics::_linkToSpecial:
+ case vmIntrinsics::_linkToInterface:
+ {
+ // pop MemberName argument
+ Node* member_name = kit.argument(callee->arg_size() - 1);
+ if (member_name->Opcode() == Op_ConP) {
+ const TypeOopPtr* oop_ptr = member_name->bottom_type()->is_oopptr();
+ ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget();
-CallGenerator* CallGenerator::for_invokedynamic_call(JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
- assert(callee->is_method_handle_invoke() || callee->is_method_handle_adapter(), "for_invokedynamic_call mismatch");
- // Get the CallSite object.
- ciBytecodeStream str(caller);
- str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
- ciCallSite* call_site = str.get_call_site();
- CallGenerator* cg = CallGenerator::for_invokedynamic_inline(call_site, jvms, caller, callee, profile);
- if (cg != NULL)
- return cg;
- return CallGenerator::for_dynamic_call(callee);
-}
-
-CallGenerator* CallGenerator::for_invokedynamic_inline(ciCallSite* call_site, JVMState* jvms,
- ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
- ciMethodHandle* method_handle = call_site->get_target();
+ // In lamda forms we erase signature types to avoid resolving issues
+ // involving class loaders. When we optimize a method handle invoke
+ // to a direct call we must cast the receiver and arguments to its
+ // actual types.
+ ciSignature* signature = target->signature();
+ const int receiver_skip = target->is_static() ? 0 : 1;
+ // Cast receiver to its type.
+ if (!target->is_static()) {
+ Node* arg = kit.argument(0);
+ const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr();
+ const Type* sig_type = TypeOopPtr::make_from_klass(signature->accessing_klass());
+ if (arg_type != NULL && !arg_type->higher_equal(sig_type)) {
+ Node* cast_obj = gvn.transform(new (C, 2) CheckCastPPNode(kit.control(), arg, sig_type));
+ kit.set_argument(0, cast_obj);
+ }
+ }
+ // Cast reference arguments to its type.
+ for (int i = 0; i < signature->count(); i++) {
+ ciType* t = signature->type_at(i);
+ if (t->is_klass()) {
+ Node* arg = kit.argument(receiver_skip + i);
+ const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr();
+ const Type* sig_type = TypeOopPtr::make_from_klass(t->as_klass());
+ if (arg_type != NULL && !arg_type->higher_equal(sig_type)) {
+ Node* cast_obj = gvn.transform(new (C, 2) CheckCastPPNode(kit.control(), arg, sig_type));
+ kit.set_argument(receiver_skip + i, cast_obj);
+ }
+ }
+ }
+ const int vtable_index = methodOopDesc::invalid_vtable_index;
+ const bool call_is_virtual = target->is_abstract(); // FIXME workaround
+ CallGenerator* cg = C->call_generator(target, vtable_index, call_is_virtual, jvms, true, PROB_ALWAYS);
+ if (cg != NULL && cg->is_inline())
+ return cg;
+ }
+ }
+ break;
- // Set the callee to have access to the class and signature in the
- // MethodHandleCompiler.
- method_handle->set_callee(callee);
- 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();
- if (target_method != NULL) {
- Compile *C = Compile::current();
- CallGenerator* cg = C->call_generator(target_method, -1, false, jvms, true, PROB_ALWAYS);
- if (cg != NULL && cg->is_inline()) {
- // Add a dependence for invalidation of the optimization.
- if (!call_site->is_constant_call_site()) {
- C->dependencies()->assert_call_site_target_value(call_site, method_handle);
- }
- return cg;
- }
+ default:
+ fatal(err_msg_res("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
+ break;
}
return NULL;
}
-JVMState* PredictedDynamicCallGenerator::generate(JVMState* jvms) {
- GraphKit kit(jvms);
- Compile* C = kit.C;
- PhaseGVN& gvn = kit.gvn();
-
- CompileLog* log = C->log();
- if (log != NULL) {
- log->elem("predicted_dynamic_call bci='%d'", jvms->bci());
- }
-
- const TypeOopPtr* predicted_mh_ptr = TypeOopPtr::make_from_constant(_predicted_method_handle, true);
- Node* predicted_mh = kit.makecon(predicted_mh_ptr);
-
- Node* bol = NULL;
- int bc = jvms->method()->java_code_at_bci(jvms->bci());
- if (bc != Bytecodes::_invokedynamic) {
- // This is the selectAlternative idiom for guardWithTest or
- // similar idioms.
- Node* receiver = kit.argument(0);
-
- // Check if the MethodHandle is the expected one
- Node* cmp = gvn.transform(new (C, 3) CmpPNode(receiver, predicted_mh));
- bol = gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq) );
- } else {
- // Get the constant pool cache from the caller class.
- ciMethod* caller_method = jvms->method();
- ciBytecodeStream str(caller_method);
- str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
- ciCPCache* cpcache = str.get_cpcache();
-
- // Get the offset of the CallSite from the constant pool cache
- // pointer.
- int index = str.get_method_index();
- size_t call_site_offset = cpcache->get_f1_offset(index);
-
- // Load the CallSite object from the constant pool cache.
- const TypeOopPtr* cpcache_type = TypeOopPtr::make_from_constant(cpcache); // returns TypeAryPtr of type T_OBJECT
- const TypeOopPtr* call_site_type = TypeOopPtr::make_from_klass(C->env()->CallSite_klass());
- Node* cpcache_adr = kit.makecon(cpcache_type);
- Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, call_site_offset);
- // The oops in the constant pool cache are not compressed; load then as raw pointers.
- Node* call_site = kit.make_load(kit.control(), call_site_adr, call_site_type, T_ADDRESS, Compile::AliasIdxRaw);
-
- // Load the target MethodHandle from the CallSite object.
- const TypeOopPtr* target_type = TypeOopPtr::make_from_klass(C->env()->MethodHandle_klass());
- Node* target_adr = kit.basic_plus_adr(call_site, call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
- Node* target_mh = kit.make_load(kit.control(), target_adr, target_type, T_OBJECT);
-
- // Check if the MethodHandle is still the same.
- Node* cmp = gvn.transform(new (C, 3) CmpPNode(target_mh, predicted_mh));
- bol = gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq) );
- }
- IfNode* iff = kit.create_and_xform_if(kit.control(), bol, _hit_prob, COUNT_UNKNOWN);
- kit.set_control( gvn.transform(new (C, 1) IfTrueNode (iff)));
- Node* slow_ctl = gvn.transform(new (C, 1) IfFalseNode(iff));
-
- SafePointNode* slow_map = NULL;
- JVMState* slow_jvms;
- { PreserveJVMState pjvms(&kit);
- kit.set_control(slow_ctl);
- if (!kit.stopped()) {
- slow_jvms = _if_missed->generate(kit.sync_jvms());
- if (kit.failing())
- return NULL; // might happen because of NodeCountInliningCutoff
- assert(slow_jvms != NULL, "must be");
- kit.add_exception_states_from(slow_jvms);
- kit.set_map(slow_jvms->map());
- if (!kit.stopped())
- slow_map = kit.stop();
- }
- }
-
- if (kit.stopped()) {
- // Instance exactly does not matches the desired type.
- kit.set_jvms(slow_jvms);
- return kit.transfer_exceptions_into_jvms();
- }
-
- // Make the hot call:
- JVMState* new_jvms = _if_hit->generate(kit.sync_jvms());
- if (new_jvms == NULL) {
- // Inline failed, so make a direct call.
- assert(_if_hit->is_inline(), "must have been a failed inline");
- CallGenerator* cg = CallGenerator::for_direct_call(_if_hit->method());
- new_jvms = cg->generate(kit.sync_jvms());
- }
- kit.add_exception_states_from(new_jvms);
- kit.set_jvms(new_jvms);
-
- // Need to merge slow and fast?
- if (slow_map == NULL) {
- // The fast path is the only path remaining.
- return kit.transfer_exceptions_into_jvms();
- }
-
- if (kit.stopped()) {
- // Inlined method threw an exception, so it's just the slow path after all.
- kit.set_jvms(slow_jvms);
- return kit.transfer_exceptions_into_jvms();
- }
-
- // Finish the diamond.
- kit.C->set_has_split_ifs(true); // Has chance for split-if optimization
- RegionNode* region = new (C, 3) RegionNode(3);
- region->init_req(1, kit.control());
- region->init_req(2, slow_map->control());
- kit.set_control(gvn.transform(region));
- Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO);
- iophi->set_req(2, slow_map->i_o());
- kit.set_i_o(gvn.transform(iophi));
- kit.merge_memory(slow_map->merged_memory(), region, 2);
- uint tos = kit.jvms()->stkoff() + kit.sp();
- uint limit = slow_map->req();
- for (uint i = TypeFunc::Parms; i < limit; i++) {
- // Skip unused stack slots; fast forward to monoff();
- if (i == tos) {
- i = kit.jvms()->monoff();
- if( i >= limit ) break;
- }
- Node* m = kit.map()->in(i);
- Node* n = slow_map->in(i);
- if (m != n) {
- const Type* t = gvn.type(m)->meet(gvn.type(n));
- Node* phi = PhiNode::make(region, m, t);
- phi->set_req(2, n);
- kit.map()->set_req(i, gvn.transform(phi));
- }
- }
- return kit.transfer_exceptions_into_jvms();
-}
-
-
//-------------------------UncommonTrapCallGenerator-----------------------------
// Internal class which handles all out-of-line calls checking receiver type.
class UncommonTrapCallGenerator : public CallGenerator {
--- a/hotspot/src/share/vm/opto/callGenerator.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/callGenerator.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -25,6 +25,7 @@
#ifndef SHARE_VM_OPTO_CALLGENERATOR_HPP
#define SHARE_VM_OPTO_CALLGENERATOR_HPP
+#include "compiler/compileBroker.hpp"
#include "opto/callnode.hpp"
#include "opto/compile.hpp"
#include "opto/type.hpp"
@@ -44,7 +45,7 @@
ciMethod* _method; // The method being called.
protected:
- CallGenerator(ciMethod* method);
+ CallGenerator(ciMethod* method) : _method(method) {}
public:
// Accessors
@@ -111,11 +112,8 @@
static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface
static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic
- static CallGenerator* for_method_handle_call(Node* method_handle, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
- static CallGenerator* for_invokedynamic_call( JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
-
- static CallGenerator* for_method_handle_inline(Node* method_handle, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
- static CallGenerator* for_invokedynamic_inline(ciCallSite* call_site, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
+ static CallGenerator* for_method_handle_call( JVMState* jvms, ciMethod* caller, ciMethod* callee);
+ static CallGenerator* for_method_handle_inline(JVMState* jvms, ciMethod* caller, ciMethod* callee);
// How to generate a replace a direct call with an inline version
static CallGenerator* for_late_inline(ciMethod* m, CallGenerator* inline_cg);
@@ -145,13 +143,21 @@
// Registry for intrinsics:
static CallGenerator* for_intrinsic(ciMethod* m);
static void register_intrinsic(ciMethod* m, CallGenerator* cg);
+
+ static void print_inlining(ciMethod* callee, int inline_level, int bci, const char* msg) {
+ if (PrintInlining)
+ CompileTask::print_inlining(callee, inline_level, bci, msg);
+ }
};
-class InlineCallGenerator : public CallGenerator {
- virtual bool is_inline() const { return true; }
+//------------------------InlineCallGenerator----------------------------------
+class InlineCallGenerator : public CallGenerator {
protected:
- InlineCallGenerator(ciMethod* method) : CallGenerator(method) { }
+ InlineCallGenerator(ciMethod* method) : CallGenerator(method) {}
+
+ public:
+ virtual bool is_inline() const { return true; }
};
--- a/hotspot/src/share/vm/opto/callnode.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/callnode.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -231,9 +231,9 @@
}
//=============================================================================
-JVMState::JVMState(ciMethod* method, JVMState* caller) {
+JVMState::JVMState(ciMethod* method, JVMState* caller) :
+ _method(method) {
assert(method != NULL, "must be valid call site");
- _method = method;
_reexecute = Reexecute_Undefined;
debug_only(_bci = -99); // random garbage value
debug_only(_map = (SafePointNode*)-1);
@@ -246,8 +246,8 @@
_endoff = _monoff;
_sp = 0;
}
-JVMState::JVMState(int stack_size) {
- _method = NULL;
+JVMState::JVMState(int stack_size) :
+ _method(NULL) {
_bci = InvocationEntryBci;
_reexecute = Reexecute_Undefined;
debug_only(_map = (SafePointNode*)-1);
@@ -526,8 +526,8 @@
}
_map->dump(2);
}
- st->print("JVMS depth=%d loc=%d stk=%d mon=%d scalar=%d end=%d mondepth=%d sp=%d bci=%d reexecute=%s method=",
- depth(), locoff(), stkoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci(), should_reexecute()?"true":"false");
+ st->print("JVMS depth=%d loc=%d stk=%d arg=%d mon=%d scalar=%d end=%d mondepth=%d sp=%d bci=%d reexecute=%s method=",
+ depth(), locoff(), stkoff(), argoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci(), should_reexecute()?"true":"false");
if (_method == NULL) {
st->print_cr("(none)");
} else {
--- a/hotspot/src/share/vm/opto/callnode.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/callnode.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -197,7 +197,7 @@
private:
JVMState* _caller; // List pointer for forming scope chains
- uint _depth; // One mroe than caller depth, or one.
+ uint _depth; // One more than caller depth, or one.
uint _locoff; // Offset to locals in input edge mapping
uint _stkoff; // Offset to stack in input edge mapping
uint _monoff; // Offset to monitors in input edge mapping
@@ -223,6 +223,8 @@
JVMState(int stack_size); // root state; has a null method
// Access functions for the JVM
+ // ... --|--- loc ---|--- stk ---|--- arg ---|--- mon ---|--- scl ---|
+ // \ locoff \ stkoff \ argoff \ monoff \ scloff \ endoff
uint locoff() const { return _locoff; }
uint stkoff() const { return _stkoff; }
uint argoff() const { return _stkoff + _sp; }
@@ -231,15 +233,16 @@
uint endoff() const { return _endoff; }
uint oopoff() const { return debug_end(); }
- int loc_size() const { return _stkoff - _locoff; }
- int stk_size() const { return _monoff - _stkoff; }
- int mon_size() const { return _scloff - _monoff; }
- int scl_size() const { return _endoff - _scloff; }
+ int loc_size() const { return stkoff() - locoff(); }
+ int stk_size() const { return monoff() - stkoff(); }
+ int arg_size() const { return monoff() - argoff(); }
+ int mon_size() const { return scloff() - monoff(); }
+ int scl_size() const { return endoff() - scloff(); }
- bool is_loc(uint i) const { return i >= _locoff && i < _stkoff; }
- bool is_stk(uint i) const { return i >= _stkoff && i < _monoff; }
- bool is_mon(uint i) const { return i >= _monoff && i < _scloff; }
- bool is_scl(uint i) const { return i >= _scloff && i < _endoff; }
+ bool is_loc(uint i) const { return locoff() <= i && i < stkoff(); }
+ bool is_stk(uint i) const { return stkoff() <= i && i < monoff(); }
+ bool is_mon(uint i) const { return monoff() <= i && i < scloff(); }
+ bool is_scl(uint i) const { return scloff() <= i && i < endoff(); }
uint sp() const { return _sp; }
int bci() const { return _bci; }
--- a/hotspot/src/share/vm/opto/chaitin.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/chaitin.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1536,7 +1536,7 @@
// Check for AddP-related opcodes
if( !derived->is_Phi() ) {
- assert(derived->as_Mach()->ideal_Opcode() == Op_AddP, err_msg("but is: %s", derived->Name()));
+ assert(derived->as_Mach()->ideal_Opcode() == Op_AddP, err_msg_res("but is: %s", derived->Name()));
Node *base = derived->in(AddPNode::Base);
derived_base_map[derived->_idx] = base;
return base;
--- a/hotspot/src/share/vm/opto/compile.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/compile.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3138,7 +3138,7 @@
default: ShouldNotReachHere();
}
assert(constant_addr, "consts section too small");
- assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), err_msg("must be: %d == %d", constant_addr - _masm.code()->consts()->start(), con.offset()));
+ assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), err_msg_res("must be: %d == %d", constant_addr - _masm.code()->consts()->start(), con.offset()));
}
}
@@ -3199,7 +3199,7 @@
if (Compile::current()->in_scratch_emit_size()) return;
assert(labels.is_nonempty(), "must be");
- assert((uint) labels.length() == n->outcnt(), err_msg("must be equal: %d == %d", labels.length(), n->outcnt()));
+ assert((uint) labels.length() == n->outcnt(), err_msg_res("must be equal: %d == %d", labels.length(), n->outcnt()));
// Since MachConstantNode::constant_offset() also contains
// table_base_offset() we need to subtract the table_base_offset()
@@ -3211,7 +3211,7 @@
for (uint i = 0; i < n->outcnt(); i++) {
address* constant_addr = &jump_table_base[i];
- assert(*constant_addr == (((address) n) + i), err_msg("all jump-table entries must contain adjusted node pointer: " INTPTR_FORMAT " == " INTPTR_FORMAT, *constant_addr, (((address) n) + i)));
+ assert(*constant_addr == (((address) n) + i), err_msg_res("all jump-table entries must contain adjusted node pointer: " INTPTR_FORMAT " == " INTPTR_FORMAT, *constant_addr, (((address) n) + i)));
*constant_addr = cb.consts()->target(*labels.at(i), (address) constant_addr);
cb.consts()->relocate((address) constant_addr, relocInfo::internal_word_type);
}
--- a/hotspot/src/share/vm/opto/doCall.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/doCall.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -59,13 +59,13 @@
}
#endif
-CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual,
+CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool call_is_virtual,
JVMState* jvms, bool allow_inline,
float prof_factor, bool allow_intrinsics) {
ciMethod* caller = jvms->method();
int bci = jvms->bci();
Bytecodes::Code bytecode = caller->java_code_at_bci(bci);
- guarantee(call_method != NULL, "failed method resolution");
+ guarantee(callee != NULL, "failed method resolution");
// Dtrace currently doesn't work unless all calls are vanilla
if (env()->dtrace_method_probes()) {
@@ -91,7 +91,7 @@
int rid = (receiver_count >= 0)? log->identify(profile.receiver(0)): -1;
int r2id = (rid != -1 && profile.has_receiver(1))? log->identify(profile.receiver(1)):-1;
log->begin_elem("call method='%d' count='%d' prof_factor='%g'",
- log->identify(call_method), site_count, prof_factor);
+ log->identify(callee), site_count, prof_factor);
if (call_is_virtual) log->print(" virtual='1'");
if (allow_inline) log->print(" inline='1'");
if (receiver_count >= 0) {
@@ -109,7 +109,7 @@
// We do this before the strict f.p. check below because the
// intrinsics handle strict f.p. correctly.
if (allow_inline && allow_intrinsics) {
- CallGenerator* cg = find_intrinsic(call_method, call_is_virtual);
+ CallGenerator* cg = find_intrinsic(callee, call_is_virtual);
if (cg != NULL) return cg;
}
@@ -117,19 +117,12 @@
// NOTE: This must happen before normal inlining logic below since
// MethodHandle.invoke* are native methods which obviously don't
// have bytecodes and so normal inlining fails.
- if (call_method->is_method_handle_invoke()) {
- if (bytecode != Bytecodes::_invokedynamic) {
- GraphKit kit(jvms);
- Node* method_handle = kit.argument(0);
- return CallGenerator::for_method_handle_call(method_handle, jvms, caller, call_method, profile);
- }
- else {
- return CallGenerator::for_invokedynamic_call(jvms, caller, call_method, profile);
- }
+ if (callee->is_method_handle_intrinsic()) {
+ return CallGenerator::for_method_handle_call(jvms, caller, callee);
}
// Do not inline strict fp into non-strict code, or the reverse
- if (caller->is_strict() ^ call_method->is_strict()) {
+ if (caller->is_strict() ^ callee->is_strict()) {
allow_inline = false;
}
@@ -155,26 +148,26 @@
}
WarmCallInfo scratch_ci;
if (!UseOldInlining)
- scratch_ci.init(jvms, call_method, profile, prof_factor);
- WarmCallInfo* ci = ilt->ok_to_inline(call_method, jvms, profile, &scratch_ci);
+ scratch_ci.init(jvms, callee, profile, prof_factor);
+ WarmCallInfo* ci = ilt->ok_to_inline(callee, jvms, profile, &scratch_ci);
assert(ci != &scratch_ci, "do not let this pointer escape");
bool allow_inline = (ci != NULL && !ci->is_cold());
bool require_inline = (allow_inline && ci->is_hot());
if (allow_inline) {
- CallGenerator* cg = CallGenerator::for_inline(call_method, expected_uses);
- if (require_inline && cg != NULL && should_delay_inlining(call_method, jvms)) {
+ CallGenerator* cg = CallGenerator::for_inline(callee, expected_uses);
+ if (require_inline && cg != NULL && should_delay_inlining(callee, jvms)) {
// Delay the inlining of this method to give us the
// opportunity to perform some high level optimizations
// first.
- return CallGenerator::for_late_inline(call_method, cg);
+ return CallGenerator::for_late_inline(callee, cg);
}
if (cg == NULL) {
// Fall through.
} else if (require_inline || !InlineWarmCalls) {
return cg;
} else {
- CallGenerator* cold_cg = call_generator(call_method, vtable_index, call_is_virtual, jvms, false, prof_factor);
+ CallGenerator* cold_cg = call_generator(callee, vtable_index, call_is_virtual, jvms, false, prof_factor);
return CallGenerator::for_warm_call(ci, cold_cg, cg);
}
}
@@ -189,7 +182,7 @@
(profile.morphism() == 2 && UseBimorphicInlining)) {
// receiver_method = profile.method();
// Profiles do not suggest methods now. Look it up in the major receiver.
- receiver_method = call_method->resolve_invoke(jvms->method()->holder(),
+ receiver_method = callee->resolve_invoke(jvms->method()->holder(),
profile.receiver(0));
}
if (receiver_method != NULL) {
@@ -201,7 +194,7 @@
CallGenerator* next_hit_cg = NULL;
ciMethod* next_receiver_method = NULL;
if (profile.morphism() == 2 && UseBimorphicInlining) {
- next_receiver_method = call_method->resolve_invoke(jvms->method()->holder(),
+ next_receiver_method = callee->resolve_invoke(jvms->method()->holder(),
profile.receiver(1));
if (next_receiver_method != NULL) {
next_hit_cg = this->call_generator(next_receiver_method,
@@ -224,12 +217,12 @@
) {
// Generate uncommon trap for class check failure path
// in case of monomorphic or bimorphic virtual call site.
- miss_cg = CallGenerator::for_uncommon_trap(call_method, reason,
+ miss_cg = CallGenerator::for_uncommon_trap(callee, reason,
Deoptimization::Action_maybe_recompile);
} else {
// Generate virtual call for class check failure path
// in case of polymorphic virtual call site.
- miss_cg = CallGenerator::for_virtual_call(call_method, vtable_index);
+ miss_cg = CallGenerator::for_virtual_call(callee, vtable_index);
}
if (miss_cg != NULL) {
if (next_hit_cg != NULL) {
@@ -252,11 +245,11 @@
// There was no special inlining tactic, or it bailed out.
// Use a more generic tactic, like a simple call.
if (call_is_virtual) {
- return CallGenerator::for_virtual_call(call_method, vtable_index);
+ return CallGenerator::for_virtual_call(callee, vtable_index);
} else {
// Class Hierarchy Analysis or Type Profile reveals a unique target,
// or it is a static or special call.
- return CallGenerator::for_direct_call(call_method, should_delay_inlining(call_method, jvms));
+ return CallGenerator::for_direct_call(callee, should_delay_inlining(callee, jvms));
}
}
@@ -355,33 +348,40 @@
// Find target being called
bool will_link;
- ciMethod* dest_method = iter().get_method(will_link);
- ciInstanceKlass* holder_klass = dest_method->holder();
+ ciMethod* bc_callee = iter().get_method(will_link); // actual callee from bytecode
+ ciInstanceKlass* holder_klass = bc_callee->holder();
ciKlass* holder = iter().get_declared_method_holder();
ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder);
- int nargs = dest_method->arg_size();
- if (is_invokedynamic) nargs -= 1;
-
// uncommon-trap when callee is unloaded, uninitialized or will not link
// bailout when too many arguments for register representation
- if (!will_link || can_not_compile_call_site(dest_method, klass)) {
+ if (!will_link || can_not_compile_call_site(bc_callee, klass)) {
#ifndef PRODUCT
if (PrintOpto && (Verbose || WizardMode)) {
method()->print_name(); tty->print_cr(" can not compile call at bci %d to:", bci());
- dest_method->print_name(); tty->cr();
+ bc_callee->print_name(); tty->cr();
}
#endif
return;
}
assert(holder_klass->is_loaded(), "");
- assert((dest_method->is_static() || is_invokedynamic) == !has_receiver , "must match bc");
+ //assert((bc_callee->is_static() || is_invokedynamic) == !has_receiver , "must match bc"); // XXX invokehandle (cur_bc_raw)
// Note: this takes into account invokeinterface of methods declared in java/lang/Object,
// which should be invokevirtuals but according to the VM spec may be invokeinterfaces
assert(holder_klass->is_interface() || holder_klass->super() == NULL || (bc() != Bytecodes::_invokeinterface), "must match bc");
// Note: In the absence of miranda methods, an abstract class K can perform
// an invokevirtual directly on an interface method I.m if K implements I.
+ const int nargs = bc_callee->arg_size();
+
+ // Push appendix argument (MethodType, CallSite, etc.), if one.
+ if (iter().has_appendix()) {
+ ciObject* appendix_arg = iter().get_appendix();
+ const TypeOopPtr* appendix_arg_type = TypeOopPtr::make_from_constant(appendix_arg);
+ Node* appendix_arg_node = _gvn.makecon(appendix_arg_type);
+ push(appendix_arg_node);
+ }
+
// ---------------------
// Does Class Hierarchy Analysis reveal only a single target of a v-call?
// Then we may inline or make a static call, but become dependent on there being only 1 target.
@@ -392,21 +392,21 @@
// Choose call strategy.
bool call_is_virtual = is_virtual_or_interface;
int vtable_index = methodOopDesc::invalid_vtable_index;
- ciMethod* call_method = dest_method;
+ ciMethod* callee = bc_callee;
// Try to get the most accurate receiver type
if (is_virtual_or_interface) {
Node* receiver_node = stack(sp() - nargs);
const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr();
- ciMethod* optimized_virtual_method = optimize_inlining(method(), bci(), klass, dest_method, receiver_type);
+ ciMethod* optimized_virtual_method = optimize_inlining(method(), bci(), klass, bc_callee, receiver_type);
// Have the call been sufficiently improved such that it is no longer a virtual?
if (optimized_virtual_method != NULL) {
- call_method = optimized_virtual_method;
+ callee = optimized_virtual_method;
call_is_virtual = false;
- } else if (!UseInlineCaches && is_virtual && call_method->is_loaded()) {
+ } else if (!UseInlineCaches && is_virtual && callee->is_loaded()) {
// We can make a vtable call at this site
- vtable_index = call_method->resolve_vtable_index(method()->holder(), klass);
+ vtable_index = callee->resolve_vtable_index(method()->holder(), klass);
}
}
@@ -416,22 +416,24 @@
bool try_inline = (C->do_inlining() || InlineAccessors);
// ---------------------
- inc_sp(- nargs); // Temporarily pop args for JVM state of call
+ dec_sp(nargs); // Temporarily pop args for JVM state of call
JVMState* jvms = sync_jvms();
// ---------------------
// Decide call tactic.
// This call checks with CHA, the interpreter profile, intrinsics table, etc.
// It decides whether inlining is desirable or not.
- CallGenerator* cg = C->call_generator(call_method, vtable_index, call_is_virtual, jvms, try_inline, prof_factor());
+ CallGenerator* cg = C->call_generator(callee, vtable_index, call_is_virtual, jvms, try_inline, prof_factor());
+
+ bc_callee = callee = NULL; // don't use bc_callee and callee after this point
// ---------------------
// Round double arguments before call
- round_double_arguments(dest_method);
+ round_double_arguments(cg->method());
#ifndef PRODUCT
// bump global counters for calls
- count_compiled_calls(false/*at_method_entry*/, cg->is_inline());
+ count_compiled_calls(/*at_method_entry*/ false, cg->is_inline());
// Record first part of parsing work for this call
parse_histogram()->record_change();
@@ -447,8 +449,8 @@
// because exceptions don't return to the call site.)
profile_call(receiver);
- JVMState* new_jvms;
- if ((new_jvms = cg->generate(jvms)) == NULL) {
+ JVMState* new_jvms = cg->generate(jvms);
+ if (new_jvms == NULL) {
// When inlining attempt fails (e.g., too many arguments),
// it may contaminate the current compile state, making it
// impossible to pull back and try again. Once we call
@@ -460,7 +462,7 @@
// the call site, perhaps because it did not match a pattern the
// intrinsic was expecting to optimize. Should always be possible to
// get a normal java call that may inline in that case
- cg = C->call_generator(call_method, vtable_index, call_is_virtual, jvms, try_inline, prof_factor(), /* allow_intrinsics= */ false);
+ cg = C->call_generator(cg->method(), vtable_index, call_is_virtual, jvms, try_inline, prof_factor(), /* allow_intrinsics= */ false);
if ((new_jvms = cg->generate(jvms)) == NULL) {
guarantee(failing(), "call failed to generate: calls should work");
return;
@@ -469,8 +471,8 @@
if (cg->is_inline()) {
// Accumulate has_loops estimate
- C->set_has_loops(C->has_loops() || call_method->has_loops());
- C->env()->notice_inlined_method(call_method);
+ C->set_has_loops(C->has_loops() || cg->method()->has_loops());
+ C->env()->notice_inlined_method(cg->method());
}
// Reset parser state from [new_]jvms, which now carries results of the call.
@@ -492,20 +494,74 @@
}
// Round double result after a call from strict to non-strict code
- round_double_result(dest_method);
+ round_double_result(cg->method());
+
+ ciType* rtype = cg->method()->return_type();
+ if (iter().cur_bc_raw() == Bytecodes::_invokehandle || is_invokedynamic) {
+ // Be careful here with return types.
+ ciType* ctype = iter().get_declared_method_signature()->return_type();
+ if (ctype != rtype) {
+ BasicType rt = rtype->basic_type();
+ BasicType ct = ctype->basic_type();
+ Node* retnode = peek();
+ if (ct == T_VOID) {
+ // It's OK for a method to return a value that is discarded.
+ // The discarding does not require any special action from the caller.
+ // The Java code knows this, at VerifyType.isNullConversion.
+ pop_node(rt); // whatever it was, pop it
+ retnode = top();
+ } else if (rt == T_INT || is_subword_type(rt)) {
+ // FIXME: This logic should be factored out.
+ if (ct == T_BOOLEAN) {
+ retnode = _gvn.transform( new (C, 3) AndINode(retnode, intcon(0x1)) );
+ } else if (ct == T_CHAR) {
+ retnode = _gvn.transform( new (C, 3) AndINode(retnode, intcon(0xFFFF)) );
+ } else if (ct == T_BYTE) {
+ retnode = _gvn.transform( new (C, 3) LShiftINode(retnode, intcon(24)) );
+ retnode = _gvn.transform( new (C, 3) RShiftINode(retnode, intcon(24)) );
+ } else if (ct == T_SHORT) {
+ retnode = _gvn.transform( new (C, 3) LShiftINode(retnode, intcon(16)) );
+ retnode = _gvn.transform( new (C, 3) RShiftINode(retnode, intcon(16)) );
+ } else {
+ assert(ct == T_INT, err_msg_res("rt=%s, ct=%s", type2name(rt), type2name(ct)));
+ }
+ } else if (rt == T_OBJECT || rt == T_ARRAY) {
+ assert(ct == T_OBJECT || ct == T_ARRAY, err_msg_res("rt=%s, ct=%s", type2name(rt), type2name(ct)));
+ if (ctype->is_loaded()) {
+ Node* if_fail = top();
+ retnode = gen_checkcast(retnode, makecon(TypeKlassPtr::make(ctype->as_klass())), &if_fail);
+ if (if_fail != top()) {
+ PreserveJVMState pjvms(this);
+ set_control(if_fail);
+ builtin_throw(Deoptimization::Reason_class_check);
+ }
+ pop();
+ push(retnode);
+ }
+ } else {
+ assert(ct == rt, err_msg_res("unexpected mismatch rt=%d, ct=%d", rt, ct));
+ // push a zero; it's better than getting an oop/int mismatch
+ retnode = pop_node(rt);
+ retnode = zerocon(ct);
+ push_node(ct, retnode);
+ }
+ // Now that the value is well-behaved, continue with the call-site type.
+ rtype = ctype;
+ }
+ }
// If the return type of the method is not loaded, assert that the
// value we got is a null. Otherwise, we need to recompile.
- if (!dest_method->return_type()->is_loaded()) {
+ if (!rtype->is_loaded()) {
#ifndef PRODUCT
if (PrintOpto && (Verbose || WizardMode)) {
method()->print_name(); tty->print_cr(" asserting nullness of result at bci: %d", bci());
- dest_method->print_name(); tty->cr();
+ cg->method()->print_name(); tty->cr();
}
#endif
if (C->log() != NULL) {
C->log()->elem("assert_null reason='return' klass='%d'",
- C->log()->identify(dest_method->return_type()));
+ C->log()->identify(rtype));
}
// If there is going to be a trap, put it at the next bytecode:
set_bci(iter().next_bci());
@@ -585,8 +641,8 @@
#ifndef PRODUCT
// We do not expect the same handler bci to take both cold unloaded
// and hot loaded exceptions. But, watch for it.
- if (extype->is_loaded()) {
- tty->print_cr("Warning: Handler @%d takes mixed loaded/unloaded exceptions in ");
+ if ((Verbose || WizardMode) && extype->is_loaded()) {
+ tty->print("Warning: Handler @%d takes mixed loaded/unloaded exceptions in ", bci());
method()->print_name(); tty->cr();
} else if (PrintOpto && (Verbose || WizardMode)) {
tty->print("Bailing out on unloaded exception type ");
--- a/hotspot/src/share/vm/opto/escape.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/escape.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1055,7 +1055,7 @@
C->log()->text("%s", (iterations >= CG_BUILD_ITER_LIMIT) ? "iterations" : "time");
C->log()->end_elem(" limit'");
}
- assert(false, err_msg("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d",
+ assert(false, err_msg_res("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d",
time.seconds(), iterations, nodes_size(), ptnodes_worklist.length()));
// Possible infinite build_connection_graph loop,
// bailout (no changes to ideal graph were made).
@@ -1768,8 +1768,12 @@
assert(ptadr->is_Field() && ptadr->ideal_node() == n, "sanity");
return;
}
+ bool unsafe = false;
+ bool is_oop = is_oop_field(n, offset, &unsafe);
+ if (unsafe) {
+ es = PointsToNode::GlobalEscape;
+ }
Compile* C = _compile;
- bool is_oop = is_oop_field(n, offset);
FieldNode* field = new (C->comp_arena()) FieldNode(C, n, es, offset, is_oop);
_nodes.at_put(n->_idx, field);
}
@@ -1794,7 +1798,7 @@
dst->set_arraycopy_dst();
}
-bool ConnectionGraph::is_oop_field(Node* n, int offset) {
+bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) {
const Type* adr_type = n->as_AddP()->bottom_type();
BasicType bt = T_INT;
if (offset == Type::OffsetBot) {
@@ -1813,7 +1817,16 @@
if (field != NULL) {
bt = field->layout_type();
} else {
- // Ignore non field load (for example, klass load)
+ // Check for unsafe oop field access
+ for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+ int opcode = n->fast_out(i)->Opcode();
+ if (opcode == Op_StoreP || opcode == Op_LoadP ||
+ opcode == Op_StoreN || opcode == Op_LoadN) {
+ bt = T_OBJECT;
+ (*unsafe) = true;
+ break;
+ }
+ }
}
} else if (adr_type->isa_aryptr()) {
if (offset == arrayOopDesc::length_offset_in_bytes()) {
@@ -1831,6 +1844,7 @@
if (opcode == Op_StoreP || opcode == Op_LoadP ||
opcode == Op_StoreN || opcode == Op_LoadN) {
bt = T_OBJECT;
+ break;
}
}
}
--- a/hotspot/src/share/vm/opto/escape.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/escape.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -512,13 +512,11 @@
assert(ptn != NULL, "node should be registered");
}
add_edge(ptnode_adr(n->_idx), ptn);
- }
-
+ }
// Helper functions
- bool is_oop_field(Node* n, int offset);
- static Node* get_addp_base(Node *addp);
- static Node* find_second_addp(Node* addp, Node* n);
-
+ bool is_oop_field(Node* n, int offset, bool* unsafe);
+ static Node* get_addp_base(Node *addp);
+ static Node* find_second_addp(Node* addp, Node* n);
// offset of a field reference
int address_offset(Node* adr, PhaseTransform *phase);
--- a/hotspot/src/share/vm/opto/graphKit.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -965,7 +965,7 @@
assert(call->jvms()->debug_depth() == call->req() - non_debug_edges, "");
}
-bool GraphKit::compute_stack_effects(int& inputs, int& depth) {
+bool GraphKit::compute_stack_effects(int& inputs, int& depth, bool for_parse) {
Bytecodes::Code code = java_bc();
if (code == Bytecodes::_wide) {
code = method()->java_code_at_bci(bci() + 1);
@@ -1032,12 +1032,21 @@
ciBytecodeStream iter(method());
iter.reset_to_bci(bci());
iter.next();
- ciMethod* method = iter.get_method(ignore);
+ ciMethod* callee = iter.get_method(ignore);
// (Do not use ciMethod::arg_size(), because
// it might be an unloaded method, which doesn't
// know whether it is static or not.)
- inputs = method->invoke_arg_size(code);
- int size = method->return_type()->size();
+ if (for_parse) {
+ // Case 1: When called from parse we are *before* the invoke (in the
+ // caller) and need to to adjust the inputs by an appendix
+ // argument that will be pushed implicitly.
+ inputs = callee->invoke_arg_size(code) - (iter.has_appendix() ? 1 : 0);
+ } else {
+ // Case 2: Here we are *after* the invoke (in the callee) and need to
+ // remove any appendix arguments that were popped.
+ inputs = callee->invoke_arg_size(code) - (callee->has_member_arg() ? 1 : 0);
+ }
+ int size = callee->return_type()->size();
depth = size - inputs;
}
break;
@@ -1373,7 +1382,6 @@
}
-
//=============================================================================
//--------------------------------memory---------------------------------------
Node* GraphKit::memory(uint alias_idx) {
--- a/hotspot/src/share/vm/opto/graphKit.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/graphKit.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -145,6 +145,7 @@
void clean_stack(int from_sp); // clear garbage beyond from_sp to top
void inc_sp(int i) { set_sp(sp() + i); }
+ void dec_sp(int i) { set_sp(sp() - i); }
void set_bci(int bci) { _bci = bci; }
// Make sure jvms has current bci & sp.
@@ -285,7 +286,7 @@
// How many stack inputs does the current BC consume?
// And, how does the stack change after the bytecode?
// Returns false if unknown.
- bool compute_stack_effects(int& inputs, int& depth);
+ bool compute_stack_effects(int& inputs, int& depth, bool for_parse = false);
// Add a fixed offset to a pointer
Node* basic_plus_adr(Node* base, Node* ptr, intptr_t offset) {
@@ -370,9 +371,9 @@
// Replace all occurrences of one node by another.
void replace_in_map(Node* old, Node* neww);
- void push(Node* n) { map_not_null(); _map->set_stack(_map->_jvms,_sp++,n); }
- Node* pop() { map_not_null(); return _map->stack(_map->_jvms,--_sp); }
- Node* peek(int off=0) { map_not_null(); return _map->stack(_map->_jvms, _sp - off - 1); }
+ void push(Node* n) { map_not_null(); _map->set_stack(_map->_jvms, _sp++, n); }
+ Node* pop() { map_not_null(); return _map->stack( _map->_jvms, --_sp); }
+ Node* peek(int off = 0) { map_not_null(); return _map->stack( _map->_jvms, _sp - off - 1); }
void push_pair(Node* ldval) {
push(ldval);
--- a/hotspot/src/share/vm/opto/idealGraphPrinter.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -155,8 +155,8 @@
} else {
// It would be nice if we could shut down cleanly but it should
// be an error if we can't connect to the visualizer.
- fatal(err_msg("Couldn't connect to visualizer at %s:%d",
- PrintIdealGraphAddress, PrintIdealGraphPort));
+ fatal(err_msg_res("Couldn't connect to visualizer at %s:%d",
+ PrintIdealGraphAddress, PrintIdealGraphPort));
}
}
--- a/hotspot/src/share/vm/opto/library_call.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/library_call.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -2171,7 +2171,7 @@
if (id == vmIntrinsics::_reverseBytes_l && !Matcher::has_match_rule(Op_ReverseBytesL)) return false;
if (id == vmIntrinsics::_reverseBytes_c && !Matcher::has_match_rule(Op_ReverseBytesUS)) return false;
if (id == vmIntrinsics::_reverseBytes_s && !Matcher::has_match_rule(Op_ReverseBytesS)) return false;
- _sp += arg_size(); // restore stack pointer
+ _sp += arg_size(); // restore stack pointer
switch (id) {
case vmIntrinsics::_reverseBytes_i:
push(_gvn.transform(new (C, 2) ReverseBytesINode(0, pop())));
@@ -2344,6 +2344,7 @@
// Argument words: "this" plus (oop/offset) or (lo/hi) args plus maybe 1 or 2 value words
int nargs = 1 + (is_native_ptr ? 2 : 3) + (is_store ? type_words : 0);
+ assert(callee()->arg_size() == nargs, "must be");
debug_only(int saved_sp = _sp);
_sp += nargs;
@@ -4047,7 +4048,8 @@
}
}
}
- else if (method->is_method_handle_adapter()) {
+ else if (method->is_method_handle_intrinsic() ||
+ method->is_compiled_lambda_form()) {
// This is an internal adapter frame from the MethodHandleCompiler -- skip it
return true;
}
--- a/hotspot/src/share/vm/opto/matcher.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/matcher.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1283,8 +1283,9 @@
if (is_method_handle_invoke) {
// Kill some extra stack space in case method handles want to do
// a little in-place argument insertion.
+ // FIXME: Is this still necessary?
int regs_per_word = NOT_LP64(1) LP64_ONLY(2); // %%% make a global const!
- out_arg_limit_per_call += MethodHandlePushLimit * regs_per_word;
+ out_arg_limit_per_call += methodOopDesc::extra_stack_entries() * regs_per_word;
// Do not update mcall->_argsize because (a) the extra space is not
// pushed as arguments and (b) _argsize is dead (not used anywhere).
}
--- a/hotspot/src/share/vm/opto/node.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/node.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -363,7 +363,7 @@
#endif
// Reference to the i'th input Node. Error if out of bounds.
- Node* in(uint i) const { assert(i < _max,"oob"); return _in[i]; }
+ Node* in(uint i) const { assert(i < _max, err_msg_res("oob: i=%d, _max=%d", i, _max)); return _in[i]; }
// Reference to the i'th output Node. Error if out of bounds.
// Use this accessor sparingly. We are going trying to use iterators instead.
Node* raw_out(uint i) const { assert(i < _outcnt,"oob"); return _out[i]; }
@@ -394,7 +394,7 @@
void ins_req( uint i, Node *n ); // Insert a NEW required input
void set_req( uint i, Node *n ) {
assert( is_not_dead(n), "can not use dead node");
- assert( i < _cnt, "oob");
+ assert( i < _cnt, err_msg_res("oob: i=%d, _cnt=%d", i, _cnt));
assert( !VerifyHashTableKeys || _hash_lock == 0,
"remove node from hash table before modifying it");
Node** p = &_in[i]; // cache this._in, across the del_out call
--- a/hotspot/src/share/vm/opto/parse.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/parse.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -84,7 +84,7 @@
static const char* check_can_parse(ciMethod* callee);
static InlineTree* build_inline_tree_root();
- static InlineTree* find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee, bool create_if_not_found = false);
+ static InlineTree* find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee);
// For temporary (stack-allocated, stateless) ilts:
InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio, int max_inline_level);
--- a/hotspot/src/share/vm/opto/parse1.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/parse1.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -398,7 +398,7 @@
if (PrintCompilation || PrintOpto) {
// Make sure I have an inline tree, so I can print messages about it.
JVMState* ilt_caller = is_osr_parse() ? caller->caller() : caller;
- InlineTree::find_subtree_from_root(C->ilt(), ilt_caller, parse_method, true);
+ InlineTree::find_subtree_from_root(C->ilt(), ilt_caller, parse_method);
}
_max_switch_depth = 0;
_est_switch_depth = 0;
@@ -1398,8 +1398,8 @@
#ifdef ASSERT
int pre_bc_sp = sp();
int inputs, depth;
- bool have_se = !stopped() && compute_stack_effects(inputs, depth);
- assert(!have_se || pre_bc_sp >= inputs, "have enough stack to execute this BC");
+ bool have_se = !stopped() && compute_stack_effects(inputs, depth, /*for_parse*/ true);
+ assert(!have_se || pre_bc_sp >= inputs, err_msg_res("have enough stack to execute this BC: pre_bc_sp=%d, inputs=%d", pre_bc_sp, inputs));
#endif //ASSERT
do_one_bytecode();
--- a/hotspot/src/share/vm/opto/phaseX.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/opto/phaseX.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -193,6 +193,7 @@
// If you want the type of a very new (untransformed) node,
// you must use type_or_null, and test the result for NULL.
const Type* type(const Node* n) const {
+ assert(n != NULL, "must not be null");
const Type* t = _types.fast_lookup(n->_idx);
assert(t != NULL, "must set before get");
return t;
--- a/hotspot/src/share/vm/prims/jvm.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/jvm.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -2241,7 +2241,7 @@
klassOop k = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls));
k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
oop method = instanceKlass::cast(k)->methods()->obj_at(method_index);
- return methodOop(method)->max_stack();
+ return methodOop(method)->verifier_max_stack();
JVM_END
--- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3164,9 +3164,6 @@
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 Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2086 +0,0 @@
-/*
- * Copyright (c) 2008, 2012, 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 "interpreter/rewriter.hpp"
-#include "memory/oopFactory.hpp"
-#include "prims/methodHandleWalk.hpp"
-
-/*
- * 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
-
-void MethodHandleChain::set_method_handle(Handle mh, TRAPS) {
- if (!java_lang_invoke_MethodHandle::is_instance(mh())) lose("bad method handle", CHECK);
-
- // set current method handle and unpack partially
- _method_handle = mh;
- _is_last = false;
- _is_bound = false;
- _arg_slot = -1;
- _arg_type = T_VOID;
- _conversion = -1;
- _last_invoke = Bytecodes::_nop; //arbitrary non-garbage
-
- if (java_lang_invoke_DirectMethodHandle::is_instance(mh())) {
- set_last_method(mh(), THREAD);
- return;
- }
- if (java_lang_invoke_AdapterMethodHandle::is_instance(mh())) {
- _conversion = AdapterMethodHandle_conversion();
- assert(_conversion != -1, "bad conv value");
- assert(java_lang_invoke_BoundMethodHandle::is_instance(mh()), "also BMH");
- }
- if (java_lang_invoke_BoundMethodHandle::is_instance(mh())) {
- if (!is_adapter()) // keep AMH and BMH separate in this model
- _is_bound = true;
- _arg_slot = BoundMethodHandle_vmargslot();
- oop target = MethodHandle_vmtarget_oop();
- if (!is_bound() || java_lang_invoke_MethodHandle::is_instance(target)) {
- _arg_type = compute_bound_arg_type(target, NULL, _arg_slot, CHECK);
- } else if (target != NULL && target->is_method()) {
- methodOop m = (methodOop) target;
- _arg_type = compute_bound_arg_type(NULL, m, _arg_slot, CHECK);
- set_last_method(mh(), CHECK);
- } else {
- _is_bound = false; // lose!
- }
- }
- if (is_bound() && _arg_type == T_VOID) {
- lose("bad vmargslot", CHECK);
- }
- if (!is_bound() && !is_adapter()) {
- lose("unrecognized MH type", CHECK);
- }
-}
-
-
-void MethodHandleChain::set_last_method(oop target, TRAPS) {
- _is_last = true;
- KlassHandle receiver_limit; int flags = 0;
- _last_method = MethodHandles::decode_method(target, receiver_limit, flags);
- if ((flags & MethodHandles::_dmf_has_receiver) == 0)
- _last_invoke = Bytecodes::_invokestatic;
- else if ((flags & MethodHandles::_dmf_does_dispatch) == 0)
- _last_invoke = Bytecodes::_invokespecial;
- else if ((flags & MethodHandles::_dmf_from_interface) != 0)
- _last_invoke = Bytecodes::_invokeinterface;
- else
- _last_invoke = Bytecodes::_invokevirtual;
-}
-
-
-BasicType MethodHandleChain::compute_bound_arg_type(oop target, methodOop m, int arg_slot, TRAPS) {
- // There is no direct indication of whether the argument is primitive or not.
- // It is implied by the _vmentry code, and by the MethodType of the target.
- BasicType arg_type = T_VOID;
- if (target != NULL) {
- oop mtype = java_lang_invoke_MethodHandle::type(target);
- int arg_num = MethodHandles::argument_slot_to_argnum(mtype, arg_slot);
- if (arg_num >= 0) {
- oop ptype = java_lang_invoke_MethodType::ptype(mtype, arg_num);
- arg_type = java_lang_Class::as_BasicType(ptype);
- }
- } else if (m != NULL) {
- // figure out the argument type from the slot
- // FIXME: make this explicit in the MH
- int cur_slot = m->size_of_parameters();
- if (arg_slot >= cur_slot)
- return T_VOID;
- if (!m->is_static()) {
- cur_slot -= type2size[T_OBJECT];
- if (cur_slot == arg_slot)
- return T_OBJECT;
- }
- ResourceMark rm(THREAD);
- for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) {
- BasicType bt = ss.type();
- cur_slot -= type2size[bt];
- if (cur_slot <= arg_slot) {
- if (cur_slot == arg_slot)
- arg_type = bt;
- break;
- }
- }
- }
- if (arg_type == T_ARRAY)
- arg_type = T_OBJECT;
- return arg_type;
-}
-
-
-void MethodHandleChain::lose(const char* msg, TRAPS) {
- _lose_message = msg;
-#ifdef ASSERT
- if (Verbose) {
- tty->print_cr(INTPTR_FORMAT " lose: %s", _method_handle(), msg);
- print();
- }
-#endif
- if (!THREAD->is_Java_thread() || ((JavaThread*)THREAD)->thread_state() != _thread_in_vm) {
- // throw a preallocated exception
- THROW_OOP(Universe::virtual_machine_error_instance());
- }
- THROW_MSG(vmSymbols::java_lang_InternalError(), msg);
-}
-
-
-#ifdef ASSERT
-static const char* adapter_ops[] = {
- "retype_only" ,
- "retype_raw" ,
- "check_cast" ,
- "prim_to_prim" ,
- "ref_to_prim" ,
- "prim_to_ref" ,
- "swap_args" ,
- "rot_args" ,
- "dup_args" ,
- "drop_args" ,
- "collect_args" ,
- "spread_args" ,
- "fold_args"
-};
-
-static const char* adapter_op_to_string(int op) {
- if (op >= 0 && op < (int)ARRAY_SIZE(adapter_ops))
- return adapter_ops[op];
- return "unknown_op";
-}
-
-void MethodHandleChain::print(oopDesc* m) {
- HandleMark hm;
- ResourceMark rm;
- Handle mh(m);
- EXCEPTION_MARK;
- MethodHandleChain mhc(mh, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- oop ex = THREAD->pending_exception();
- CLEAR_PENDING_EXCEPTION;
- ex->print();
- return;
- }
- mhc.print();
-}
-
-
-void MethodHandleChain::print() {
- EXCEPTION_MARK;
- print_impl(THREAD);
- if (HAS_PENDING_EXCEPTION) {
- oop ex = THREAD->pending_exception();
- CLEAR_PENDING_EXCEPTION;
- ex->print();
- }
-}
-
-void MethodHandleChain::print_impl(TRAPS) {
- ResourceMark rm;
-
- MethodHandleChain chain(_root, CHECK);
- for (;;) {
- tty->print(INTPTR_FORMAT ": ", chain.method_handle()());
- if (chain.is_bound()) {
- tty->print("bound: arg_type %s arg_slot %d",
- type2name(chain.bound_arg_type()),
- chain.bound_arg_slot());
- oop o = chain.bound_arg_oop();
- if (o != NULL) {
- if (o->is_instance()) {
- tty->print(" instance %s", o->klass()->klass_part()->internal_name());
- if (java_lang_invoke_CountingMethodHandle::is_instance(o)) {
- tty->print(" vmcount: %d", java_lang_invoke_CountingMethodHandle::vmcount(o));
- }
- } else {
- o->print();
- }
- }
- oop vmt = chain.vmtarget_oop();
- if (vmt != NULL) {
- if (vmt->is_method()) {
- tty->print(" ");
- methodOop(vmt)->print_short_name(tty);
- } else if (java_lang_invoke_MethodHandle::is_instance(vmt)) {
- tty->print(" method handle " INTPTR_FORMAT, vmt);
- } else {
- ShouldNotReachHere();
- }
- }
- } else if (chain.is_adapter()) {
- tty->print("adapter: arg_slot %d conversion op %s",
- chain.adapter_arg_slot(),
- adapter_op_to_string(chain.adapter_conversion_op()));
- switch (chain.adapter_conversion_op()) {
- case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
- if (java_lang_invoke_CountingMethodHandle::is_instance(chain.method_handle_oop())) {
- tty->print(" vmcount: %d", java_lang_invoke_CountingMethodHandle::vmcount(chain.method_handle_oop()));
- }
- case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW:
- case java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST:
- case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM:
- case java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM:
- break;
-
- case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF: {
- tty->print(" src_type = %s", type2name(chain.adapter_conversion_src_type()));
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS:
- case java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS: {
- int dest_arg_slot = chain.adapter_conversion_vminfo();
- tty->print(" dest_arg_slot %d type %s", dest_arg_slot, type2name(chain.adapter_conversion_src_type()));
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS:
- case java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS: {
- int dup_slots = chain.adapter_conversion_stack_pushes();
- tty->print(" pushes %d", dup_slots);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS:
- case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: {
- int coll_slots = chain.MethodHandle_vmslots();
- tty->print(" coll_slots %d", coll_slots);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS: {
- // Check the required length.
- int spread_slots = 1 + chain.adapter_conversion_stack_pushes();
- tty->print(" spread_slots %d", spread_slots);
- break;
- }
-
- default:
- tty->print_cr("bad adapter conversion");
- break;
- }
- } else {
- // DMH
- tty->print("direct: ");
- chain.last_method_oop()->print_short_name(tty);
- }
-
- tty->print(" (");
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(chain.method_type_oop());
- for (int i = ptypes->length() - 1; i >= 0; i--) {
- BasicType t = java_lang_Class::as_BasicType(ptypes->obj_at(i));
- if (t == T_ARRAY) t = T_OBJECT;
- tty->print("%c", type2char(t));
- if (t == T_LONG || t == T_DOUBLE) tty->print("_");
- }
- tty->print(")");
- BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(chain.method_type_oop()));
- if (rtype == T_ARRAY) rtype = T_OBJECT;
- tty->print("%c", type2char(rtype));
- tty->cr();
- if (!chain.is_last()) {
- chain.next(CHECK);
- } else {
- break;
- }
- }
-}
-#endif
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleWalker
-
-Bytecodes::Code MethodHandleWalker::conversion_code(BasicType src, BasicType dest) {
- if (is_subword_type(src)) {
- src = T_INT; // all subword src types act like int
- }
- if (src == dest) {
- return Bytecodes::_nop;
- }
-
-#define SRC_DEST(s,d) (((int)(s) << 4) + (int)(d))
- switch (SRC_DEST(src, dest)) {
- case SRC_DEST(T_INT, T_LONG): return Bytecodes::_i2l;
- case SRC_DEST(T_INT, T_FLOAT): return Bytecodes::_i2f;
- case SRC_DEST(T_INT, T_DOUBLE): return Bytecodes::_i2d;
- case SRC_DEST(T_INT, T_BYTE): return Bytecodes::_i2b;
- case SRC_DEST(T_INT, T_CHAR): return Bytecodes::_i2c;
- case SRC_DEST(T_INT, T_SHORT): return Bytecodes::_i2s;
-
- case SRC_DEST(T_LONG, T_INT): return Bytecodes::_l2i;
- case SRC_DEST(T_LONG, T_FLOAT): return Bytecodes::_l2f;
- case SRC_DEST(T_LONG, T_DOUBLE): return Bytecodes::_l2d;
-
- case SRC_DEST(T_FLOAT, T_INT): return Bytecodes::_f2i;
- case SRC_DEST(T_FLOAT, T_LONG): return Bytecodes::_f2l;
- case SRC_DEST(T_FLOAT, T_DOUBLE): return Bytecodes::_f2d;
-
- case SRC_DEST(T_DOUBLE, T_INT): return Bytecodes::_d2i;
- case SRC_DEST(T_DOUBLE, T_LONG): return Bytecodes::_d2l;
- case SRC_DEST(T_DOUBLE, T_FLOAT): return Bytecodes::_d2f;
- }
-#undef SRC_DEST
-
- // cannot do it in one step, or at all
- return Bytecodes::_illegal;
-}
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleWalker::walk
-//
-MethodHandleWalker::ArgToken
-MethodHandleWalker::walk(TRAPS) {
- ArgToken empty = ArgToken(); // Empty return value.
-
- walk_incoming_state(CHECK_(empty));
-
- for (;;) {
- set_method_handle(chain().method_handle_oop());
-
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
-
- if (chain().is_adapter()) {
- int conv_op = chain().adapter_conversion_op();
- int arg_slot = chain().adapter_arg_slot();
-
- // Check that the arg_slot is valid. In most cases it must be
- // within range of the current arguments but there are some
- // exceptions. Those are sanity checked in their implemention
- // below.
- if ((arg_slot < 0 || arg_slot >= _outgoing.length()) &&
- conv_op > java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW &&
- conv_op != java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS &&
- conv_op != java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS) {
- lose(err_msg("bad argument index %d", arg_slot), CHECK_(empty));
- }
-
- bool retain_original_args = false; // used by fold/collect logic
-
- // perform the adapter action
- switch (conv_op) {
- case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
- // No changes to arguments; pass the bits through.
- break;
-
- case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW: {
- // 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());
- 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--) {
- if (arg_type(slot) == T_VOID) continue;
-
- 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.
- }
-
- // 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;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST: {
- // checkcast the Nth outgoing argument in place
- klassOop dest_klass = NULL;
- BasicType dest = java_lang_Class::as_BasicType(chain().adapter_arg_oop(), &dest_klass);
- assert(dest == T_OBJECT, "");
- ArgToken arg = _outgoing.at(arg_slot);
- assert(dest == arg.basic_type(), "");
- arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty));
- // replace the object by the result of the cast, to make the compiler happy:
- change_argument(T_OBJECT, arg_slot, T_OBJECT, arg);
- debug_only(dest_klass = (klassOop)badOop);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM: {
- // i2l, etc., on the Nth outgoing argument in place
- BasicType src = chain().adapter_conversion_src_type(),
- dest = chain().adapter_conversion_dest_type();
- ArgToken arg = _outgoing.at(arg_slot);
- Bytecodes::Code bc = conversion_code(src, dest);
- if (bc == Bytecodes::_nop) {
- break;
- } else if (bc != Bytecodes::_illegal) {
- arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
- } else if (is_subword_type(dest)) {
- bc = conversion_code(src, T_INT);
- if (bc != Bytecodes::_illegal) {
- arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
- bc = conversion_code(T_INT, dest);
- arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
- }
- }
- if (bc == Bytecodes::_illegal) {
- lose(err_msg("bad primitive conversion for %s -> %s", type2name(src), type2name(dest)), CHECK_(empty));
- }
- change_argument(src, arg_slot, dest, arg);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM: {
- // checkcast to wrapper type & call intValue, etc.
- BasicType dest = chain().adapter_conversion_dest_type();
- ArgToken arg = _outgoing.at(arg_slot);
- arg = make_conversion(T_OBJECT, SystemDictionary::box_klass(dest),
- Bytecodes::_checkcast, arg, CHECK_(empty));
- vmIntrinsics::ID unboxer = vmIntrinsics::for_unboxing(dest);
- if (unboxer == vmIntrinsics::_none) {
- lose("no unboxing method", CHECK_(empty));
- }
- ArgToken arglist[2];
- arglist[0] = arg; // outgoing 'this'
- arglist[1] = ArgToken(); // sentinel
- arg = make_invoke(methodHandle(), unboxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty));
- change_argument(T_OBJECT, arg_slot, dest, arg);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF: {
- // call wrapper type.valueOf
- BasicType src = chain().adapter_conversion_src_type();
- vmIntrinsics::ID boxer = vmIntrinsics::for_boxing(src);
- if (boxer == vmIntrinsics::_none) {
- lose("no boxing method", CHECK_(empty));
- }
- ArgToken arg = _outgoing.at(arg_slot);
- ArgToken arglist[2];
- arglist[0] = arg; // outgoing value
- arglist[1] = ArgToken(); // sentinel
- arg = make_invoke(methodHandle(), boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
- change_argument(src, arg_slot, T_OBJECT, arg);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS: {
- int dest_arg_slot = chain().adapter_conversion_vminfo();
- if (!has_argument(dest_arg_slot)) {
- lose("bad swap index", CHECK_(empty));
- }
- // a simple swap between two arguments
- if (arg_slot > dest_arg_slot) {
- int tmp = arg_slot;
- arg_slot = dest_arg_slot;
- dest_arg_slot = tmp;
- }
- ArgToken a1 = _outgoing.at(arg_slot);
- ArgToken a2 = _outgoing.at(dest_arg_slot);
- change_argument(a2.basic_type(), dest_arg_slot, a1);
- change_argument(a1.basic_type(), arg_slot, a2);
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS: {
- int limit_raw = chain().adapter_conversion_vminfo();
- bool rot_down = (arg_slot < limit_raw);
- int limit_bias = (rot_down ? MethodHandles::OP_ROT_ARGS_DOWN_LIMIT_BIAS : 0);
- int limit_slot = limit_raw - limit_bias;
- if ((uint)limit_slot > (uint)_outgoing.length()) {
- lose("bad rotate index", CHECK_(empty));
- }
- // Rotate the source argument (plus following N slots) into the
- // position occupied by the dest argument (plus following N slots).
- int rotate_count = type2size[chain().adapter_conversion_src_type()];
- // (no other rotate counts are currently supported)
- if (rot_down) {
- for (int i = 0; i < rotate_count; i++) {
- ArgToken temp = _outgoing.at(arg_slot);
- _outgoing.remove_at(arg_slot);
- _outgoing.insert_before(limit_slot - 1, temp);
- }
- } else { // arg_slot > limit_slot => rotate_up
- for (int i = 0; i < rotate_count; i++) {
- ArgToken temp = _outgoing.at(arg_slot + rotate_count - 1);
- _outgoing.remove_at(arg_slot + rotate_count - 1);
- _outgoing.insert_before(limit_slot, temp);
- }
- }
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS: {
- int dup_slots = chain().adapter_conversion_stack_pushes();
- if (dup_slots <= 0) {
- lose("bad dup count", CHECK_(empty));
- }
- for (int i = 0; i < dup_slots; i++) {
- ArgToken dup = _outgoing.at(arg_slot + 2*i);
- if (dup.basic_type() != T_VOID) _outgoing_argc += 1;
- _outgoing.insert_before(i, dup);
- }
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS: {
- int drop_slots = -chain().adapter_conversion_stack_pushes();
- if (drop_slots <= 0) {
- lose("bad drop count", CHECK_(empty));
- }
- for (int i = 0; i < drop_slots; i++) {
- ArgToken drop = _outgoing.at(arg_slot);
- if (drop.basic_type() != T_VOID) _outgoing_argc -= 1;
- _outgoing.remove_at(arg_slot);
- }
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
- break;
- }
-
- 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--) {
- ArgToken arg_state = _outgoing.at(slot);
- BasicType arg_type = arg_state.basic_type();
- if (arg_type == T_VOID) continue;
- ArgToken arg = _outgoing.at(slot);
- 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));
- i++;
- }
- 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(methodHandle(THREAD, methodOop(invoker)), vmIntrinsics::_invokeGeneric,
- Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty));
- // The iid = _invokeGeneric really means to adjust reference types as needed.
- 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));
- }
- }
- if (rtype != T_VOID) {
- int ret_slot = arg_slot + (retain_original_args ? coll_slots : 0);
- change_argument(T_VOID, ret_slot, rtype, ret);
- }
- break;
- }
-
- case java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS: {
- klassOop array_klass_oop = NULL;
- BasicType array_type = java_lang_Class::as_BasicType(chain().adapter_arg_oop(),
- &array_klass_oop);
- assert(array_type == T_OBJECT, "");
- assert(Klass::cast(array_klass_oop)->oop_is_array(), "");
- arrayKlassHandle array_klass(THREAD, array_klass_oop);
- debug_only(array_klass_oop = (klassOop)badOop);
-
- klassOop element_klass_oop = NULL;
- BasicType element_type = java_lang_Class::as_BasicType(array_klass->component_mirror(),
- &element_klass_oop);
- KlassHandle element_klass(THREAD, element_klass_oop);
- debug_only(element_klass_oop = (klassOop)badOop);
-
- // Fetch the argument, which we will cast to the required array type.
- ArgToken arg = _outgoing.at(arg_slot);
- assert(arg.basic_type() == T_OBJECT, "");
- ArgToken array_arg = arg;
- array_arg = make_conversion(T_OBJECT, array_klass(), Bytecodes::_checkcast, array_arg, CHECK_(empty));
- change_argument(T_OBJECT, arg_slot, T_VOID, ArgToken(tt_void));
-
- // Check the required length.
- int spread_slots = 1 + chain().adapter_conversion_stack_pushes();
- int spread_length = spread_slots;
- if (type2size[element_type] == 2) {
- if (spread_slots % 2 != 0) spread_slots = -1; // force error
- spread_length = spread_slots / 2;
- }
- if (spread_slots < 0) {
- lose("bad spread length", CHECK_(empty));
- }
-
- jvalue length_jvalue; length_jvalue.i = spread_length;
- ArgToken length_arg = make_prim_constant(T_INT, &length_jvalue, CHECK_(empty));
- // Call a built-in method known to the JVM to validate the length.
- ArgToken arglist[3];
- arglist[0] = array_arg; // value to check
- arglist[1] = length_arg; // length to check
- arglist[2] = ArgToken(); // sentinel
- make_invoke(methodHandle(), vmIntrinsics::_checkSpreadArgument,
- Bytecodes::_invokestatic, false, 2, &arglist[0], CHECK_(empty));
-
- // Spread out the array elements.
- 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++) {
- jvalue offset_jvalue; offset_jvalue.i = i;
- ArgToken offset_arg = make_prim_constant(T_INT, &offset_jvalue, CHECK_(empty));
- ArgToken element_arg = make_fetch(element_type, element_klass(), aload_op, array_arg, offset_arg, CHECK_(empty));
- change_argument(T_VOID, ap, element_type, element_arg);
- //ap += type2size[element_type]; // don't do this; insert next arg to *right* of previous
- }
- break;
- }
-
- default:
- lose("bad adapter conversion", CHECK_(empty));
- break;
- }
- }
-
- if (chain().is_bound()) {
- // push a new argument
- BasicType arg_type = chain().bound_arg_type();
- jint arg_slot = chain().bound_arg_slot();
- oop arg_oop = chain().bound_arg_oop();
- ArgToken arg;
- if (arg_type == T_OBJECT) {
- arg = make_oop_constant(arg_oop, CHECK_(empty));
- } else {
- jvalue arg_value;
- BasicType bt = java_lang_boxing_object::get_value(arg_oop, &arg_value);
- if (bt == arg_type || (bt == T_INT && is_subword_type(arg_type))) {
- arg = make_prim_constant(arg_type, &arg_value, CHECK_(empty));
- } else {
- lose(err_msg("bad bound value: arg_type %s boxing %s", type2name(arg_type), type2name(bt)), CHECK_(empty));
- }
- }
- DEBUG_ONLY(arg_oop = badOop);
- change_argument(T_VOID, arg_slot, arg_type, arg);
- }
-
- // this test must come after the body of the loop
- if (!chain().is_last()) {
- chain().next(CHECK_(empty));
- } else {
- break;
- }
- }
-
- // finish the sequence with a tail-call to the ultimate target
- // parameters are passed in logical order (recv 1st), not slot order
- ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, _outgoing.length() + 1);
- int ap = 0;
- for (int i = _outgoing.length() - 1; i >= 0; i--) {
- ArgToken arg_state = _outgoing.at(i);
- if (arg_state.basic_type() == T_VOID) continue;
- arglist[ap++] = _outgoing.at(i);
- }
- assert(ap == _outgoing_argc, "");
- arglist[ap] = ArgToken(); // add a sentinel, for the sake of asserts
- return make_invoke(chain().last_method(),
- vmIntrinsics::_none,
- chain().last_invoke_code(), true,
- ap, arglist, THREAD);
-}
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleWalker::walk_incoming_state
-//
-void MethodHandleWalker::walk_incoming_state(TRAPS) {
- Handle mtype(THREAD, chain().method_type_oop());
- int nptypes = java_lang_invoke_MethodType::ptype_count(mtype());
- _outgoing_argc = nptypes;
- int argp = nptypes - 1;
- if (argp >= 0) {
- _outgoing.at_grow(argp, ArgToken(tt_void)); // presize
- }
- 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);
- 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);
- _outgoing.at_put(argp, arg);
- if (type2size[arg_type] == 2) {
- // add the extra slot, so we can model the JVM stack
- _outgoing.insert_before(argp+1, ArgToken(tt_void));
- }
- --argp;
- }
- // 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);
- ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK);
- // ignore ret; client can catch it if needed
-
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
-
- verify_args_and_signature(CHECK);
-}
-
-
-#ifdef ASSERT
-void MethodHandleWalker::verify_args_and_signature(TRAPS) {
- int index = _outgoing.length() - 1;
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(chain().method_type_oop());
- for (int i = 0, limit = ptypes->length(); i < limit; i++) {
- BasicType t = java_lang_Class::as_BasicType(ptypes->obj_at(i));
- if (t == T_ARRAY) t = T_OBJECT;
- if (t == T_LONG || t == T_DOUBLE) {
- assert(T_VOID == _outgoing.at(index).basic_type(), "types must match");
- index--;
- }
- assert(t == _outgoing.at(index).basic_type(), "types must match");
- index--;
- }
-}
-#endif
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleWalker::change_argument
-//
-// This is messy because some kinds of arguments are paired with
-// companion slots containing an empty value.
-void MethodHandleWalker::change_argument(BasicType old_type, int slot, const ArgToken& new_arg) {
- BasicType new_type = new_arg.basic_type();
- int old_size = type2size[old_type];
- int new_size = type2size[new_type];
- if (old_size == new_size) {
- // simple case first
- _outgoing.at_put(slot, new_arg);
- } else if (old_size > new_size) {
- for (int i = old_size - 1; i >= new_size; i--) {
- assert((i != 0) == (_outgoing.at(slot + i).basic_type() == T_VOID), "");
- _outgoing.remove_at(slot + i);
- }
- if (new_size > 0)
- _outgoing.at_put(slot, new_arg);
- else
- _outgoing_argc -= 1; // deleted a real argument
- } else {
- for (int i = old_size; i < new_size; i++) {
- _outgoing.insert_before(slot + i, ArgToken(tt_void));
- }
- _outgoing.at_put(slot, new_arg);
- if (old_size == 0)
- _outgoing_argc += 1; // inserted a real argument
- }
- assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
-}
-
-
-#ifdef ASSERT
-int MethodHandleWalker::argument_count_slow() {
- int args_seen = 0;
- for (int i = _outgoing.length() - 1; i >= 0; i--) {
- if (_outgoing.at(i).basic_type() != T_VOID) {
- ++args_seen;
- if (_outgoing.at(i).basic_type() == T_LONG ||
- _outgoing.at(i).basic_type() == T_DOUBLE) {
- assert(_outgoing.at(i + 1).basic_type() == T_VOID, "should only follow two word");
- }
- } else {
- assert(_outgoing.at(i - 1).basic_type() == T_LONG ||
- _outgoing.at(i - 1).basic_type() == T_DOUBLE, "should only follow two word");
- }
- }
- return args_seen;
-}
-#endif
-
-
-// -----------------------------------------------------------------------------
-// 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)) {
- 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);
- assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity");
- arglist[0] = arg; // outgoing 'this'
- arglist[1] = ArgToken(); // sentinel
- arg = make_invoke(methodHandle(), iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
- change_argument(src, slot, dst, arg);
- } else {
- // return type conversion
- if (_return_conv == vmIntrinsics::_none) {
- _return_conv = iid;
- } else if (_return_conv == vmIntrinsics::for_raw_conversion(dst, src)) {
- _return_conv = vmIntrinsics::_none;
- } else if (_return_conv != zero_return_conv()) {
- lose(err_msg("requested raw return conversion not allowed: %s -> %s (before %s)", type2name(src), type2name(dst), vmIntrinsics::name_at(_return_conv)), CHECK);
- }
- }
- } else {
- // Nothing to do.
- }
- } else if (for_return && (!is_subword_type(src) || !is_subword_type(dst))) {
- // This can occur in exception-throwing MHs, which have a fictitious return value encoded as Void or Empty.
- _return_conv = zero_return_conv();
- } 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(err_msg("requested raw conversion not allowed: %s -> %s", type2name(src), type2name(dst)), CHECK);
- }
- }
-}
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleCompiler
-
-MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* signature, int invoke_count, bool is_invokedynamic, TRAPS)
- : MethodHandleWalker(root, is_invokedynamic, THREAD),
- _invoke_count(invoke_count),
- _thread(THREAD),
- _bytecode(THREAD, 50),
- _constants(THREAD, 10),
- _non_bcp_klasses(THREAD, 5),
- _cur_stack(0),
- _max_stack(0),
- _rtype(T_ILLEGAL),
- _selectAlternative_bci(-1),
- _taken_count(0),
- _not_taken_count(0)
-{
-
- // Element zero is always the null constant.
- (void) _constants.append(NULL);
-
- // Set name and signature index.
- _name_index = cpool_symbol_put(name);
- _signature_index = cpool_symbol_put(signature);
-
- // To make the resulting methods more recognizable by
- // stack walkers and compiler heuristics,
- // we put them in holder class MethodHandle.
- // See klass_is_method_handle_adapter_holder
- // and methodOopDesc::is_method_handle_adapter.
- _target_klass = SystemDictionaryHandles::MethodHandle_klass();
-
- check_non_bcp_klasses(java_lang_invoke_MethodHandle::type(root()), CHECK);
-
- // Get return type klass.
- Handle first_mtype(THREAD, chain().method_type_oop());
- // _rklass is NULL for primitives.
- _rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(first_mtype()), &_rklass);
- if (_rtype == T_ARRAY) _rtype = T_OBJECT;
-
- ArgumentSizeComputer args(signature);
- int params = args.size() + 1; // Incoming arguments plus receiver.
- _num_params = for_invokedynamic() ? params - 1 : params; // XXX Check if callee is static?
-}
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleCompiler::compile
-//
-// Compile this MethodHandle into a bytecode adapter and return a
-// methodOop.
-methodHandle MethodHandleCompiler::compile(TRAPS) {
- assert(_thread == THREAD, "must be same thread");
- methodHandle nullHandle;
- (void) walk(CHECK_(nullHandle));
- record_non_bcp_klasses();
- return get_method_oop(CHECK_(nullHandle));
-}
-
-
-void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index, int args_size) {
- Bytecodes::check(op); // Are we legal?
-
- switch (op) {
- // b
- case Bytecodes::_aconst_null:
- case Bytecodes::_iconst_m1:
- case Bytecodes::_iconst_0:
- case Bytecodes::_iconst_1:
- case Bytecodes::_iconst_2:
- case Bytecodes::_iconst_3:
- case Bytecodes::_iconst_4:
- case Bytecodes::_iconst_5:
- case Bytecodes::_lconst_0:
- case Bytecodes::_lconst_1:
- case Bytecodes::_fconst_0:
- case Bytecodes::_fconst_1:
- case Bytecodes::_fconst_2:
- case Bytecodes::_dconst_0:
- case Bytecodes::_dconst_1:
- case Bytecodes::_iload_0:
- case Bytecodes::_iload_1:
- case Bytecodes::_iload_2:
- case Bytecodes::_iload_3:
- case Bytecodes::_lload_0:
- case Bytecodes::_lload_1:
- case Bytecodes::_lload_2:
- case Bytecodes::_lload_3:
- case Bytecodes::_fload_0:
- case Bytecodes::_fload_1:
- case Bytecodes::_fload_2:
- case Bytecodes::_fload_3:
- case Bytecodes::_dload_0:
- case Bytecodes::_dload_1:
- case Bytecodes::_dload_2:
- case Bytecodes::_dload_3:
- case Bytecodes::_aload_0:
- case Bytecodes::_aload_1:
- case Bytecodes::_aload_2:
- case Bytecodes::_aload_3:
- case Bytecodes::_istore_0:
- case Bytecodes::_istore_1:
- case Bytecodes::_istore_2:
- case Bytecodes::_istore_3:
- case Bytecodes::_lstore_0:
- case Bytecodes::_lstore_1:
- case Bytecodes::_lstore_2:
- case Bytecodes::_lstore_3:
- case Bytecodes::_fstore_0:
- case Bytecodes::_fstore_1:
- case Bytecodes::_fstore_2:
- case Bytecodes::_fstore_3:
- case Bytecodes::_dstore_0:
- case Bytecodes::_dstore_1:
- case Bytecodes::_dstore_2:
- case Bytecodes::_dstore_3:
- case Bytecodes::_astore_0:
- case Bytecodes::_astore_1:
- case Bytecodes::_astore_2:
- case Bytecodes::_astore_3:
- case Bytecodes::_iand:
- case Bytecodes::_i2l:
- case Bytecodes::_i2f:
- case Bytecodes::_i2d:
- case Bytecodes::_i2b:
- case Bytecodes::_i2c:
- case Bytecodes::_i2s:
- case Bytecodes::_l2i:
- case Bytecodes::_l2f:
- case Bytecodes::_l2d:
- case Bytecodes::_f2i:
- case Bytecodes::_f2l:
- case Bytecodes::_f2d:
- case Bytecodes::_d2i:
- case Bytecodes::_d2l:
- case Bytecodes::_d2f:
- case Bytecodes::_iaload:
- case Bytecodes::_laload:
- case Bytecodes::_faload:
- case Bytecodes::_daload:
- case Bytecodes::_aaload:
- case Bytecodes::_baload:
- case Bytecodes::_caload:
- case Bytecodes::_saload:
- case Bytecodes::_ireturn:
- case Bytecodes::_lreturn:
- case Bytecodes::_freturn:
- case Bytecodes::_dreturn:
- case Bytecodes::_areturn:
- case Bytecodes::_return:
- assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_b, "wrong bytecode format");
- _bytecode.push(op);
- break;
-
- // bi
- case Bytecodes::_ldc:
- assert(Bytecodes::format_bits(op, false) == (Bytecodes::_fmt_b|Bytecodes::_fmt_has_k), "wrong bytecode format");
- if (index == (index & 0xff)) {
- _bytecode.push(op);
- _bytecode.push(index);
- } else {
- _bytecode.push(Bytecodes::_ldc_w);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- }
- break;
-
- case Bytecodes::_iload:
- case Bytecodes::_lload:
- case Bytecodes::_fload:
- case Bytecodes::_dload:
- case Bytecodes::_aload:
- case Bytecodes::_istore:
- case Bytecodes::_lstore:
- case Bytecodes::_fstore:
- case Bytecodes::_dstore:
- case Bytecodes::_astore:
- assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bi, "wrong bytecode format");
- if (index == (index & 0xff)) {
- _bytecode.push(op);
- _bytecode.push(index);
- } else {
- // doesn't fit in a u2
- _bytecode.push(Bytecodes::_wide);
- _bytecode.push(op);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- }
- break;
-
- // bkk
- case Bytecodes::_ldc_w:
- case Bytecodes::_ldc2_w:
- case Bytecodes::_checkcast:
- assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bkk, "wrong bytecode format");
- assert((unsigned short) index == index, "index does not fit in 16-bit");
- _bytecode.push(op);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- break;
-
- // bJJ
- case Bytecodes::_invokestatic:
- case Bytecodes::_invokespecial:
- case Bytecodes::_invokevirtual:
- assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bJJ, "wrong bytecode format");
- assert((unsigned short) index == index, "index does not fit in 16-bit");
- _bytecode.push(op);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- break;
-
- case Bytecodes::_invokeinterface:
- assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bJJ, "wrong bytecode format");
- assert((unsigned short) index == index, "index does not fit in 16-bit");
- assert(args_size > 0, "valid args_size");
- _bytecode.push(op);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- _bytecode.push(args_size);
- _bytecode.push(0);
- break;
-
- case Bytecodes::_ifeq:
- assert((unsigned short) index == index, "index does not fit in 16-bit");
- _bytecode.push(op);
- _bytecode.push(index >> 8);
- _bytecode.push(index);
- break;
-
- default:
- ShouldNotReachHere();
- }
-}
-
-void MethodHandleCompiler::update_branch_dest(int src, int dst) {
- switch (_bytecode.at(src)) {
- case Bytecodes::_ifeq:
- dst -= src; // compute the offset
- assert((unsigned short) dst == dst, "index does not fit in 16-bit");
- _bytecode.at_put(src + 1, dst >> 8);
- _bytecode.at_put(src + 2, dst);
- break;
- default:
- ShouldNotReachHere();
- }
-}
-
-void MethodHandleCompiler::emit_load(ArgToken arg) {
- TokenType tt = arg.token_type();
- BasicType bt = arg.basic_type();
-
- switch (tt) {
- case tt_parameter:
- case tt_temporary:
- emit_load(bt, arg.index());
- break;
- case tt_constant:
- emit_load_constant(arg);
- break;
- case tt_illegal:
- case tt_void:
- default:
- ShouldNotReachHere();
- }
-}
-
-
-void MethodHandleCompiler::emit_load(BasicType bt, int index) {
- if (index <= 3) {
- switch (bt) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: emit_bc(Bytecodes::cast(Bytecodes::_iload_0 + index)); break;
- case T_LONG: emit_bc(Bytecodes::cast(Bytecodes::_lload_0 + index)); break;
- case T_FLOAT: emit_bc(Bytecodes::cast(Bytecodes::_fload_0 + index)); break;
- case T_DOUBLE: emit_bc(Bytecodes::cast(Bytecodes::_dload_0 + index)); break;
- case T_OBJECT: emit_bc(Bytecodes::cast(Bytecodes::_aload_0 + index)); break;
- default:
- ShouldNotReachHere();
- }
- }
- else {
- switch (bt) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: emit_bc(Bytecodes::_iload, index); break;
- case T_LONG: emit_bc(Bytecodes::_lload, index); break;
- case T_FLOAT: emit_bc(Bytecodes::_fload, index); break;
- case T_DOUBLE: emit_bc(Bytecodes::_dload, index); break;
- case T_OBJECT: emit_bc(Bytecodes::_aload, index); break;
- default:
- ShouldNotReachHere();
- }
- }
- stack_push(bt);
-}
-
-void MethodHandleCompiler::emit_store(BasicType bt, int index) {
- if (index <= 3) {
- switch (bt) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: emit_bc(Bytecodes::cast(Bytecodes::_istore_0 + index)); break;
- case T_LONG: emit_bc(Bytecodes::cast(Bytecodes::_lstore_0 + index)); break;
- case T_FLOAT: emit_bc(Bytecodes::cast(Bytecodes::_fstore_0 + index)); break;
- case T_DOUBLE: emit_bc(Bytecodes::cast(Bytecodes::_dstore_0 + index)); break;
- case T_OBJECT: emit_bc(Bytecodes::cast(Bytecodes::_astore_0 + index)); break;
- default:
- ShouldNotReachHere();
- }
- }
- else {
- switch (bt) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: emit_bc(Bytecodes::_istore, index); break;
- case T_LONG: emit_bc(Bytecodes::_lstore, index); break;
- case T_FLOAT: emit_bc(Bytecodes::_fstore, index); break;
- case T_DOUBLE: emit_bc(Bytecodes::_dstore, index); break;
- case T_OBJECT: emit_bc(Bytecodes::_astore, index); break;
- default:
- ShouldNotReachHere();
- }
- }
- stack_pop(bt);
-}
-
-
-void MethodHandleCompiler::emit_load_constant(ArgToken arg) {
- BasicType bt = arg.basic_type();
- if (is_subword_type(bt)) bt = T_INT;
- switch (bt) {
- case T_INT: {
- jint value = arg.get_jint();
- if (-1 <= value && value <= 5)
- emit_bc(Bytecodes::cast(Bytecodes::_iconst_0 + value));
- else
- emit_bc(Bytecodes::_ldc, cpool_int_put(value));
- break;
- }
- case T_LONG: {
- jlong value = arg.get_jlong();
- if (0 <= value && value <= 1)
- emit_bc(Bytecodes::cast(Bytecodes::_lconst_0 + (int) value));
- else
- emit_bc(Bytecodes::_ldc2_w, cpool_long_put(value));
- break;
- }
- case T_FLOAT: {
- jfloat value = arg.get_jfloat();
- if (value == 0.0 || value == 1.0 || value == 2.0)
- emit_bc(Bytecodes::cast(Bytecodes::_fconst_0 + (int) value));
- else
- emit_bc(Bytecodes::_ldc, cpool_float_put(value));
- break;
- }
- case T_DOUBLE: {
- jdouble value = arg.get_jdouble();
- if (value == 0.0 || value == 1.0)
- emit_bc(Bytecodes::cast(Bytecodes::_dconst_0 + (int) value));
- else
- emit_bc(Bytecodes::_ldc2_w, cpool_double_put(value));
- break;
- }
- case T_OBJECT: {
- Handle value = arg.object();
- if (value.is_null()) {
- emit_bc(Bytecodes::_aconst_null);
- break;
- }
- if (java_lang_Class::is_instance(value())) {
- klassOop k = java_lang_Class::as_klassOop(value());
- if (k != NULL) {
- emit_bc(Bytecodes::_ldc, cpool_klass_put(k));
- break;
- }
- }
- emit_bc(Bytecodes::_ldc, cpool_object_put(value));
- break;
- }
- default:
- ShouldNotReachHere();
- }
- stack_push(bt);
-}
-
-
-MethodHandleWalker::ArgToken
-MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Code op,
- const ArgToken& src, TRAPS) {
-
- BasicType srctype = src.basic_type();
- TokenType tt = src.token_type();
- int index = -1;
-
- switch (op) {
- case Bytecodes::_i2l:
- case Bytecodes::_i2f:
- case Bytecodes::_i2d:
- case Bytecodes::_i2b:
- case Bytecodes::_i2c:
- case Bytecodes::_i2s:
-
- case Bytecodes::_l2i:
- case Bytecodes::_l2f:
- case Bytecodes::_l2d:
-
- case Bytecodes::_f2i:
- case Bytecodes::_f2l:
- case Bytecodes::_f2d:
-
- case Bytecodes::_d2i:
- case Bytecodes::_d2l:
- case Bytecodes::_d2f:
- if (tt == tt_constant) {
- emit_load_constant(src);
- } else {
- emit_load(srctype, src.index());
- }
- stack_pop(srctype); // pop the src type
- emit_bc(op);
- stack_push(type); // push the dest value
- if (tt != tt_constant)
- index = src.index();
- if (srctype != type || index == -1)
- index = new_local_index(type);
- emit_store(type, index);
- break;
-
- case Bytecodes::_checkcast:
- if (tt == tt_constant) {
- emit_load_constant(src);
- } else {
- emit_load(srctype, src.index());
- index = src.index();
- }
- emit_bc(op, cpool_klass_put(tk));
- check_non_bcp_klass(tk, CHECK_(src));
- // Allocate a new local for the type so that we don't hide the
- // previous type from the verifier.
- index = new_local_index(type);
- emit_store(srctype, index);
- break;
-
- case Bytecodes::_nop:
- // nothing to do
- return src;
-
- default:
- if (op == Bytecodes::_illegal)
- lose(err_msg("no such primitive conversion: %s -> %s", type2name(src.basic_type()), type2name(type)), THREAD);
- else
- lose(err_msg("bad primitive conversion op: %s", Bytecodes::name(op)), THREAD);
- return make_prim_constant(type, &zero_jvalue, THREAD);
- }
-
- return make_parameter(type, tk, index, THREAD);
-}
-
-
-// -----------------------------------------------------------------------------
-// MethodHandleCompiler
-//
-
-// Values used by the compiler.
-jvalue MethodHandleCompiler::zero_jvalue = { 0 };
-jvalue MethodHandleCompiler::one_jvalue = { 1 };
-
-// Fetch any values from CountingMethodHandles and capture them for profiles
-bool MethodHandleCompiler::fetch_counts(ArgToken arg1, ArgToken arg2) {
- int count1 = -1, count2 = -1;
- if (arg1.token_type() == tt_constant && arg1.basic_type() == T_OBJECT &&
- java_lang_invoke_CountingMethodHandle::is_instance(arg1.object()())) {
- count1 = java_lang_invoke_CountingMethodHandle::vmcount(arg1.object()());
- }
- if (arg2.token_type() == tt_constant && arg2.basic_type() == T_OBJECT &&
- java_lang_invoke_CountingMethodHandle::is_instance(arg2.object()())) {
- count2 = java_lang_invoke_CountingMethodHandle::vmcount(arg2.object()());
- }
- int total = count1 + count2;
- if (count1 != -1 && count2 != -1 && total != 0) {
- // Normalize the collect counts to the invoke_count
- if (count1 != 0) _not_taken_count = (int)(_invoke_count * count1 / (double)total);
- if (count2 != 0) _taken_count = (int)(_invoke_count * count2 / (double)total);
- return true;
- }
- return false;
-}
-
-// Emit bytecodes for the given invoke instruction.
-MethodHandleWalker::ArgToken
-MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
- Bytecodes::Code op, bool tailcall,
- int argc, MethodHandleWalker::ArgToken* argv,
- TRAPS) {
- ArgToken zero;
- if (m.is_null()) {
- // Get the intrinsic methodOop.
- m = methodHandle(THREAD, vmIntrinsics::method_for(iid));
- if (m.is_null()) {
- lose(vmIntrinsics::name_at(iid), CHECK_(zero));
- }
- }
-
- klassOop klass = m->method_holder();
- Symbol* name = m->name();
- Symbol* signature = m->signature();
-
- if (iid == vmIntrinsics::_invokeGeneric &&
- argc >= 1 && argv[0].token_type() == tt_constant) {
- assert(m->intrinsic_id() == vmIntrinsics::_invokeExact, "");
- Handle receiver = argv[0].object();
- Handle rtype(THREAD, java_lang_invoke_MethodHandle::type(receiver()));
- Handle mtype(THREAD, m->method_handle_type());
- if (rtype() != mtype()) {
- assert(java_lang_invoke_MethodType::form(rtype()) ==
- java_lang_invoke_MethodType::form(mtype()),
- "must be the same shape");
- // customize m to the exact required rtype
- bool has_non_bcp_klass = check_non_bcp_klasses(rtype(), CHECK_(zero));
- TempNewSymbol sig2 = java_lang_invoke_MethodType::as_signature(rtype(), true, CHECK_(zero));
- methodHandle m2;
- if (!has_non_bcp_klass) {
- methodOop m2_oop = SystemDictionary::find_method_handle_invoke(m->name(), sig2,
- KlassHandle(), CHECK_(zero));
- m2 = methodHandle(THREAD, m2_oop);
- }
- if (m2.is_null()) {
- // just build it fresh
- m2 = methodOopDesc::make_invoke_method(klass, m->name(), sig2, rtype, CHECK_(zero));
- if (m2.is_null())
- lose(err_msg("no customized invoker %s", sig2->as_utf8()), CHECK_(zero));
- }
- m = m2;
- signature = m->signature();
- }
- }
-
- if (m->intrinsic_id() == vmIntrinsics::_selectAlternative &&
- fetch_counts(argv[1], argv[2])) {
- assert(argc == 3, "three arguments");
- assert(tailcall, "only");
-
- // do inline bytecodes so we can drop profile data into it,
- // 0: iload_0
- emit_load(argv[0]);
- // 1: ifeq 8
- _selectAlternative_bci = _bytecode.length();
- emit_bc(Bytecodes::_ifeq, 0); // emit placeholder offset
- // 4: aload_1
- emit_load(argv[1]);
- // 5: areturn;
- emit_bc(Bytecodes::_areturn);
- // 8: aload_2
- update_branch_dest(_selectAlternative_bci, cur_bci());
- emit_load(argv[2]);
- // 9: areturn
- emit_bc(Bytecodes::_areturn);
- return ArgToken(); // Dummy return value.
- }
-
- check_non_bcp_klass(klass, CHECK_(zero));
- if (m->is_method_handle_invoke()) {
- check_non_bcp_klasses(m->method_handle_type(), CHECK_(zero));
- }
-
- // Count the number of arguments, not the size
- ArgumentCount asc(signature);
- assert(argc == asc.size() + ((op == Bytecodes::_invokestatic || op == Bytecodes::_invokedynamic) ? 0 : 1),
- "argc mismatch");
-
- for (int i = 0; i < argc; i++) {
- ArgToken arg = argv[i];
- TokenType tt = arg.token_type();
- BasicType bt = arg.basic_type();
-
- switch (tt) {
- case tt_parameter:
- case tt_temporary:
- emit_load(bt, arg.index());
- break;
- case tt_constant:
- emit_load_constant(arg);
- break;
- case tt_illegal:
- // Sentinel.
- assert(i == (argc - 1), "sentinel must be last entry");
- break;
- case tt_void:
- default:
- ShouldNotReachHere();
- }
- }
-
- // Populate constant pool.
- int name_index = cpool_symbol_put(name);
- int signature_index = cpool_symbol_put(signature);
- int name_and_type_index = cpool_name_and_type_put(name_index, signature_index);
- int klass_index = cpool_klass_put(klass);
- int methodref_index = cpool_methodref_put(op, klass_index, name_and_type_index, m);
-
- // Generate invoke.
- switch (op) {
- case Bytecodes::_invokestatic:
- case Bytecodes::_invokespecial:
- case Bytecodes::_invokevirtual:
- emit_bc(op, methodref_index);
- break;
-
- case Bytecodes::_invokeinterface: {
- ArgumentSizeComputer asc(signature);
- emit_bc(op, methodref_index, asc.size() + 1);
- break;
- }
-
- default:
- ShouldNotReachHere();
- }
-
- // If tailcall, we have walked all the way to a direct method handle.
- // Otherwise, make a recursive call to some helper routine.
- BasicType rbt = m->result_type();
- if (rbt == T_ARRAY) rbt = T_OBJECT;
- stack_push(rbt); // The return value is already pushed onto the stack.
- ArgToken ret;
- if (tailcall) {
- if (return_conv() == zero_return_conv()) {
- rbt = T_VOID; // discard value
- } else if (return_conv() != vmIntrinsics::_none) {
- // return value conversion
- int index = new_local_index(rbt);
- emit_store(rbt, index);
- ArgToken arglist[2];
- arglist[0] = ArgToken(tt_temporary, rbt, index);
- arglist[1] = ArgToken(); // sentinel
- ret = make_invoke(methodHandle(), return_conv(), Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(zero));
- set_return_conv(vmIntrinsics::_none);
- rbt = ret.basic_type();
- emit_load(rbt, ret.index());
- }
- if (rbt != _rtype) {
- if (rbt == T_VOID) {
- // push a zero of the right sort
- if (_rtype == T_OBJECT) {
- zero = make_oop_constant(NULL, CHECK_(zero));
- } else {
- zero = make_prim_constant(_rtype, &zero_jvalue, CHECK_(zero));
- }
- emit_load_constant(zero);
- } 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 {
- lose("unknown conversion", CHECK_(zero));
- }
- }
- switch (_rtype) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: emit_bc(Bytecodes::_ireturn); break;
- case T_LONG: emit_bc(Bytecodes::_lreturn); break;
- case T_FLOAT: emit_bc(Bytecodes::_freturn); break;
- case T_DOUBLE: emit_bc(Bytecodes::_dreturn); break;
- case T_VOID: emit_bc(Bytecodes::_return); break;
- case T_OBJECT:
- if (_rklass.not_null() && _rklass() != SystemDictionary::Object_klass() && !Klass::cast(_rklass())->is_interface()) {
- emit_bc(Bytecodes::_checkcast, cpool_klass_put(_rklass()));
- check_non_bcp_klass(_rklass(), CHECK_(zero));
- }
- emit_bc(Bytecodes::_areturn);
- break;
- default: ShouldNotReachHere();
- }
- ret = ArgToken(); // Dummy return value.
- }
- else {
- int index = new_local_index(rbt);
- switch (rbt) {
- case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
- case T_INT: case T_LONG: case T_FLOAT: case T_DOUBLE:
- case T_OBJECT:
- emit_store(rbt, index);
- ret = ArgToken(tt_temporary, rbt, index);
- break;
- case T_VOID:
- ret = ArgToken(tt_void);
- break;
- default:
- ShouldNotReachHere();
- }
- }
-
- return ret;
-}
-
-MethodHandleWalker::ArgToken
-MethodHandleCompiler::make_fetch(BasicType type, klassOop tk, Bytecodes::Code op,
- const MethodHandleWalker::ArgToken& base,
- const MethodHandleWalker::ArgToken& offset,
- TRAPS) {
- switch (base.token_type()) {
- case tt_parameter:
- case tt_temporary:
- emit_load(base.basic_type(), base.index());
- break;
- case tt_constant:
- emit_load_constant(base);
- break;
- default:
- ShouldNotReachHere();
- }
- switch (offset.token_type()) {
- case tt_parameter:
- case tt_temporary:
- emit_load(offset.basic_type(), offset.index());
- break;
- case tt_constant:
- emit_load_constant(offset);
- break;
- default:
- ShouldNotReachHere();
- }
- emit_bc(op);
- int index = new_local_index(type);
- emit_store(type, index);
- return ArgToken(tt_temporary, type, index);
-}
-
-
-int MethodHandleCompiler::cpool_primitive_put(BasicType bt, jvalue* con) {
- jvalue con_copy;
- assert(bt < T_OBJECT, "");
- if (type2aelembytes(bt) < jintSize) {
- // widen to int
- con_copy = (*con);
- con = &con_copy;
- switch (bt) {
- case T_BOOLEAN: con->i = (con->z ? 1 : 0); break;
- case T_BYTE: con->i = con->b; break;
- case T_CHAR: con->i = con->c; break;
- case T_SHORT: con->i = con->s; break;
- default: ShouldNotReachHere();
- }
- bt = T_INT;
- }
-
-// for (int i = 1, imax = _constants.length(); i < imax; i++) {
-// ConstantValue* con = _constants.at(i);
-// if (con != NULL && con->is_primitive() && con.basic_type() == bt) {
-// bool match = false;
-// switch (type2size[bt]) {
-// case 1: if (pcon->_value.i == con->i) match = true; break;
-// case 2: if (pcon->_value.j == con->j) match = true; break;
-// }
-// if (match)
-// return i;
-// }
-// }
- ConstantValue* cv = new ConstantValue(bt, *con);
- int index = _constants.append(cv);
-
- // long and double entries take 2 slots, we add another empty entry.
- if (type2size[bt] == 2)
- (void) _constants.append(NULL);
-
- return index;
-}
-
-bool MethodHandleCompiler::check_non_bcp_klasses(Handle method_type, TRAPS) {
- bool res = false;
- for (int i = -1, len = java_lang_invoke_MethodType::ptype_count(method_type()); i < len; i++) {
- oop ptype = (i == -1
- ? java_lang_invoke_MethodType::rtype(method_type())
- : java_lang_invoke_MethodType::ptype(method_type(), i));
- res |= check_non_bcp_klass(java_lang_Class::as_klassOop(ptype), CHECK_(false));
- }
- return res;
-}
-
-bool MethodHandleCompiler::check_non_bcp_klass(klassOop klass, TRAPS) {
- klass = methodOopDesc::check_non_bcp_klass(klass);
- if (klass != NULL) {
- Symbol* name = Klass::cast(klass)->name();
- for (int i = _non_bcp_klasses.length() - 1; i >= 0; i--) {
- klassOop k2 = _non_bcp_klasses.at(i)();
- if (Klass::cast(k2)->name() == name) {
- if (k2 != klass) {
- lose(err_msg("unsupported klass name alias %s", name->as_utf8()), THREAD);
- }
- return true;
- }
- }
- _non_bcp_klasses.append(KlassHandle(THREAD, klass));
- return true;
- }
- return false;
-}
-
-void MethodHandleCompiler::record_non_bcp_klasses() {
- // Append extra klasses to constant pool, to guide klass lookup.
- for (int k = 0; k < _non_bcp_klasses.length(); k++) {
- klassOop non_bcp_klass = _non_bcp_klasses.at(k)();
- bool add_to_cp = true;
- for (int j = 1; j < _constants.length(); j++) {
- ConstantValue* cv = _constants.at(j);
- if (cv != NULL && cv->tag() == JVM_CONSTANT_Class
- && cv->klass_oop() == non_bcp_klass) {
- add_to_cp = false;
- break;
- }
- }
- if (add_to_cp) cpool_klass_put(non_bcp_klass);
- }
-}
-
-constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const {
- constantPoolHandle nullHandle;
- constantPoolOop cpool_oop = oopFactory::new_constantPool(_constants.length(),
- oopDesc::IsSafeConc,
- CHECK_(nullHandle));
- constantPoolHandle cpool(THREAD, cpool_oop);
-
- // Fill the real constant pool skipping the zero element.
- for (int i = 1; i < _constants.length(); i++) {
- ConstantValue* cv = _constants.at(i);
- switch (cv->tag()) {
- case JVM_CONSTANT_Utf8: cpool->symbol_at_put( i, cv->symbol() ); break;
- case JVM_CONSTANT_Integer: cpool->int_at_put( i, cv->get_jint() ); break;
- case JVM_CONSTANT_Float: cpool->float_at_put( i, cv->get_jfloat() ); break;
- case JVM_CONSTANT_Long: cpool->long_at_put( i, cv->get_jlong() ); break;
- case JVM_CONSTANT_Double: cpool->double_at_put( i, cv->get_jdouble() ); break;
- case JVM_CONSTANT_Class: cpool->klass_at_put( i, cv->klass_oop() ); break;
- case JVM_CONSTANT_Methodref: cpool->method_at_put( i, cv->first_index(), cv->second_index()); break;
- case JVM_CONSTANT_InterfaceMethodref:
- cpool->interface_method_at_put(i, cv->first_index(), cv->second_index()); break;
- case JVM_CONSTANT_NameAndType: cpool->name_and_type_at_put(i, cv->first_index(), cv->second_index()); break;
- case JVM_CONSTANT_Object: cpool->object_at_put( i, cv->object_oop() ); break;
- default: ShouldNotReachHere();
- }
-
- switch (cv->tag()) {
- case JVM_CONSTANT_Long:
- case JVM_CONSTANT_Double:
- i++; // Skip empty entry.
- assert(_constants.at(i) == NULL, "empty entry");
- break;
- }
- }
-
- cpool->set_preresolution();
-
- // Set the constant pool holder to the target method's class.
- cpool->set_pool_holder(_target_klass());
-
- return cpool;
-}
-
-
-methodHandle MethodHandleCompiler::get_method_oop(TRAPS) {
- methodHandle empty;
- // Create a method that holds the generated bytecode. invokedynamic
- // has no receiver, normal MH calls do.
- int flags_bits;
- if (for_invokedynamic())
- flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC | JVM_ACC_STATIC);
- else
- flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC);
-
- // Create a new method
- methodHandle m;
- {
- methodOop m_oop = oopFactory::new_method(bytecode_length(),
- accessFlags_from(flags_bits),
- 0, 0, 0, 0, oopDesc::IsSafeConc, CHECK_(empty));
- m = methodHandle(THREAD, m_oop);
- }
-
- constantPoolHandle cpool = get_constant_pool(CHECK_(empty));
- m->set_constants(cpool());
-
- m->set_name_index(_name_index);
- m->set_signature_index(_signature_index);
-
- m->set_code((address) bytecode());
-
- m->set_max_stack(_max_stack);
- m->set_max_locals(max_locals());
- m->set_size_of_parameters(_num_params);
-
- // Rewrite the method and set up the constant pool cache.
- 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_(empty)); // Use fake class.
- Rewriter::relocate_and_link(_target_klass(), methods, CHECK_(empty)); // Use fake class.
-
- // Pre-resolve selected CP cache entries, to avoid problems with class loader scoping.
- constantPoolCacheHandle cpc(THREAD, cpool->cache());
- for (int i = 0; i < cpc->length(); i++) {
- ConstantPoolCacheEntry* e = cpc->entry_at(i);
- assert(!e->is_secondary_entry(), "no indy instructions in here, yet");
- int constant_pool_index = e->constant_pool_index();
- ConstantValue* cv = _constants.at(constant_pool_index);
- if (!cv->has_linkage()) continue;
- methodHandle m = cv->linkage();
- int index;
- switch (cv->tag()) {
- case JVM_CONSTANT_Methodref:
- index = m->vtable_index();
- if (m->is_static()) {
- e->set_method(Bytecodes::_invokestatic, m, index);
- } else {
- e->set_method(Bytecodes::_invokespecial, m, index);
- e->set_method(Bytecodes::_invokevirtual, m, index);
- }
- break;
- case JVM_CONSTANT_InterfaceMethodref:
- index = klassItable::compute_itable_index(m());
- e->set_interface_call(m, index);
- break;
- }
- }
-
- // 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);
-
- bool found_selectAlternative = false;
- // 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);
- }
- if (profile_data->is_BranchData() &&
- profile_data->bci() == _selectAlternative_bci) {
- BranchData* bd = profile_data->as_BranchData();
- bd->set_taken(_taken_count);
- bd->set_not_taken(_not_taken_count);
- found_selectAlternative = true;
- }
- }
- assert(_selectAlternative_bci == -1 || found_selectAlternative, "must have found profile entry");
- }
-
-#ifndef PRODUCT
- if (TraceMethodHandles) {
- m->print();
- m->print_codes();
- }
-#endif //PRODUCT
-
- assert(m->is_method_handle_adapter(), "must be recognized as an adapter");
- return m;
-}
-
-
-#ifndef PRODUCT
-
-// MH printer for debugging.
-
-class MethodHandlePrinter : public MethodHandleWalker {
-private:
- outputStream* _out;
- bool _verbose;
- int _temp_num;
- int _param_state;
- stringStream _strbuf;
- const char* strbuf() {
- const char* s = _strbuf.as_string();
- _strbuf.reset();
- return s;
- }
- ArgToken token(const char* str, BasicType type) {
- return ArgToken(str, type);
- }
- const char* string(ArgToken token) {
- return token.str();
- }
- 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;
- if (tk != NULL)
- kname = Klass::cast(tk)->external_name();
- s->print("%s", (kname != NULL) ? kname : type2name(type));
- }
- ArgToken maybe_make_temp(const char* statement_op, BasicType type, const char* temp_name) {
- const char* value = strbuf();
- if (!_verbose) return token(value, type);
- // make an explicit binding for each separate value
- _strbuf.print("%s%d", temp_name, ++_temp_num);
- const char* temp = strbuf();
- _out->print("\n %s %s %s = %s;", statement_op, type2name(type), temp, value);
- return token(temp, type);
- }
-
-public:
- MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS)
- : MethodHandleWalker(root, false, THREAD),
- _out(out),
- _verbose(verbose),
- _param_state(0),
- _temp_num(0)
- {
- out->print("MethodHandle:");
- java_lang_invoke_MethodType::print_signature(java_lang_invoke_MethodHandle::type(root()), out);
- out->print(" : #");
- start_params();
- }
- virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
- if (argnum < 0) {
- end_params();
- return token("return", type);
- }
- if ((_param_state & 1) == 0) {
- _param_state |= 1;
- _out->print(_verbose ? "\n " : "");
- } else {
- _out->print(_verbose ? ",\n " : ", ");
- }
- if (argnum >= _temp_num)
- _temp_num = argnum;
- // generate an argument name
- _strbuf.print("a%d", argnum);
- const char* arg = strbuf();
- put_type_name(type, tk, _out);
- _out->print(" %s", arg);
- return token(arg, type);
- }
- virtual ArgToken make_oop_constant(oop con, TRAPS) {
- if (con == NULL)
- _strbuf.print("null");
- else
- con->print_value_on(&_strbuf);
- if (_strbuf.size() == 0) { // yuck
- _strbuf.print("(a ");
- put_type_name(T_OBJECT, con->klass(), &_strbuf);
- _strbuf.print(")");
- }
- return maybe_make_temp("constant", T_OBJECT, "k");
- }
- virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) {
- java_lang_boxing_object::print(type, con, &_strbuf);
- return maybe_make_temp("constant", type, "k");
- }
- 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);
- }
- _strbuf.print(")");
- return maybe_make_temp("convert", type, "v");
- }
- 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);
- }
- _strbuf.print(")");
- return maybe_make_temp("fetch", type, "x");
- }
- virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid,
- Bytecodes::Code op, bool tailcall,
- int argc, ArgToken* argv, TRAPS) {
- Symbol* name;
- Symbol* sig;
- if (m.not_null()) {
- name = m->name();
- sig = m->signature();
- } else {
- name = vmSymbols::symbol_at(vmIntrinsics::name_for(iid));
- sig = vmSymbols::symbol_at(vmIntrinsics::signature_for(iid));
- }
- _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 ? ", " : ""), string(argv[i]));
- }
- _strbuf.print(")");
- if (!tailcall) {
- BasicType rt = char2type(sig->byte_at(sig->utf8_length()-1));
- if (rt == T_ILLEGAL) rt = T_OBJECT; // ';' at the end of '(...)L...;'
- return maybe_make_temp("invoke", rt, "x");
- } else {
- const char* ret = strbuf();
- _out->print(_verbose ? "\n return " : " ");
- _out->print("%s", ret);
- _out->print(_verbose ? "\n}\n" : " }");
- }
- return ArgToken();
- }
-
- virtual void set_method_handle(oop mh) {
- if (WizardMode && Verbose) {
- tty->print("\n--- next target: ");
- mh->print();
- }
- }
-
- static void print(Handle root, bool verbose, outputStream* out, TRAPS) {
- ResourceMark rm;
- MethodHandlePrinter printer(root, verbose, out, CHECK);
- printer.walk(CHECK);
- out->print("\n");
- }
- static void print(Handle root, bool verbose = Verbose, outputStream* out = tty) {
- Thread* THREAD = Thread::current();
- ResourceMark rm;
- MethodHandlePrinter printer(root, verbose, out, THREAD);
- if (!HAS_PENDING_EXCEPTION)
- printer.walk(THREAD);
- if (HAS_PENDING_EXCEPTION) {
- oop ex = PENDING_EXCEPTION;
- CLEAR_PENDING_EXCEPTION;
- out->print(" *** ");
- if (printer.lose_message() != NULL) out->print("%s ", printer.lose_message());
- out->print("}");
- }
- out->print("\n");
- }
-};
-
-extern "C"
-void print_method_handle(oop mh) {
- if (!mh->is_oop()) {
- tty->print_cr("*** not a method handle: "PTR_FORMAT, (intptr_t)mh);
- } else if (java_lang_invoke_MethodHandle::is_instance(mh)) {
- MethodHandlePrinter::print(mh);
- } else {
- tty->print("*** not a method handle: ");
- mh->print();
- }
-}
-
-#endif // PRODUCT
--- a/hotspot/src/share/vm/prims/methodHandleWalk.hpp Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,486 +0,0 @@
-/*
- * 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
- * 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_PRIMS_METHODHANDLEWALK_HPP
-#define SHARE_VM_PRIMS_METHODHANDLEWALK_HPP
-
-#include "prims/methodHandles.hpp"
-
-// Low-level parser for method handle chains.
-class MethodHandleChain : StackObj {
-public:
- typedef MethodHandles::EntryKind EntryKind;
-
-private:
- Handle _root; // original target
- Handle _method_handle; // current target
- bool _is_last; // final guy in chain
- bool _is_bound; // has a bound argument
- BasicType _arg_type; // if is_bound, the bound argument type
- int _arg_slot; // if is_bound or is_adapter, affected argument slot
- jint _conversion; // conversion field of AMH or -1
- methodHandle _last_method; // if is_last, which method we target
- Bytecodes::Code _last_invoke; // if is_last, type of invoke
- const char* _lose_message; // saved argument to lose()
-
- void set_method_handle(Handle target, TRAPS);
- void set_last_method(oop target, TRAPS);
- static BasicType compute_bound_arg_type(oop target, methodOop m, int arg_slot, TRAPS);
-
- oop MethodHandle_type_oop() { return java_lang_invoke_MethodHandle::type(method_handle_oop()); }
- oop MethodHandle_vmtarget_oop() { return java_lang_invoke_MethodHandle::vmtarget(method_handle_oop()); }
- int MethodHandle_vmslots() { return java_lang_invoke_MethodHandle::vmslots(method_handle_oop()); }
- int DirectMethodHandle_vmindex() { return java_lang_invoke_DirectMethodHandle::vmindex(method_handle_oop()); }
- oop BoundMethodHandle_argument_oop() { return java_lang_invoke_BoundMethodHandle::argument(method_handle_oop()); }
- int BoundMethodHandle_vmargslot() { return java_lang_invoke_BoundMethodHandle::vmargslot(method_handle_oop()); }
- int AdapterMethodHandle_conversion() { return java_lang_invoke_AdapterMethodHandle::conversion(method_handle_oop()); }
-
-#ifdef ASSERT
- void print_impl(TRAPS);
-#endif
-
-public:
- MethodHandleChain(Handle root, TRAPS)
- : _root(root)
- { set_method_handle(root, THREAD); }
-
- bool is_adapter() { return _conversion != -1; }
- bool is_bound() { return _is_bound; }
- bool is_last() { return _is_last; }
-
- void next(TRAPS) {
- assert(!is_last(), "");
- set_method_handle(MethodHandle_vmtarget_oop(), THREAD);
- }
-
- Handle root() { return _root; }
- Handle method_handle() { return _method_handle; }
- oop method_handle_oop() { return _method_handle(); }
- oop method_type_oop() { return MethodHandle_type_oop(); }
- oop vmtarget_oop() { return MethodHandle_vmtarget_oop(); }
-
- jint adapter_conversion() { assert(is_adapter(), ""); return _conversion; }
- int adapter_conversion_op() { return MethodHandles::adapter_conversion_op(adapter_conversion()); }
- BasicType adapter_conversion_src_type()
- { return MethodHandles::adapter_conversion_src_type(adapter_conversion()); }
- BasicType adapter_conversion_dest_type()
- { return MethodHandles::adapter_conversion_dest_type(adapter_conversion()); }
- int adapter_conversion_stack_move()
- { return MethodHandles::adapter_conversion_stack_move(adapter_conversion()); }
- int adapter_conversion_stack_pushes()
- { return adapter_conversion_stack_move() / MethodHandles::stack_move_unit(); }
- int adapter_conversion_vminfo()
- { return MethodHandles::adapter_conversion_vminfo(adapter_conversion()); }
- int adapter_arg_slot() { assert(is_adapter(), ""); return _arg_slot; }
- oop adapter_arg_oop() { assert(is_adapter(), ""); return BoundMethodHandle_argument_oop(); }
-
- BasicType bound_arg_type() { assert(is_bound(), ""); return _arg_type; }
- int bound_arg_slot() { assert(is_bound(), ""); return _arg_slot; }
- oop bound_arg_oop() { assert(is_bound(), ""); return BoundMethodHandle_argument_oop(); }
-
- methodHandle last_method() { assert(is_last(), ""); return _last_method; }
- methodOop last_method_oop() { assert(is_last(), ""); return _last_method(); }
- Bytecodes::Code last_invoke_code() { assert(is_last(), ""); return _last_invoke; }
-
- void lose(const char* msg, TRAPS);
- const char* lose_message() { return _lose_message; }
-
-#ifdef ASSERT
- // Print a symbolic description of a method handle chain, including
- // the signature for each method. The signatures are printed in
- // slot order to make it easier to understand.
- void print();
- static void print(oopDesc* mh);
-#endif
-};
-
-
-// Structure walker for method handles.
-// Does abstract interpretation on top of low-level parsing.
-// You supply the tokens shuffled by the abstract interpretation.
-class MethodHandleWalker : StackObj {
-public:
- // Stack values:
- enum TokenType {
- tt_void,
- tt_parameter,
- tt_temporary,
- tt_constant,
- tt_symbolic,
- tt_illegal
- };
-
- // Argument token:
- class ArgToken {
- private:
- TokenType _tt;
- BasicType _bt;
- jvalue _value;
- Handle _handle;
-
- public:
- ArgToken(TokenType tt = tt_illegal) : _tt(tt), _bt(tt == tt_void ? T_VOID : T_ILLEGAL) {
- assert(tt == tt_illegal || tt == tt_void, "invalid token type");
- }
-
- ArgToken(TokenType tt, BasicType bt, int index) : _tt(tt), _bt(bt) {
- assert(_tt == tt_parameter || _tt == tt_temporary, "must have index");
- _value.i = index;
- }
-
- ArgToken(BasicType bt, jvalue value) : _tt(tt_constant), _bt(bt), _value(value) { assert(_bt != T_OBJECT, "wrong constructor"); }
- ArgToken(Handle handle) : _tt(tt_constant), _bt(T_OBJECT), _handle(handle) {}
-
-
- ArgToken(const char* str, BasicType type) : _tt(tt_symbolic), _bt(type) {
- _value.j = (intptr_t)str;
- }
-
- TokenType token_type() const { return _tt; }
- BasicType basic_type() const { return _bt; }
- bool has_index() const { return _tt == tt_parameter || _tt == tt_temporary; }
- int index() const { assert(has_index(), "must have index");; return _value.i; }
- Handle object() const { assert(_bt == T_OBJECT, "wrong accessor"); assert(_tt == tt_constant, "value type"); return _handle; }
- const char* str() const { assert(_tt == tt_symbolic, "string type"); return (const char*)(intptr_t)_value.j; }
-
- jint get_jint() const { assert(_bt == T_INT || is_subword_type(_bt), "wrong accessor"); assert(_tt == tt_constant, "value types"); return _value.i; }
- jlong get_jlong() const { assert(_bt == T_LONG, "wrong accessor"); assert(_tt == tt_constant, "value types"); return _value.j; }
- jfloat get_jfloat() const { assert(_bt == T_FLOAT, "wrong accessor"); assert(_tt == tt_constant, "value types"); return _value.f; }
- jdouble get_jdouble() const { assert(_bt == T_DOUBLE, "wrong accessor"); assert(_tt == tt_constant, "value types"); return _value.d; }
- };
-
-private:
- MethodHandleChain _chain;
- 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<ArgToken> _outgoing; // current outgoing parameter slots
- int _outgoing_argc; // # non-empty outgoing slots
-
- vmIntrinsics::ID _return_conv; // Return conversion required by raw retypes.
-
- // Replace a value of type old_type at slot (and maybe slot+1) with the new value.
- // If old_type != T_VOID, remove the old argument at that point.
- // If new_type != T_VOID, insert the new argument at that point.
- // Insert or delete a second empty slot as needed.
- void change_argument(BasicType old_type, int slot, const ArgToken& new_arg);
- void change_argument(BasicType old_type, int slot, BasicType type, const ArgToken& new_arg) {
- assert(type == new_arg.basic_type(), "must agree");
- change_argument(old_type, slot, 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); }
-
- BasicType arg_type(int slot) {
- return _outgoing.at(slot).basic_type();
- }
- bool has_argument(int slot) {
- return arg_type(slot) < T_VOID;
- }
-
-#ifdef ASSERT
- int argument_count_slow();
-#endif
-
- // Return a bytecode for converting src to dest, if one exists.
- Bytecodes::Code conversion_code(BasicType src, BasicType dest);
-
- void walk_incoming_state(TRAPS);
-
- void verify_args_and_signature(TRAPS) NOT_DEBUG_RETURN;
-
-public:
- MethodHandleWalker(Handle root, bool for_invokedynamic, TRAPS)
- : _chain(root, THREAD),
- _for_invokedynamic(for_invokedynamic),
- _outgoing(THREAD, 10),
- _outgoing_argc(0),
- _return_conv(vmIntrinsics::_none)
- {
- _local_index = for_invokedynamic ? 0 : 1;
- }
-
- MethodHandleChain& chain() { return _chain; }
-
- bool for_invokedynamic() const { return _for_invokedynamic; }
-
- vmIntrinsics::ID return_conv() const { return _return_conv; }
- void set_return_conv(vmIntrinsics::ID c) { _return_conv = c; }
- static vmIntrinsics::ID zero_return_conv() { return vmIntrinsics::_min; }
-
- int new_local_index(BasicType bt) {
- //int index = _for_invokedynamic ? _local_index : _local_index - 1;
- int index = _local_index;
- _local_index += type2size[bt];
- return index;
- }
-
- 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(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) = 0;
-
- // For make_invoke, the methodHandle can be NULL if the intrinsic ID
- // is something other than vmIntrinsics::_none.
-
- // and in case anyone cares to related the previous actions to the chain:
- virtual void set_method_handle(oop mh) { }
-
- void lose(const char* msg, TRAPS) { chain().lose(msg, THREAD); }
- const char* lose_message() { return chain().lose_message(); }
-
- ArgToken walk(TRAPS);
-};
-
-
-// An abstract interpreter for method handle chains.
-// Produces an account of the semantics of a chain, in terms of a static IR.
-// The IR happens to be JVM bytecodes.
-class MethodHandleCompiler : public MethodHandleWalker {
-private:
- int _invoke_count; // count the original call site has been executed
- KlassHandle _rklass; // Return type for casting.
- BasicType _rtype;
- KlassHandle _target_klass;
- Thread* _thread;
-
- int _selectAlternative_bci; // These are used for capturing profiles from GWTs
- int _taken_count;
- int _not_taken_count;
-
- // Values used by the compiler.
- static jvalue zero_jvalue;
- static jvalue one_jvalue;
-
- // Fake constant pool entry.
- class ConstantValue : public ResourceObj {
- private:
- int _tag; // Constant pool tag type.
- JavaValue _value;
- Handle _handle;
- Symbol* _sym;
- methodHandle _method; // pre-linkage
-
- public:
- // Constructor for oop types.
- ConstantValue(int tag, Handle con) : _tag(tag), _handle(con) {
- assert(tag == JVM_CONSTANT_Class ||
- tag == JVM_CONSTANT_String ||
- tag == JVM_CONSTANT_Object, "must be oop type");
- }
-
- ConstantValue(int tag, Symbol* con) : _tag(tag), _sym(con) {
- assert(tag == JVM_CONSTANT_Utf8, "must be symbol type");
- }
-
- // Constructor for oop reference types.
- ConstantValue(int tag, int index) : _tag(tag) {
- assert(JVM_CONSTANT_Fieldref <= tag && tag <= JVM_CONSTANT_NameAndType, "must be ref type");
- _value.set_jint(index);
- }
- ConstantValue(int tag, int first_index, int second_index) : _tag(tag) {
- assert(JVM_CONSTANT_Fieldref <= tag && tag <= JVM_CONSTANT_NameAndType, "must be ref type");
- _value.set_jint(first_index << 16 | second_index);
- }
-
- // Constructor for primitive types.
- ConstantValue(BasicType bt, jvalue con) {
- _value.set_type(bt);
- switch (bt) {
- case T_INT: _tag = JVM_CONSTANT_Integer; _value.set_jint( con.i); break;
- case T_LONG: _tag = JVM_CONSTANT_Long; _value.set_jlong( con.j); break;
- case T_FLOAT: _tag = JVM_CONSTANT_Float; _value.set_jfloat( con.f); break;
- case T_DOUBLE: _tag = JVM_CONSTANT_Double; _value.set_jdouble(con.d); break;
- default: ShouldNotReachHere();
- }
- }
-
- int tag() const { return _tag; }
- Symbol* symbol() const { return _sym; }
- klassOop klass_oop() const { return (klassOop) _handle(); }
- oop object_oop() const { return _handle(); }
- int index() const { return _value.get_jint(); }
- int first_index() const { return _value.get_jint() >> 16; }
- int second_index() const { return _value.get_jint() & 0x0000FFFF; }
-
- bool is_primitive() const { return is_java_primitive(_value.get_type()); }
- jint get_jint() const { return _value.get_jint(); }
- jlong get_jlong() const { return _value.get_jlong(); }
- jfloat get_jfloat() const { return _value.get_jfloat(); }
- jdouble get_jdouble() const { return _value.get_jdouble(); }
-
- void set_linkage(methodHandle method) {
- assert(_method.is_null(), "");
- _method = method;
- }
- bool has_linkage() const { return _method.not_null(); }
- methodHandle linkage() const { return _method; }
- };
-
- // Fake constant pool.
- GrowableArray<ConstantValue*> _constants;
-
- // Non-BCP classes that appear in associated MethodTypes (require special handling).
- GrowableArray<KlassHandle> _non_bcp_klasses;
-
- // Accumulated compiler state:
- GrowableArray<unsigned char> _bytecode;
-
- int _cur_stack;
- int _max_stack;
- int _num_params;
- int _name_index;
- int _signature_index;
-
- void stack_push(BasicType bt) {
- _cur_stack += type2size[bt];
- if (_cur_stack > _max_stack) _max_stack = _cur_stack;
- }
- void stack_pop(BasicType bt) {
- _cur_stack -= type2size[bt];
- assert(_cur_stack >= 0, "sanity");
- }
-
- unsigned char* bytecode() const { return _bytecode.adr_at(0); }
- int bytecode_length() const { return _bytecode.length(); }
- int cur_bci() const { return _bytecode.length(); }
-
- // Fake constant pool.
- int cpool_oop_put(int tag, Handle con) {
- if (con.is_null()) return 0;
- ConstantValue* cv = new ConstantValue(tag, con);
- return _constants.append(cv);
- }
-
- int cpool_symbol_put(int tag, Symbol* con) {
- if (con == NULL) return 0;
- ConstantValue* cv = new ConstantValue(tag, con);
- con->increment_refcount();
- return _constants.append(cv);
- }
-
- int cpool_oop_reference_put(int tag, int first_index, int second_index, methodHandle method) {
- if (first_index == 0 && second_index == 0) return 0;
- assert(first_index != 0 && second_index != 0, "no zero indexes");
- ConstantValue* cv = new ConstantValue(tag, first_index, second_index);
- if (method.not_null()) cv->set_linkage(method);
- return _constants.append(cv);
- }
-
- int cpool_primitive_put(BasicType type, jvalue* con);
-
- bool check_non_bcp_klasses(Handle method_type, TRAPS);
- bool check_non_bcp_klass(klassOop klass, TRAPS);
- void record_non_bcp_klasses();
-
- int cpool_int_put(jint value) {
- jvalue con; con.i = value;
- return cpool_primitive_put(T_INT, &con);
- }
- int cpool_long_put(jlong value) {
- jvalue con; con.j = value;
- return cpool_primitive_put(T_LONG, &con);
- }
- int cpool_float_put(jfloat value) {
- jvalue con; con.f = value;
- return cpool_primitive_put(T_FLOAT, &con);
- }
- int cpool_double_put(jdouble value) {
- jvalue con; con.d = value;
- return cpool_primitive_put(T_DOUBLE, &con);
- }
-
- int cpool_object_put(Handle obj) {
- return cpool_oop_put(JVM_CONSTANT_Object, obj);
- }
- int cpool_symbol_put(Symbol* sym) {
- return cpool_symbol_put(JVM_CONSTANT_Utf8, sym);
- }
- int cpool_klass_put(klassOop klass) {
- return cpool_oop_put(JVM_CONSTANT_Class, klass);
- }
- int cpool_methodref_put(Bytecodes::Code op, int class_index, int name_and_type_index, methodHandle method) {
- int tag = (op == Bytecodes::_invokeinterface ? JVM_CONSTANT_InterfaceMethodref : JVM_CONSTANT_Methodref);
- return cpool_oop_reference_put(tag, class_index, name_and_type_index, method);
- }
- int cpool_name_and_type_put(int name_index, int signature_index) {
- return cpool_oop_reference_put(JVM_CONSTANT_NameAndType, name_index, signature_index, methodHandle());
- }
-
- void emit_bc(Bytecodes::Code op, int index = 0, int args_size = -1);
- void update_branch_dest(int src, int dst);
- void emit_load(ArgToken arg);
- void emit_load(BasicType bt, int index);
- void emit_store(BasicType bt, int index);
- void emit_load_constant(ArgToken arg);
-
- virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
- return ArgToken(tt_parameter, type, argnum);
- }
- virtual ArgToken make_oop_constant(oop con, TRAPS) {
- Handle h(THREAD, con);
- return ArgToken(h);
- }
- virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) {
- return ArgToken(type, *con);
- }
-
- virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS);
- virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS);
- virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS);
-
- // Check for profiling information on a GWT and return true if it's found
- bool fetch_counts(ArgToken a1, ArgToken a2);
-
- // Get a real constant pool.
- constantPoolHandle get_constant_pool(TRAPS) const;
-
- // Get a real methodOop.
- methodHandle get_method_oop(TRAPS);
-
-public:
- MethodHandleCompiler(Handle root, Symbol* name, Symbol* signature, int invoke_count, bool for_invokedynamic, TRAPS);
-
- // Compile the given MH chain into bytecode.
- methodHandle compile(TRAPS);
-
- // Tests if the given class is a MH adapter holder.
- static bool klass_is_method_handle_adapter_holder(klassOop klass) {
- return (klass == SystemDictionary::MethodHandle_klass());
- }
-};
-
-#endif // SHARE_VM_PRIMS_METHODHANDLEWALK_HPP
--- a/hotspot/src/share/vm/prims/methodHandles.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -30,166 +30,30 @@
#include "memory/allocation.inline.hpp"
#include "memory/oopFactory.hpp"
#include "prims/methodHandles.hpp"
-#include "prims/methodHandleWalk.hpp"
#include "runtime/compilationPolicy.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/reflection.hpp"
#include "runtime/signature.hpp"
#include "runtime/stubRoutines.hpp"
+
/*
* JSR 292 reference implementation: method handles
+ * The JDK 7 reference implementation represented method handle
+ * combinations as chains. Each link in the chain had a "vmentry"
+ * field which pointed at a bit of assembly code which performed
+ * one transformation before dispatching to the next link in the chain.
+ *
+ * The current reference implementation pushes almost all code generation
+ * responsibility to (trusted) Java code. A method handle contains a
+ * pointer to its "LambdaForm", which embodies all details of the method
+ * handle's behavior. The LambdaForm is a normal Java object, managed
+ * by a runtime coded in Java.
*/
bool MethodHandles::_enabled = false; // set true after successful native linkage
-
-MethodHandleEntry* MethodHandles::_entries[MethodHandles::_EK_LIMIT] = {NULL};
-const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
- "raise_exception",
- "invokestatic", // how a MH emulates invokestatic
- "invokespecial", // ditto for the other invokes...
- "invokevirtual",
- "invokeinterface",
- "bound_ref", // these are for BMH...
- "bound_int",
- "bound_long",
- "bound_ref_direct", // (direct versions have a direct methodOop)
- "bound_int_direct",
- "bound_long_direct",
-
- // starting at _adapter_mh_first:
- "adapter_retype_only", // these are for AMH...
- "adapter_retype_raw",
- "adapter_check_cast",
- "adapter_prim_to_prim",
- "adapter_ref_to_prim",
- "adapter_prim_to_ref",
- "adapter_swap_args",
- "adapter_rot_args",
- "adapter_dup_args",
- "adapter_drop_args",
- "adapter_collect_args",
- "adapter_spread_args",
- "adapter_fold_args",
- "adapter_unused_13",
-
- // optimized adapter types:
- "adapter_swap_args/1",
- "adapter_swap_args/2",
- "adapter_rot_args/1,up",
- "adapter_rot_args/1,down",
- "adapter_rot_args/2,up",
- "adapter_rot_args/2,down",
- "adapter_prim_to_prim/i2i",
- "adapter_prim_to_prim/l2i",
- "adapter_prim_to_prim/d2f",
- "adapter_prim_to_prim/i2l",
- "adapter_prim_to_prim/f2d",
- "adapter_ref_to_prim/unboxi",
- "adapter_ref_to_prim/unboxl",
-
- // 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",
-
- "adapter_opt_profiling",
-
- NULL
-};
-
-// Adapters.
MethodHandlesAdapterBlob* MethodHandles::_adapter_code = NULL;
-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_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
-
-
//------------------------------------------------------------------------------
// MethodHandles::generate_adapters
//
@@ -216,36 +80,20 @@
//
void MethodHandlesAdapterGenerator::generate() {
// Generate generic method handle adapters.
- for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
- ek < MethodHandles::_EK_LIMIT;
- ek = MethodHandles::EntryKind(1 + (int)ek)) {
- if (MethodHandles::ek_supported(ek)) {
- StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
- MethodHandles::generate_method_handle_stub(_masm, ek);
+ // Generate interpreter entries
+ for (Interpreter::MethodKind mk = Interpreter::method_handle_invoke_FIRST;
+ mk <= Interpreter::method_handle_invoke_LAST;
+ mk = Interpreter::MethodKind(1 + (int)mk)) {
+ vmIntrinsics::ID iid = Interpreter::method_handle_intrinsic(mk);
+ StubCodeMark mark(this, "MethodHandle::interpreter_entry", vmIntrinsics::name_at(iid));
+ address entry = MethodHandles::generate_method_handle_interpreter_entry(_masm, iid);
+ if (entry != NULL) {
+ Interpreter::set_entry_for_kind(mk, entry);
}
+ // If the entry is not set, it will throw AbstractMethodError.
}
}
-
-//------------------------------------------------------------------------------
-// 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 conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF);
- case _adapter_collect_args:
- return conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS);
- case _adapter_fold_args:
- return conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS);
- }
- return true;
-}
-
-
void MethodHandles::set_enabled(bool z) {
if (_enabled != z) {
guarantee(z && EnableInvokeDynamic, "can only enable once, and only if -XX:+EnableInvokeDynamic");
@@ -253,217 +101,6 @@
}
}
-// Note: A method which does not have a TRAPS argument cannot block in the GC
-// or throw exceptions. Such methods are used in this file to do something quick
-// and local, like parse a data structure. For speed, such methods work on plain
-// oops, not handles. Trapping methods uniformly operate on handles.
-
-methodHandle MethodHandles::decode_vmtarget(oop vmtarget, int vmindex, oop mtype,
- KlassHandle& receiver_limit_result, int& decode_flags_result) {
- if (vmtarget == NULL) return methodHandle();
- assert(methodOopDesc::nonvirtual_vtable_index < 0, "encoding");
- if (vmindex < 0) {
- // this DMH performs no dispatch; it is directly bound to a methodOop
- // A MemberName may either be directly bound to a methodOop,
- // or it may use the klass/index form; both forms mean the same thing.
- methodOop m = decode_methodOop(methodOop(vmtarget), decode_flags_result);
- if ((decode_flags_result & _dmf_has_receiver) != 0
- && java_lang_invoke_MethodType::is_instance(mtype)) {
- // Extract receiver type restriction from mtype.ptypes[0].
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(mtype);
- oop ptype0 = (ptypes == NULL || ptypes->length() < 1) ? oop(NULL) : ptypes->obj_at(0);
- if (java_lang_Class::is_instance(ptype0))
- receiver_limit_result = java_lang_Class::as_klassOop(ptype0);
- }
- if (vmindex == methodOopDesc::nonvirtual_vtable_index) {
- // this DMH can be an "invokespecial" version
- decode_flags_result &= ~_dmf_does_dispatch;
- } else {
- assert(vmindex == methodOopDesc::invalid_vtable_index, "random vmindex?");
- }
- return m;
- } else {
- assert(vmtarget->is_klass(), "must be class or interface");
- decode_flags_result |= MethodHandles::_dmf_does_dispatch;
- decode_flags_result |= MethodHandles::_dmf_has_receiver;
- receiver_limit_result = (klassOop)vmtarget;
- Klass* tk = Klass::cast((klassOop)vmtarget);
- if (tk->is_interface()) {
- // an itable linkage is <interface, itable index>
- decode_flags_result |= MethodHandles::_dmf_from_interface;
- return klassItable::method_for_itable_index((klassOop)vmtarget, vmindex);
- } else {
- if (!tk->oop_is_instance())
- tk = instanceKlass::cast(SystemDictionary::Object_klass());
- return ((instanceKlass*)tk)->method_at_vtable(vmindex);
- }
- }
-}
-
-// MemberName and DirectMethodHandle have the same linkage to the JVM internals.
-// (MemberName is the non-operational name used for queries and setup.)
-
-methodHandle MethodHandles::decode_DirectMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- oop vmtarget = java_lang_invoke_DirectMethodHandle::vmtarget(mh);
- int vmindex = java_lang_invoke_DirectMethodHandle::vmindex(mh);
- oop mtype = java_lang_invoke_DirectMethodHandle::type(mh);
- return decode_vmtarget(vmtarget, vmindex, mtype, receiver_limit_result, decode_flags_result);
-}
-
-methodHandle MethodHandles::decode_BoundMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- assert(java_lang_invoke_BoundMethodHandle::is_instance(mh), "");
- assert(mh->klass() != SystemDictionary::AdapterMethodHandle_klass(), "");
- for (oop bmh = mh;;) {
- // Bound MHs can be stacked to bind several arguments.
- oop target = java_lang_invoke_MethodHandle::vmtarget(bmh);
- if (target == NULL) return methodHandle();
- decode_flags_result |= MethodHandles::_dmf_binds_argument;
- klassOop tk = target->klass();
- if (tk == SystemDictionary::BoundMethodHandle_klass()) {
- bmh = target;
- continue;
- } else {
- if (java_lang_invoke_MethodHandle::is_subclass(tk)) {
- //assert(tk == SystemDictionary::DirectMethodHandle_klass(), "end of BMH chain must be DMH");
- return decode_MethodHandle(target, receiver_limit_result, decode_flags_result);
- } else {
- // Optimized case: binding a receiver to a non-dispatched DMH
- // short-circuits directly to the methodOop.
- // (It might be another argument besides a receiver also.)
- assert(target->is_method(), "must be a simple method");
- decode_flags_result |= MethodHandles::_dmf_binds_method;
- methodOop m = (methodOop) target;
- if (!m->is_static())
- decode_flags_result |= MethodHandles::_dmf_has_receiver;
- return m;
- }
- }
- }
-}
-
-methodHandle MethodHandles::decode_AdapterMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- assert(mh->klass() == SystemDictionary::AdapterMethodHandle_klass(), "");
- for (oop amh = mh;;) {
- // Adapter MHs can be stacked to convert several arguments.
- int conv_op = adapter_conversion_op(java_lang_invoke_AdapterMethodHandle::conversion(amh));
- decode_flags_result |= (_dmf_adapter_lsb << conv_op) & _DMF_ADAPTER_MASK;
- oop target = java_lang_invoke_MethodHandle::vmtarget(amh);
- if (target == NULL) return methodHandle();
- klassOop tk = target->klass();
- if (tk == SystemDictionary::AdapterMethodHandle_klass()) {
- amh = target;
- continue;
- } else {
- // must be a BMH (which will bind some more arguments) or a DMH (for the final call)
- return MethodHandles::decode_MethodHandle(target, receiver_limit_result, decode_flags_result);
- }
- }
-}
-
-methodHandle MethodHandles::decode_MethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- if (mh == NULL) return methodHandle();
- klassOop mhk = mh->klass();
- assert(java_lang_invoke_MethodHandle::is_subclass(mhk), "must be a MethodHandle");
- if (mhk == SystemDictionary::DirectMethodHandle_klass()) {
- return decode_DirectMethodHandle(mh, receiver_limit_result, decode_flags_result);
- } else if (mhk == SystemDictionary::BoundMethodHandle_klass()) {
- return decode_BoundMethodHandle(mh, receiver_limit_result, decode_flags_result);
- } else if (mhk == SystemDictionary::AdapterMethodHandle_klass()) {
- return decode_AdapterMethodHandle(mh, receiver_limit_result, decode_flags_result);
- } else if (java_lang_invoke_BoundMethodHandle::is_subclass(mhk)) {
- // could be a JavaMethodHandle (but not an adapter MH)
- return decode_BoundMethodHandle(mh, receiver_limit_result, decode_flags_result);
- } else {
- assert(false, "cannot parse this MH");
- return methodHandle(); // random MH?
- }
-}
-
-methodOop MethodHandles::decode_methodOop(methodOop m, int& decode_flags_result) {
- assert(m->is_method(), "");
- if (m->is_static()) {
- // check that signature begins '(L' or '([' (not '(I', '()', etc.)
- Symbol* sig = m->signature();
- BasicType recv_bt = char2type(sig->byte_at(1));
- // Note: recv_bt might be T_ILLEGAL if byte_at(2) is ')'
- assert(sig->byte_at(0) == '(', "must be method sig");
-// if (recv_bt == T_OBJECT || recv_bt == T_ARRAY)
-// decode_flags_result |= _dmf_has_receiver;
- } else {
- // non-static method
- decode_flags_result |= _dmf_has_receiver;
- if (!m->can_be_statically_bound() && !m->is_initializer()) {
- decode_flags_result |= _dmf_does_dispatch;
- if (Klass::cast(m->method_holder())->is_interface())
- decode_flags_result |= _dmf_from_interface;
- }
- }
- return m;
-}
-
-
-// A trusted party is handing us a cookie to determine a method.
-// Let's boil it down to the method oop they really want.
-methodHandle MethodHandles::decode_method(oop x, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- decode_flags_result = 0;
- receiver_limit_result = KlassHandle();
- klassOop xk = x->klass();
- if (xk == Universe::methodKlassObj()) {
- return decode_methodOop((methodOop) x, decode_flags_result);
- } else if (xk == SystemDictionary::MemberName_klass()) {
- // Note: This only works if the MemberName has already been resolved.
- return decode_MemberName(x, receiver_limit_result, decode_flags_result);
- } else if (java_lang_invoke_MethodHandle::is_subclass(xk)) {
- return decode_MethodHandle(x, receiver_limit_result, decode_flags_result);
- } else if (xk == SystemDictionary::reflect_Method_klass()) {
- oop clazz = java_lang_reflect_Method::clazz(x);
- int slot = java_lang_reflect_Method::slot(x);
- klassOop k = java_lang_Class::as_klassOop(clazz);
- if (k != NULL && Klass::cast(k)->oop_is_instance())
- return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot),
- decode_flags_result);
- } else if (xk == SystemDictionary::reflect_Constructor_klass()) {
- oop clazz = java_lang_reflect_Constructor::clazz(x);
- int slot = java_lang_reflect_Constructor::slot(x);
- klassOop k = java_lang_Class::as_klassOop(clazz);
- if (k != NULL && Klass::cast(k)->oop_is_instance())
- return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot),
- decode_flags_result);
- } else {
- // unrecognized object
- assert(!x->is_method(), "already checked");
- assert(!java_lang_invoke_MemberName::is_instance(x), "already checked");
- }
- return methodHandle();
-}
-
-
-int MethodHandles::decode_MethodHandle_stack_pushes(oop mh) {
- if (mh->klass() == SystemDictionary::DirectMethodHandle_klass())
- return 0; // no push/pop
- int this_vmslots = java_lang_invoke_MethodHandle::vmslots(mh);
- int last_vmslots = 0;
- oop last_mh = mh;
- for (;;) {
- oop target = java_lang_invoke_MethodHandle::vmtarget(last_mh);
- if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) {
- last_vmslots = java_lang_invoke_MethodHandle::vmslots(target);
- break;
- } else if (!java_lang_invoke_MethodHandle::is_instance(target)) {
- // might be klass or method
- assert(target->is_method(), "must get here with a direct ref to method");
- last_vmslots = methodOop(target)->size_of_parameters();
- break;
- }
- last_mh = target;
- }
- // If I am called with fewer VM slots than my ultimate callee,
- // it must be that I push the additionally needed slots.
- // Likewise if am called with more VM slots, I will pop them.
- return (last_vmslots - this_vmslots);
-}
-
-
// MemberName support
// import java_lang_invoke_MemberName.*
@@ -472,10 +109,11 @@
IS_CONSTRUCTOR = java_lang_invoke_MemberName::MN_IS_CONSTRUCTOR,
IS_FIELD = java_lang_invoke_MemberName::MN_IS_FIELD,
IS_TYPE = java_lang_invoke_MemberName::MN_IS_TYPE,
+ REFERENCE_KIND_SHIFT = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT,
+ REFERENCE_KIND_MASK = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
SEARCH_SUPERCLASSES = java_lang_invoke_MemberName::MN_SEARCH_SUPERCLASSES,
SEARCH_INTERFACES = java_lang_invoke_MemberName::MN_SEARCH_INTERFACES,
- ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE,
- VM_INDEX_UNINITIALIZED = java_lang_invoke_MemberName::VM_INDEX_UNINITIALIZED
+ ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE
};
Handle MethodHandles::new_MemberName(TRAPS) {
@@ -485,72 +123,265 @@
return Handle(THREAD, k->allocate_instance(THREAD));
}
-void MethodHandles::init_MemberName(oop mname_oop, oop target_oop) {
- if (target_oop->klass() == SystemDictionary::reflect_Field_klass()) {
+oop MethodHandles::init_MemberName(oop mname_oop, oop target_oop) {
+ klassOop target_klass = target_oop->klass();
+ if (target_klass == SystemDictionary::reflect_Field_klass()) {
oop clazz = java_lang_reflect_Field::clazz(target_oop); // fd.field_holder()
int slot = java_lang_reflect_Field::slot(target_oop); // fd.index()
int mods = java_lang_reflect_Field::modifiers(target_oop);
+ oop type = java_lang_reflect_Field::type(target_oop);
+ oop name = java_lang_reflect_Field::name(target_oop);
klassOop k = java_lang_Class::as_klassOop(clazz);
- int offset = instanceKlass::cast(k)->field_offset(slot);
- init_MemberName(mname_oop, k, accessFlags_from(mods), offset);
- } else {
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = MethodHandles::decode_method(target_oop, receiver_limit, decode_flags);
- bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0);
- init_MemberName(mname_oop, m(), do_dispatch);
+ intptr_t offset = instanceKlass::cast(k)->field_offset(slot);
+ return init_field_MemberName(mname_oop, k, accessFlags_from(mods), type, name, offset);
+ } else if (target_klass == SystemDictionary::reflect_Method_klass()) {
+ oop clazz = java_lang_reflect_Method::clazz(target_oop);
+ int slot = java_lang_reflect_Method::slot(target_oop);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ if (k != NULL && Klass::cast(k)->oop_is_instance()) {
+ methodOop m = instanceKlass::cast(k)->method_with_idnum(slot);
+ return init_method_MemberName(mname_oop, m, true, k);
+ }
+ } else if (target_klass == SystemDictionary::reflect_Constructor_klass()) {
+ oop clazz = java_lang_reflect_Constructor::clazz(target_oop);
+ int slot = java_lang_reflect_Constructor::slot(target_oop);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ if (k != NULL && Klass::cast(k)->oop_is_instance()) {
+ methodOop m = instanceKlass::cast(k)->method_with_idnum(slot);
+ return init_method_MemberName(mname_oop, m, false, k);
+ }
+ } else if (target_klass == SystemDictionary::MemberName_klass()) {
+ // Note: This only works if the MemberName has already been resolved.
+ oop clazz = java_lang_invoke_MemberName::clazz(target_oop);
+ int flags = java_lang_invoke_MemberName::flags(target_oop);
+ oop vmtarget = java_lang_invoke_MemberName::vmtarget(target_oop);
+ intptr_t vmindex = java_lang_invoke_MemberName::vmindex(target_oop);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ int ref_kind = (flags >> REFERENCE_KIND_SHIFT) & REFERENCE_KIND_MASK;
+ if (vmtarget == NULL) return NULL; // not resolved
+ if ((flags & IS_FIELD) != 0) {
+ assert(vmtarget->is_klass(), "field vmtarget is klassOop");
+ int basic_mods = (ref_kind_is_static(ref_kind) ? JVM_ACC_STATIC : 0);
+ // FIXME: how does k (receiver_limit) contribute?
+ return init_field_MemberName(mname_oop, klassOop(vmtarget), accessFlags_from(basic_mods), NULL, NULL, vmindex);
+ } else if ((flags & (IS_METHOD | IS_CONSTRUCTOR)) != 0) {
+ assert(vmtarget->is_method(), "method or constructor vmtarget is methodOop");
+ return init_method_MemberName(mname_oop, methodOop(vmtarget), ref_kind_does_dispatch(ref_kind), k);
+ } else {
+ return NULL;
+ }
}
+ return NULL;
}
-void MethodHandles::init_MemberName(oop mname_oop, methodOop m, bool do_dispatch) {
- int flags = ((m->is_initializer() ? IS_CONSTRUCTOR : IS_METHOD)
- | (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS ));
- oop vmtarget = m;
- int vmindex = methodOopDesc::invalid_vtable_index; // implies no info yet
- if (!do_dispatch || (flags & IS_CONSTRUCTOR) || m->can_be_statically_bound())
- vmindex = methodOopDesc::nonvirtual_vtable_index; // implies never any dispatch
- assert(vmindex != VM_INDEX_UNINITIALIZED, "Java sentinel value");
+oop MethodHandles::init_method_MemberName(oop mname_oop, methodOop m, bool do_dispatch,
+ klassOop receiver_limit) {
+ AccessFlags mods = m->access_flags();
+ int flags = (jushort)( mods.as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS );
+ int vmindex = methodOopDesc::nonvirtual_vtable_index; // implies never any dispatch
+ klassOop mklass = m->method_holder();
+ if (receiver_limit == NULL)
+ receiver_limit = mklass;
+ if (m->is_initializer()) {
+ flags |= IS_CONSTRUCTOR | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT);
+ } else if (mods.is_static()) {
+ flags |= IS_METHOD | (JVM_REF_invokeStatic << REFERENCE_KIND_SHIFT);
+ } else if (receiver_limit != mklass &&
+ !Klass::cast(receiver_limit)->is_subtype_of(mklass)) {
+ return NULL; // bad receiver limit
+ } else if (Klass::cast(receiver_limit)->is_interface() &&
+ Klass::cast(mklass)->is_interface()) {
+ flags |= IS_METHOD | (JVM_REF_invokeInterface << REFERENCE_KIND_SHIFT);
+ receiver_limit = mklass; // ignore passed-in limit; interfaces are interconvertible
+ vmindex = klassItable::compute_itable_index(m);
+ } else if (mklass != receiver_limit && Klass::cast(mklass)->is_interface()) {
+ flags |= IS_METHOD | (JVM_REF_invokeVirtual << REFERENCE_KIND_SHIFT);
+ // it is a miranda method, so m->vtable_index is not what we want
+ ResourceMark rm;
+ klassVtable* vt = instanceKlass::cast(receiver_limit)->vtable();
+ vmindex = vt->index_of_miranda(m->name(), m->signature());
+ } else if (!do_dispatch || m->can_be_statically_bound()) {
+ flags |= IS_METHOD | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT);
+ } else {
+ flags |= IS_METHOD | (JVM_REF_invokeVirtual << REFERENCE_KIND_SHIFT);
+ vmindex = m->vtable_index();
+ }
+
+ java_lang_invoke_MemberName::set_flags(mname_oop, flags);
+ java_lang_invoke_MemberName::set_vmtarget(mname_oop, m);
+ java_lang_invoke_MemberName::set_vmindex(mname_oop, vmindex); // vtable/itable index
+ java_lang_invoke_MemberName::set_clazz(mname_oop, Klass::cast(receiver_limit)->java_mirror());
+ // Note: name and type can be lazily computed by resolve_MemberName,
+ // if Java code needs them as resolved String and MethodType objects.
+ // The clazz must be eagerly stored, because it provides a GC
+ // root to help keep alive the methodOop.
+ // If relevant, the vtable or itable value is stored as vmindex.
+ // This is done eagerly, since it is readily available without
+ // constructing any new objects.
+ // TO DO: maybe intern mname_oop
+ return mname_oop;
+}
+
+Handle MethodHandles::init_method_MemberName(oop mname_oop, CallInfo& info, TRAPS) {
+ Handle empty;
+ if (info.resolved_appendix().not_null()) {
+ // The resolved MemberName must not be accompanied by an appendix argument,
+ // since there is no way to bind this value into the MemberName.
+ // Caller is responsible to prevent this from happening.
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "appendix", empty);
+ }
+ methodHandle m = info.resolved_method();
+ KlassHandle defc = info.resolved_klass();
+ int vmindex = -1;
+ if (defc->is_interface() && Klass::cast(m->method_holder())->is_interface()) {
+ // LinkResolver does not report itable indexes! (fix this?)
+ vmindex = klassItable::compute_itable_index(m());
+ } else if (m->can_be_statically_bound()) {
+ // LinkResolver reports vtable index even for final methods!
+ vmindex = methodOopDesc::nonvirtual_vtable_index;
+ } else {
+ vmindex = info.vtable_index();
+ }
+ oop res = init_method_MemberName(mname_oop, m(), (vmindex >= 0), defc());
+ assert(res == NULL || (java_lang_invoke_MemberName::vmindex(res) == vmindex), "");
+ return Handle(THREAD, res);
+}
+
+oop MethodHandles::init_field_MemberName(oop mname_oop, klassOop field_holder,
+ AccessFlags mods, oop type, oop name,
+ intptr_t offset, bool is_setter) {
+ int flags = (jushort)( mods.as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS );
+ flags |= IS_FIELD | ((mods.is_static() ? JVM_REF_getStatic : JVM_REF_getField) << REFERENCE_KIND_SHIFT);
+ if (is_setter) flags += ((JVM_REF_putField - JVM_REF_getField) << REFERENCE_KIND_SHIFT);
+ oop vmtarget = field_holder;
+ int vmindex = offset; // determines the field uniquely when combined with static bit
+ java_lang_invoke_MemberName::set_flags(mname_oop, flags);
java_lang_invoke_MemberName::set_vmtarget(mname_oop, vmtarget);
java_lang_invoke_MemberName::set_vmindex(mname_oop, vmindex);
- java_lang_invoke_MemberName::set_flags(mname_oop, flags);
- java_lang_invoke_MemberName::set_clazz(mname_oop, Klass::cast(m->method_holder())->java_mirror());
+ java_lang_invoke_MemberName::set_clazz(mname_oop, Klass::cast(field_holder)->java_mirror());
+ if (name != NULL)
+ java_lang_invoke_MemberName::set_name(mname_oop, name);
+ if (type != NULL)
+ java_lang_invoke_MemberName::set_type(mname_oop, type);
+ // Note: name and type can be lazily computed by resolve_MemberName,
+ // if Java code needs them as resolved String and Class objects.
+ // Note that the incoming type oop might be pre-resolved (non-null).
+ // The base clazz and field offset (vmindex) must be eagerly stored,
+ // because they unambiguously identify the field.
+ // Although the fieldDescriptor::_index would also identify the field,
+ // we do not use it, because it is harder to decode.
+ // TO DO: maybe intern mname_oop
+ return mname_oop;
}
-void MethodHandles::init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset) {
- int flags = (IS_FIELD | (jushort)( mods.as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS ));
- oop vmtarget = field_holder;
- int vmindex = offset; // determines the field uniquely when combined with static bit
- assert(vmindex != VM_INDEX_UNINITIALIZED, "bad alias on vmindex");
- java_lang_invoke_MemberName::set_vmtarget(mname_oop, vmtarget);
- java_lang_invoke_MemberName::set_vmindex(mname_oop, vmindex);
- java_lang_invoke_MemberName::set_flags(mname_oop, flags);
- java_lang_invoke_MemberName::set_clazz(mname_oop, Klass::cast(field_holder)->java_mirror());
+Handle MethodHandles::init_field_MemberName(oop mname_oop, FieldAccessInfo& info, TRAPS) {
+ return Handle();
+#if 0
+ KlassHandle field_holder = info.klass();
+ intptr_t field_offset = info.field_offset();
+ return init_field_MemberName(mname_oop, field_holder(),
+ info.access_flags(),
+ type, name,
+ field_offset, false /*is_setter*/);
+#endif
}
-methodHandle MethodHandles::decode_MemberName(oop mname, KlassHandle& receiver_limit_result, int& decode_flags_result) {
- methodHandle empty;
- int flags = java_lang_invoke_MemberName::flags(mname);
- if ((flags & (IS_METHOD | IS_CONSTRUCTOR)) == 0) return empty; // not invocable
- oop vmtarget = java_lang_invoke_MemberName::vmtarget(mname);
- int vmindex = java_lang_invoke_MemberName::vmindex(mname);
- if (vmindex == VM_INDEX_UNINITIALIZED) return empty; // not resolved
- methodHandle m = decode_vmtarget(vmtarget, vmindex, NULL, receiver_limit_result, decode_flags_result);
- oop clazz = java_lang_invoke_MemberName::clazz(mname);
- if (clazz != NULL && java_lang_Class::is_instance(clazz)) {
- klassOop klass = java_lang_Class::as_klassOop(clazz);
- if (klass != NULL) receiver_limit_result = klass;
+// JVM 2.9 Special Methods:
+// A method is signature polymorphic if and only if all of the following conditions hold :
+// * It is declared in the java.lang.invoke.MethodHandle class.
+// * It has a single formal parameter of type Object[].
+// * It has a return type of Object.
+// * It has the ACC_VARARGS and ACC_NATIVE flags set.
+bool MethodHandles::is_method_handle_invoke_name(klassOop klass, Symbol* name) {
+ if (klass == NULL)
+ return false;
+ // The following test will fail spuriously during bootstrap of MethodHandle itself:
+ // if (klass != SystemDictionary::MethodHandle_klass())
+ // Test the name instead:
+ if (Klass::cast(klass)->name() != vmSymbols::java_lang_invoke_MethodHandle())
+ return false;
+ Symbol* poly_sig = vmSymbols::object_array_object_signature();
+ methodOop m = instanceKlass::cast(klass)->find_method(name, poly_sig);
+ if (m == NULL) return false;
+ int required = JVM_ACC_NATIVE | JVM_ACC_VARARGS;
+ int flags = m->access_flags().as_int();
+ return (flags & required) == required;
+}
+
+
+Symbol* MethodHandles::signature_polymorphic_intrinsic_name(vmIntrinsics::ID iid) {
+ assert(is_signature_polymorphic_intrinsic(iid), err_msg("iid=%d", iid));
+ switch (iid) {
+ case vmIntrinsics::_invokeBasic: return vmSymbols::invokeBasic_name();
+ case vmIntrinsics::_linkToVirtual: return vmSymbols::linkToVirtual_name();
+ case vmIntrinsics::_linkToStatic: return vmSymbols::linkToStatic_name();
+ case vmIntrinsics::_linkToSpecial: return vmSymbols::linkToSpecial_name();
+ case vmIntrinsics::_linkToInterface: return vmSymbols::linkToInterface_name();
}
- return m;
+ assert(false, "");
+ return 0;
}
+int MethodHandles::signature_polymorphic_intrinsic_ref_kind(vmIntrinsics::ID iid) {
+ switch (iid) {
+ case vmIntrinsics::_invokeBasic: return 0;
+ case vmIntrinsics::_linkToVirtual: return JVM_REF_invokeVirtual;
+ case vmIntrinsics::_linkToStatic: return JVM_REF_invokeStatic;
+ case vmIntrinsics::_linkToSpecial: return JVM_REF_invokeSpecial;
+ case vmIntrinsics::_linkToInterface: return JVM_REF_invokeInterface;
+ }
+ assert(false, err_msg("iid=%d", iid));
+ return 0;
+}
+
+vmIntrinsics::ID MethodHandles::signature_polymorphic_name_id(Symbol* name) {
+ vmSymbols::SID name_id = vmSymbols::find_sid(name);
+ switch (name_id) {
+ // The ID _invokeGeneric stands for all non-static signature-polymorphic methods, except built-ins.
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(invoke_name): return vmIntrinsics::_invokeGeneric;
+ // The only built-in non-static signature-polymorphic method is MethodHandle.invokeBasic:
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(invokeBasic_name): return vmIntrinsics::_invokeBasic;
+
+ // There is one static signature-polymorphic method for each JVM invocation mode.
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(linkToVirtual_name): return vmIntrinsics::_linkToVirtual;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(linkToStatic_name): return vmIntrinsics::_linkToStatic;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(linkToSpecial_name): return vmIntrinsics::_linkToSpecial;
+ case vmSymbols::VM_SYMBOL_ENUM_NAME(linkToInterface_name): return vmIntrinsics::_linkToInterface;
+ }
+
+ // Cover the case of invokeExact and any future variants of invokeFoo.
+ klassOop mh_klass = SystemDictionary::well_known_klass(
+ SystemDictionary::WK_KLASS_ENUM_NAME(MethodHandle_klass) );
+ if (mh_klass != NULL && is_method_handle_invoke_name(mh_klass, name))
+ return vmIntrinsics::_invokeGeneric;
+
+ // Note: The pseudo-intrinsic _compiledLambdaForm is never linked against.
+ // Instead it is used to mark lambda forms bound to invokehandle or invokedynamic.
+ return vmIntrinsics::_none;
+}
+
+vmIntrinsics::ID MethodHandles::signature_polymorphic_name_id(klassOop klass, Symbol* name) {
+ if (klass != NULL &&
+ Klass::cast(klass)->name() == vmSymbols::java_lang_invoke_MethodHandle()) {
+ vmIntrinsics::ID iid = signature_polymorphic_name_id(name);
+ if (iid != vmIntrinsics::_none)
+ return iid;
+ if (is_method_handle_invoke_name(klass, name))
+ return vmIntrinsics::_invokeGeneric;
+ }
+ return vmIntrinsics::_none;
+}
+
+
// convert the external string or reflective type to an internal signature
-Symbol* MethodHandles::convert_to_signature(oop type_str, bool polymorphic, TRAPS) {
+Symbol* MethodHandles::lookup_signature(oop type_str, bool intern_if_not_found, TRAPS) {
if (java_lang_invoke_MethodType::is_instance(type_str)) {
- return java_lang_invoke_MethodType::as_signature(type_str, polymorphic, CHECK_NULL);
+ return java_lang_invoke_MethodType::as_signature(type_str, intern_if_not_found, CHECK_NULL);
} else if (java_lang_Class::is_instance(type_str)) {
return java_lang_Class::as_signature(type_str, false, CHECK_NULL);
} else if (java_lang_String::is_instance(type_str)) {
- if (polymorphic) {
+ if (intern_if_not_found) {
return java_lang_String::as_symbol(type_str, CHECK_NULL);
} else {
return java_lang_String::as_symbol_or_null(type_str);
@@ -560,121 +391,303 @@
}
}
+static const char OBJ_SIG[] = "Ljava/lang/Object;";
+enum { OBJ_SIG_LEN = 18 };
+
+bool MethodHandles::is_basic_type_signature(Symbol* sig) {
+ assert(vmSymbols::object_signature()->utf8_length() == (int)OBJ_SIG_LEN, "");
+ assert(vmSymbols::object_signature()->equals(OBJ_SIG), "");
+ const int len = sig->utf8_length();
+ for (int i = 0; i < len; i++) {
+ switch (sig->byte_at(i)) {
+ case 'L':
+ // only java/lang/Object is valid here
+ if (sig->index_of_at(i, OBJ_SIG, OBJ_SIG_LEN) != i)
+ return false;
+ i += OBJ_SIG_LEN-1; //-1 because of i++ in loop
+ continue;
+ case '(': case ')': case 'V':
+ case 'I': case 'J': case 'F': case 'D':
+ continue;
+ //case '[':
+ //case 'Z': case 'B': case 'C': case 'S':
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+Symbol* MethodHandles::lookup_basic_type_signature(Symbol* sig, bool keep_last_arg, TRAPS) {
+ Symbol* bsig = NULL;
+ if (sig == NULL) {
+ return sig;
+ } else if (is_basic_type_signature(sig)) {
+ sig->increment_refcount();
+ return sig; // that was easy
+ } else if (sig->byte_at(0) != '(') {
+ BasicType bt = char2type(sig->byte_at(0));
+ if (is_subword_type(bt)) {
+ bsig = vmSymbols::int_signature();
+ } else {
+ assert(bt == T_OBJECT || bt == T_ARRAY, "is_basic_type_signature was false");
+ bsig = vmSymbols::object_signature();
+ }
+ } else {
+ ResourceMark rm;
+ stringStream buffer(128);
+ buffer.put('(');
+ int arg_pos = 0, keep_arg_pos = -1;
+ if (keep_last_arg)
+ keep_arg_pos = ArgumentCount(sig).size() - 1;
+ for (SignatureStream ss(sig); !ss.is_done(); ss.next()) {
+ BasicType bt = ss.type();
+ size_t this_arg_pos = buffer.size();
+ if (ss.at_return_type()) {
+ buffer.put(')');
+ }
+ if (arg_pos == keep_arg_pos) {
+ buffer.write((char*) ss.raw_bytes(),
+ (int) ss.raw_length());
+ } else if (bt == T_OBJECT || bt == T_ARRAY) {
+ buffer.write(OBJ_SIG, OBJ_SIG_LEN);
+ } else {
+ if (is_subword_type(bt))
+ bt = T_INT;
+ buffer.put(type2char(bt));
+ }
+ arg_pos++;
+ }
+ const char* sigstr = buffer.base();
+ int siglen = (int) buffer.size();
+ bsig = SymbolTable::new_symbol(sigstr, siglen, THREAD);
+ }
+ assert(is_basic_type_signature(bsig) ||
+ // detune assert in case the injected argument is not a basic type:
+ keep_last_arg, "");
+ return bsig;
+}
+
+void MethodHandles::print_as_basic_type_signature_on(outputStream* st,
+ Symbol* sig,
+ bool keep_arrays,
+ bool keep_basic_names) {
+ st = st ? st : tty;
+ int len = sig->utf8_length();
+ int array = 0;
+ bool prev_type = false;
+ for (int i = 0; i < len; i++) {
+ char ch = sig->byte_at(i);
+ switch (ch) {
+ case '(': case ')':
+ prev_type = false;
+ st->put(ch);
+ continue;
+ case '[':
+ if (!keep_basic_names && keep_arrays)
+ st->put(ch);
+ array++;
+ continue;
+ case 'L':
+ {
+ if (prev_type) st->put(',');
+ int start = i+1, slash = start;
+ while (++i < len && (ch = sig->byte_at(i)) != ';') {
+ if (ch == '/' || ch == '.' || ch == '$') slash = i+1;
+ }
+ if (slash < i) start = slash;
+ if (!keep_basic_names) {
+ st->put('L');
+ } else {
+ for (int j = start; j < i; j++)
+ st->put(sig->byte_at(j));
+ prev_type = true;
+ }
+ break;
+ }
+ default:
+ {
+ if (array && char2type(ch) != T_ILLEGAL && !keep_arrays) {
+ ch = '[';
+ array = 0;
+ }
+ if (prev_type) st->put(',');
+ const char* n = NULL;
+ if (keep_basic_names)
+ n = type2name(char2type(ch));
+ if (n == NULL) {
+ // unknown letter, or we don't want to know its name
+ st->put(ch);
+ } else {
+ st->print(n);
+ prev_type = true;
+ }
+ break;
+ }
+ }
+ // Switch break goes here to take care of array suffix:
+ if (prev_type) {
+ while (array > 0) {
+ st->print("[]");
+ --array;
+ }
+ }
+ array = 0;
+ }
+}
+
+
+
+static oop object_java_mirror() {
+ return Klass::cast(SystemDictionary::Object_klass())->java_mirror();
+}
+
+static oop field_name_or_null(Symbol* s) {
+ if (s == NULL) return NULL;
+ return StringTable::lookup(s);
+}
+
+static oop field_signature_type_or_null(Symbol* s) {
+ if (s == NULL) return NULL;
+ BasicType bt = FieldType::basic_type(s);
+ if (is_java_primitive(bt)) {
+ assert(s->utf8_length() == 1, "");
+ return java_lang_Class::primitive_mirror(bt);
+ }
+ // Here are some more short cuts for common types.
+ // They are optional, since reference types can be resolved lazily.
+ if (bt == T_OBJECT) {
+ if (s == vmSymbols::object_signature()) {
+ return object_java_mirror();
+ } else if (s == vmSymbols::class_signature()) {
+ return Klass::cast(SystemDictionary::Class_klass())->java_mirror();
+ } else if (s == vmSymbols::string_signature()) {
+ return Klass::cast(SystemDictionary::String_klass())->java_mirror();
+ } else {
+ int len = s->utf8_length();
+ if (s->byte_at(0) == 'L' && s->byte_at(len-1) == ';') {
+ TempNewSymbol cname = SymbolTable::probe((const char*)&s->bytes()[1], len-2);
+ if (cname == NULL) return NULL;
+ klassOop wkk = SystemDictionary::find_well_known_klass(cname);
+ if (wkk == NULL) return NULL;
+ return Klass::cast(wkk)->java_mirror();
+ }
+ }
+ }
+ return NULL;
+}
+
// An unresolved member name is a mere symbolic reference.
// Resolving it plants a vmtarget/vmindex in it,
// which refers dirctly to JVM internals.
-void MethodHandles::resolve_MemberName(Handle mname, TRAPS) {
+Handle MethodHandles::resolve_MemberName(Handle mname, TRAPS) {
+ Handle empty;
assert(java_lang_invoke_MemberName::is_instance(mname()), "");
-#ifdef ASSERT
- // If this assert throws, renegotiate the sentinel value used by the Java code,
- // so that it is distinct from any valid vtable index value, and any special
- // values defined in methodOopDesc::VtableIndexFlag.
- // The point of the slop is to give the Java code and the JVM some room
- // to independently specify sentinel values.
- const int sentinel_slop = 10;
- const int sentinel_limit = methodOopDesc::highest_unused_vtable_index_value - sentinel_slop;
- assert(VM_INDEX_UNINITIALIZED < sentinel_limit, "Java sentinel != JVM sentinels");
-#endif
- if (java_lang_invoke_MemberName::vmindex(mname()) != VM_INDEX_UNINITIALIZED)
- return; // already resolved
+
+ if (java_lang_invoke_MemberName::vmtarget(mname()) != NULL) {
+ // Already resolved.
+ DEBUG_ONLY(int vmindex = java_lang_invoke_MemberName::vmindex(mname()));
+ assert(vmindex >= methodOopDesc::nonvirtual_vtable_index, "");
+ return mname;
+ }
+
Handle defc_oop(THREAD, java_lang_invoke_MemberName::clazz(mname()));
Handle name_str(THREAD, java_lang_invoke_MemberName::name( mname()));
Handle type_str(THREAD, java_lang_invoke_MemberName::type( mname()));
int flags = java_lang_invoke_MemberName::flags(mname());
+ int ref_kind = (flags >> REFERENCE_KIND_SHIFT) & REFERENCE_KIND_MASK;
+ if (!ref_kind_is_valid(ref_kind)) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "obsolete MemberName format", empty);
+ }
+
+ DEBUG_ONLY(int old_vmindex);
+ assert((old_vmindex = java_lang_invoke_MemberName::vmindex(mname())) == 0, "clean input");
if (defc_oop.is_null() || name_str.is_null() || type_str.is_null()) {
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to resolve");
+ THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "nothing to resolve", empty);
}
instanceKlassHandle defc;
{
klassOop defc_klassOop = java_lang_Class::as_klassOop(defc_oop());
- if (defc_klassOop == NULL) return; // a primitive; no resolution possible
+ if (defc_klassOop == NULL) return empty; // a primitive; no resolution possible
if (!Klass::cast(defc_klassOop)->oop_is_instance()) {
- if (!Klass::cast(defc_klassOop)->oop_is_array()) return;
+ if (!Klass::cast(defc_klassOop)->oop_is_array()) return empty;
defc_klassOop = SystemDictionary::Object_klass();
}
defc = instanceKlassHandle(THREAD, defc_klassOop);
}
if (defc.is_null()) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "primitive class");
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "primitive class", empty);
}
- defc->link_class(CHECK); // possible safepoint
+ defc->link_class(CHECK_(empty)); // possible safepoint
// convert the external string name to an internal symbol
TempNewSymbol name = java_lang_String::as_symbol_or_null(name_str());
- if (name == NULL) return; // no such name
+ if (name == NULL) return empty; // no such name
if (name == vmSymbols::class_initializer_name())
- return; // illegal name
+ return empty; // illegal name
- Handle polymorphic_method_type;
- bool polymorphic_signature = false;
+ vmIntrinsics::ID mh_invoke_id = vmIntrinsics::_none;
if ((flags & ALL_KINDS) == IS_METHOD &&
- (defc() == SystemDictionary::MethodHandle_klass() &&
- methodOopDesc::is_method_handle_invoke_name(name))) {
- polymorphic_signature = true;
+ (defc() == SystemDictionary::MethodHandle_klass()) &&
+ (ref_kind == JVM_REF_invokeVirtual ||
+ ref_kind == JVM_REF_invokeSpecial ||
+ // static invocation mode is required for _linkToVirtual, etc.:
+ ref_kind == JVM_REF_invokeStatic)) {
+ vmIntrinsics::ID iid = signature_polymorphic_name_id(name);
+ if (iid != vmIntrinsics::_none &&
+ ((ref_kind == JVM_REF_invokeStatic) == is_signature_polymorphic_static(iid))) {
+ // Virtual methods invoke and invokeExact, plus internal invokers like _invokeBasic.
+ // For a static reference it could an internal linkage routine like _linkToVirtual, etc.
+ mh_invoke_id = iid;
+ }
}
// convert the external string or reflective type to an internal signature
- TempNewSymbol type = convert_to_signature(type_str(), polymorphic_signature, CHECK);
- if (java_lang_invoke_MethodType::is_instance(type_str()) && polymorphic_signature) {
- polymorphic_method_type = type_str; // preserve exactly
- }
- if (type == NULL) return; // no such signature exists in the VM
+ TempNewSymbol type = lookup_signature(type_str(), (mh_invoke_id != vmIntrinsics::_none), CHECK_(empty));
+ if (type == NULL) return empty; // no such signature exists in the VM
// Time to do the lookup.
switch (flags & ALL_KINDS) {
case IS_METHOD:
{
CallInfo result;
+ bool do_dispatch = true; // default, neutral setting
{
- EXCEPTION_MARK;
- if ((flags & JVM_ACC_STATIC) != 0) {
+ assert(!HAS_PENDING_EXCEPTION, "");
+ if (ref_kind == JVM_REF_invokeStatic) {
+ //do_dispatch = false; // no need, since statics are never dispatched
LinkResolver::resolve_static_call(result,
defc, name, type, KlassHandle(), false, false, THREAD);
- } else if (defc->is_interface()) {
+ } else if (ref_kind == JVM_REF_invokeInterface) {
LinkResolver::resolve_interface_call(result, Handle(), defc,
defc, name, type, KlassHandle(), false, false, THREAD);
- } else {
+ } else if (mh_invoke_id != vmIntrinsics::_none) {
+ assert(!is_signature_polymorphic_static(mh_invoke_id), "");
+ LinkResolver::resolve_handle_call(result,
+ defc, name, type, KlassHandle(), THREAD);
+ } else if (ref_kind == JVM_REF_invokeSpecial) {
+ do_dispatch = false; // force non-virtual linkage
+ LinkResolver::resolve_special_call(result,
+ defc, name, type, KlassHandle(), false, THREAD);
+ } else if (ref_kind == JVM_REF_invokeVirtual) {
LinkResolver::resolve_virtual_call(result, Handle(), defc,
defc, name, type, KlassHandle(), false, false, THREAD);
+ } else {
+ assert(false, err_msg("ref_kind=%d", ref_kind));
}
if (HAS_PENDING_EXCEPTION) {
- CLEAR_PENDING_EXCEPTION;
- break; // go to second chance
+ return empty;
}
}
- methodHandle m = result.resolved_method();
- oop vmtarget = NULL;
- int vmindex = methodOopDesc::nonvirtual_vtable_index;
- if (defc->is_interface()) {
- vmindex = klassItable::compute_itable_index(m());
- assert(vmindex >= 0, "");
- } else if (result.has_vtable_index()) {
- vmindex = result.vtable_index();
- assert(vmindex >= 0, "");
- }
- assert(vmindex != VM_INDEX_UNINITIALIZED, "");
- if (vmindex < 0) {
- assert(result.is_statically_bound(), "");
- vmtarget = m();
- } else {
- vmtarget = result.resolved_klass()->as_klassOop();
- }
- int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS);
- java_lang_invoke_MemberName::set_vmtarget(mname(), vmtarget);
- java_lang_invoke_MemberName::set_vmindex(mname(), vmindex);
- java_lang_invoke_MemberName::set_modifiers(mname(), mods);
- DEBUG_ONLY(KlassHandle junk1; int junk2);
- assert(decode_MemberName(mname(), junk1, junk2) == result.resolved_method(),
- "properly stored for later decoding");
- return;
+ return init_method_MemberName(mname(), result, THREAD);
}
case IS_CONSTRUCTOR:
{
CallInfo result;
{
- EXCEPTION_MARK;
+ assert(!HAS_PENDING_EXCEPTION, "");
if (name == vmSymbols::object_initializer_name()) {
LinkResolver::resolve_special_call(result,
defc, name, type, KlassHandle(), false, THREAD);
@@ -682,22 +695,11 @@
break; // will throw after end of switch
}
if (HAS_PENDING_EXCEPTION) {
- CLEAR_PENDING_EXCEPTION;
- return;
+ return empty;
}
}
assert(result.is_statically_bound(), "");
- methodHandle m = result.resolved_method();
- oop vmtarget = m();
- int vmindex = methodOopDesc::nonvirtual_vtable_index;
- int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS);
- java_lang_invoke_MemberName::set_vmtarget(mname(), vmtarget);
- java_lang_invoke_MemberName::set_vmindex(mname(), vmindex);
- java_lang_invoke_MemberName::set_modifiers(mname(), mods);
- DEBUG_ONLY(KlassHandle junk1; int junk2);
- assert(decode_MemberName(mname(), junk1, junk2) == result.resolved_method(),
- "properly stored for later decoding");
- return;
+ return init_method_MemberName(mname(), result, THREAD);
}
case IS_FIELD:
{
@@ -705,54 +707,20 @@
fieldDescriptor fd; // find_field initializes fd if found
KlassHandle sel_klass(THREAD, instanceKlass::cast(defc())->find_field(name, type, &fd));
// check if field exists; i.e., if a klass containing the field def has been selected
- if (sel_klass.is_null()) return;
- oop vmtarget = sel_klass->as_klassOop();
- int vmindex = fd.offset();
- int mods = (fd.access_flags().as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS);
- if (vmindex == VM_INDEX_UNINITIALIZED) break; // should not happen
- java_lang_invoke_MemberName::set_vmtarget(mname(), vmtarget);
- java_lang_invoke_MemberName::set_vmindex(mname(), vmindex);
- java_lang_invoke_MemberName::set_modifiers(mname(), mods);
- return;
+ if (sel_klass.is_null()) return empty; // should not happen
+ oop type = field_signature_type_or_null(fd.signature());
+ oop name = field_name_or_null(fd.name());
+ bool is_setter = (ref_kind_is_valid(ref_kind) && ref_kind_is_setter(ref_kind));
+ mname = Handle(THREAD,
+ init_field_MemberName(mname(), sel_klass->as_klassOop(),
+ fd.access_flags(), type, name, fd.offset(), is_setter));
+ return mname;
}
default:
- THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format");
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format", empty);
}
- // Second chance.
- if (polymorphic_method_type.not_null()) {
- // Look on a non-null class loader.
- Handle cur_class_loader;
- const int nptypes = java_lang_invoke_MethodType::ptype_count(polymorphic_method_type());
- for (int i = 0; i <= nptypes; i++) {
- oop type_mirror;
- if (i < nptypes) type_mirror = java_lang_invoke_MethodType::ptype(polymorphic_method_type(), i);
- else type_mirror = java_lang_invoke_MethodType::rtype(polymorphic_method_type());
- klassOop example_type = java_lang_Class::as_klassOop(type_mirror);
- if (example_type == NULL) continue;
- oop class_loader = Klass::cast(example_type)->class_loader();
- if (class_loader == NULL || class_loader == cur_class_loader()) continue;
- cur_class_loader = Handle(THREAD, class_loader);
- methodOop m = SystemDictionary::find_method_handle_invoke(name,
- type,
- KlassHandle(THREAD, example_type),
- THREAD);
- if (HAS_PENDING_EXCEPTION) {
- CLEAR_PENDING_EXCEPTION;
- m = NULL;
- // try again with a different class loader...
- }
- if (m != NULL &&
- m->is_method_handle_invoke() &&
- java_lang_invoke_MethodType::equals(polymorphic_method_type(), m->method_handle_type())) {
- int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS);
- java_lang_invoke_MemberName::set_vmtarget(mname(), m);
- java_lang_invoke_MemberName::set_vmindex(mname(), m->vtable_index());
- java_lang_invoke_MemberName::set_modifiers(mname(), mods);
- return;
- }
- }
- }
+ return empty;
}
// Conversely, a member name which is only initialized from JVM internals
@@ -763,7 +731,7 @@
assert(java_lang_invoke_MemberName::is_instance(mname()), "");
oop vmtarget = java_lang_invoke_MemberName::vmtarget(mname());
int vmindex = java_lang_invoke_MemberName::vmindex(mname());
- if (vmtarget == NULL || vmindex == VM_INDEX_UNINITIALIZED) {
+ if (vmtarget == NULL) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to expand");
}
@@ -784,14 +752,12 @@
case IS_METHOD:
case IS_CONSTRUCTOR:
{
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = decode_vmtarget(vmtarget, vmindex, NULL, receiver_limit, decode_flags);
+ assert(vmtarget->is_method(), "method or constructor vmtarget is methodOop");
+ methodHandle m(THREAD, methodOop(vmtarget));
+ DEBUG_ONLY(vmtarget = NULL); // safety
if (m.is_null()) break;
if (!have_defc) {
klassOop defc = m->method_holder();
- if (receiver_limit.not_null() && receiver_limit() != defc
- && Klass::cast(receiver_limit())->is_subtype_of(defc))
- defc = receiver_limit();
java_lang_invoke_MemberName::set_clazz(mname(), Klass::cast(defc)->java_mirror());
}
if (!have_name) {
@@ -808,9 +774,10 @@
case IS_FIELD:
{
// This is taken from LinkResolver::resolve_field, sans access checks.
- if (!vmtarget->is_klass()) break;
+ assert(vmtarget->is_klass(), "field vmtarget is klassOop");
if (!Klass::cast((klassOop) vmtarget)->oop_is_instance()) break;
instanceKlassHandle defc(THREAD, (klassOop) vmtarget);
+ DEBUG_ONLY(vmtarget = NULL); // safety
bool is_static = ((flags & JVM_ACC_STATIC) != 0);
fieldDescriptor fd; // find_field initializes fd if found
if (!defc->find_field_from_offset(vmindex, is_static, &fd))
@@ -824,7 +791,11 @@
java_lang_invoke_MemberName::set_name(mname(), name());
}
if (!have_type) {
- Handle type = java_lang_String::create_from_symbol(fd.signature(), CHECK);
+ // If it is a primitive field type, don't mess with short strings like "I".
+ Handle type = field_signature_type_or_null(fd.signature());
+ if (type.is_null()) {
+ java_lang_String::create_from_symbol(fd.signature(), CHECK);
+ }
java_lang_invoke_MemberName::set_type(mname(), type());
}
return;
@@ -882,7 +853,13 @@
oop result = results->obj_at(rfill++);
if (!java_lang_invoke_MemberName::is_instance(result))
return -99; // caller bug!
- MethodHandles::init_MemberName(result, st.klass()->as_klassOop(), st.access_flags(), st.offset());
+ oop type = field_signature_type_or_null(st.signature());
+ oop name = field_name_or_null(st.name());
+ oop saved = MethodHandles::init_field_MemberName(result, st.klass()->as_klassOop(),
+ st.access_flags(), type, name,
+ st.offset());
+ if (saved != result)
+ results->obj_at_put(rfill-1, saved); // show saved instance to user
} else if (++overflow >= overflow_limit) {
match_flags = 0; break; // got tired of looking at overflow
}
@@ -930,7 +907,9 @@
oop result = results->obj_at(rfill++);
if (!java_lang_invoke_MemberName::is_instance(result))
return -99; // caller bug!
- MethodHandles::init_MemberName(result, m, true);
+ oop saved = MethodHandles::init_method_MemberName(result, m, true, NULL);
+ if (saved != result)
+ results->obj_at_put(rfill-1, saved); // show saved instance to user
} else if (++overflow >= overflow_limit) {
match_flags = 0; break; // got tired of looking at overflow
}
@@ -941,1925 +920,16 @@
return rfill + overflow;
}
-
-// Decode this java.lang.Class object into an instanceKlass, if possible.
-// Throw IAE if not
-instanceKlassHandle MethodHandles::resolve_instance_klass(oop java_mirror_oop, TRAPS) {
- instanceKlassHandle empty;
- klassOop caller = NULL;
- if (java_lang_Class::is_instance(java_mirror_oop)) {
- caller = java_lang_Class::as_klassOop(java_mirror_oop);
- }
- if (caller == NULL || !Klass::cast(caller)->oop_is_instance()) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "not a class", empty);
- }
- return instanceKlassHandle(THREAD, caller);
-}
-
-
-
-// Decode the vmtarget field of a method handle.
-// Sanitize out methodOops, klassOops, and any other non-Java data.
-// This is for debugging and reflection.
-oop MethodHandles::encode_target(Handle mh, int format, TRAPS) {
- assert(java_lang_invoke_MethodHandle::is_instance(mh()), "must be a MH");
- if (format == ETF_FORCE_DIRECT_HANDLE ||
- format == ETF_COMPILE_DIRECT_HANDLE) {
- // Internal function for stress testing.
- Handle mt = java_lang_invoke_MethodHandle::type(mh());
- int invocation_count = 10000;
- TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(mt(), true, CHECK_NULL);
- bool omit_receiver_argument = true;
- MethodHandleCompiler mhc(mh, vmSymbols::invoke_name(), signature, invocation_count, omit_receiver_argument, CHECK_NULL);
- methodHandle m = mhc.compile(CHECK_NULL);
- if (StressMethodHandleWalk && Verbose || PrintMiscellaneous) {
- tty->print_cr("MethodHandleNatives.getTarget(%s)",
- format == ETF_FORCE_DIRECT_HANDLE ? "FORCE_DIRECT" : "COMPILE_DIRECT");
- if (Verbose) {
- m->print_codes();
- }
- }
- if (StressMethodHandleWalk) {
- InterpreterOopMap mask;
- OopMapCache::compute_one_oop_map(m, m->code_size() - 1, &mask);
- }
- if ((format == ETF_COMPILE_DIRECT_HANDLE ||
- CompilationPolicy::must_be_compiled(m))
- && !instanceKlass::cast(m->method_holder())->is_not_initialized()
- && CompilationPolicy::can_be_compiled(m)) {
- // Force compilation
- CompileBroker::compile_method(m, InvocationEntryBci,
- CompilationPolicy::policy()->initial_compile_level(),
- methodHandle(), 0, "MethodHandleNatives.getTarget",
- CHECK_NULL);
- }
- // Now wrap m in a DirectMethodHandle.
- instanceKlassHandle dmh_klass(THREAD, SystemDictionary::DirectMethodHandle_klass());
- Handle dmh = dmh_klass->allocate_instance_handle(CHECK_NULL);
- JavaValue ignore_result(T_VOID);
- Symbol* init_name = vmSymbols::object_initializer_name();
- Symbol* init_sig = vmSymbols::notifyGenericMethodType_signature();
- JavaCalls::call_special(&ignore_result, dmh,
- SystemDictionaryHandles::MethodHandle_klass(), init_name, init_sig,
- java_lang_invoke_MethodHandle::type(mh()), CHECK_NULL);
- MethodHandles::init_DirectMethodHandle(dmh, m, false, CHECK_NULL);
- return dmh();
- }
- if (format == ETF_HANDLE_OR_METHOD_NAME) {
- oop target = java_lang_invoke_MethodHandle::vmtarget(mh());
- if (target == NULL) {
- return NULL; // unformed MH
- }
- klassOop tklass = target->klass();
- if (Klass::cast(tklass)->is_subclass_of(SystemDictionary::Object_klass())) {
- return target; // target is another MH (or something else?)
- }
- }
- if (format == ETF_DIRECT_HANDLE) {
- oop target = mh();
- for (;;) {
- if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) {
- return target;
- }
- if (!java_lang_invoke_MethodHandle::is_instance(target)){
- return NULL; // unformed MH
- }
- target = java_lang_invoke_MethodHandle::vmtarget(target);
- }
- }
- // cases of metadata in MH.vmtarget:
- // - AMH can have methodOop for static invoke with bound receiver
- // - DMH can have methodOop for static invoke (on variable receiver)
- // - DMH can have klassOop for dispatched (non-static) invoke
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = decode_MethodHandle(mh(), receiver_limit, decode_flags);
- if (m.is_null()) return NULL;
- switch (format) {
- case ETF_REFLECT_METHOD:
- // same as jni_ToReflectedMethod:
- if (m->is_initializer()) {
- return Reflection::new_constructor(m, THREAD);
- } else {
- return Reflection::new_method(m, UseNewReflection, false, THREAD);
- }
-
- case ETF_HANDLE_OR_METHOD_NAME: // method, not handle
- case ETF_METHOD_NAME:
- {
- if (SystemDictionary::MemberName_klass() == NULL) break;
- instanceKlassHandle mname_klass(THREAD, SystemDictionary::MemberName_klass());
- mname_klass->initialize(CHECK_NULL);
- Handle mname = mname_klass->allocate_instance_handle(CHECK_NULL); // possible safepoint
- java_lang_invoke_MemberName::set_vmindex(mname(), VM_INDEX_UNINITIALIZED);
- bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0);
- init_MemberName(mname(), m(), do_dispatch);
- expand_MemberName(mname, 0, CHECK_NULL);
- return mname();
- }
- }
-
- // Unknown format code.
- char msg[50];
- jio_snprintf(msg, sizeof(msg), "unknown getTarget format=%d", format);
- THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), msg);
-}
-
-static const char* always_null_names[] = {
- "java/lang/Void",
- "java/lang/Null",
- //"java/lang/Nothing",
- "sun/dyn/empty/Empty",
- "sun/invoke/empty/Empty",
- NULL
-};
-
-static bool is_always_null_type(klassOop klass) {
- if (klass == NULL) return false; // safety
- if (!Klass::cast(klass)->oop_is_instance()) return false;
- instanceKlass* ik = instanceKlass::cast(klass);
- // Must be on the boot class path:
- if (ik->class_loader() != NULL) return false;
- // Check the name.
- Symbol* name = ik->name();
- for (int i = 0; ; i++) {
- const char* test_name = always_null_names[i];
- if (test_name == NULL) break;
- if (name->equals(test_name))
- return true;
- }
- return false;
-}
-
-bool MethodHandles::class_cast_needed(klassOop src, klassOop dst) {
- if (dst == NULL) return true;
- if (src == NULL) return (dst != SystemDictionary::Object_klass());
- if (src == dst || dst == SystemDictionary::Object_klass())
- return false; // quickest checks
- Klass* srck = Klass::cast(src);
- Klass* dstk = Klass::cast(dst);
- if (dstk->is_interface()) {
- // interface receivers can safely be viewed as untyped,
- // because interface calls always include a dynamic check
- //dstk = Klass::cast(SystemDictionary::Object_klass());
- return false;
- }
- if (srck->is_interface()) {
- // interface arguments must be viewed as untyped
- //srck = Klass::cast(SystemDictionary::Object_klass());
- return true;
- }
- if (is_always_null_type(src)) {
- // some source types are known to be never instantiated;
- // they represent references which are always null
- // such null references never fail to convert safely
- return false;
- }
- return !srck->is_subclass_of(dstk->as_klassOop());
-}
-
-static oop object_java_mirror() {
- 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,
- bool for_return) {
- if (for_return) {
- // return values can always be forgotten:
- if (dst == T_VOID) return true;
- if (src == T_VOID) return raw && (dst == T_INT);
- // We allow caller to receive a garbage int, which is harmless.
- // This trick is pulled by trusted code (see VerifyType.canPassRaw).
- }
- assert(src != T_VOID && dst != T_VOID, "should not be here");
- if (src == dst) return true;
- if (type2size[src] != type2size[dst]) return false;
- if (src == T_OBJECT || dst == T_OBJECT) return false;
- if (raw) return true; // bitwise reinterpretation; caller guarantees safety
- // allow reinterpretation casts for integral widening
- if (is_subword_type(src)) { // subwords can fit in int or other subwords
- if (dst == T_INT) // any subword fits in an int
- return true;
- if (src == T_BOOLEAN) // boolean fits in any subword
- return is_subword_type(dst);
- if (src == T_BYTE && dst == T_SHORT)
- return true; // remaining case: byte fits in short
- }
- // allow float/fixed reinterpretation casts
- if (is_float_fixed_reinterpretation_cast(src, dst))
- return true;
- return false;
-}
-
-const char* MethodHandles::check_method_receiver(methodOop m,
- klassOop passed_recv_type) {
- assert(!m->is_static(), "caller resp.");
- if (passed_recv_type == NULL)
- return "receiver type is primitive";
- if (class_cast_needed(passed_recv_type, m->method_holder())) {
- Klass* formal = Klass::cast(m->method_holder());
- return SharedRuntime::generate_class_cast_message("receiver type",
- formal->external_name());
- }
- return NULL; // checks passed
-}
-
-// Verify that m's signature can be called type-safely by a method handle
-// of the given method type 'mtype'.
-// It takes a TRAPS argument because it must perform symbol lookups.
-void MethodHandles::verify_method_signature(methodHandle m,
- Handle mtype,
- int first_ptype_pos,
- KlassHandle insert_ptype,
- TRAPS) {
- Handle mhi_type;
- if (m->is_method_handle_invoke()) {
- // use this more exact typing instead of the symbolic signature:
- mhi_type = Handle(THREAD, m->method_handle_type());
- }
- objArrayHandle ptypes(THREAD, java_lang_invoke_MethodType::ptypes(mtype()));
- int pnum = first_ptype_pos;
- int pmax = ptypes->length();
- int anum = 0; // method argument
- const char* err = NULL;
- ResourceMark rm(THREAD);
- for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) {
- oop ptype_oop = NULL;
- if (ss.at_return_type()) {
- if (pnum != pmax)
- { err = "too many arguments"; break; }
- ptype_oop = java_lang_invoke_MethodType::rtype(mtype());
- } else {
- if (pnum >= pmax)
- { err = "not enough arguments"; break; }
- if (pnum >= 0)
- ptype_oop = ptypes->obj_at(pnum);
- else if (insert_ptype.is_null())
- ptype_oop = NULL;
- else
- ptype_oop = insert_ptype->java_mirror();
- pnum += 1;
- anum += 1;
- }
- KlassHandle pklass;
- BasicType ptype = T_OBJECT;
- bool have_ptype = false;
- // missing ptype_oop does not match any non-reference; use Object to report the error
- pklass = SystemDictionaryHandles::Object_klass();
- if (ptype_oop != NULL) {
- have_ptype = true;
- klassOop pklass_oop = NULL;
- ptype = java_lang_Class::as_BasicType(ptype_oop, &pklass_oop);
- pklass = KlassHandle(THREAD, pklass_oop);
- }
- ptype_oop = NULL; //done with this
- KlassHandle aklass;
- BasicType atype = ss.type();
- if (atype == T_ARRAY) atype = T_OBJECT; // fold all refs to T_OBJECT
- if (atype == T_OBJECT) {
- if (!have_ptype) {
- // null matches any reference
- continue;
- }
- if (mhi_type.is_null()) {
- // If we fail to resolve types at this point, we will usually throw an error.
- TempNewSymbol name = ss.as_symbol_or_null();
- if (name != NULL) {
- instanceKlass* mk = instanceKlass::cast(m->method_holder());
- Handle loader(THREAD, mk->class_loader());
- Handle domain(THREAD, mk->protection_domain());
- klassOop aklass_oop = SystemDictionary::resolve_or_null(name, loader, domain, CHECK);
- if (aklass_oop != NULL)
- aklass = KlassHandle(THREAD, aklass_oop);
- if (aklass.is_null() &&
- pklass.not_null() &&
- loader.is_null() &&
- pklass->name() == name)
- // accept name equivalence here, since that's the best we can do
- aklass = pklass;
- }
- } else {
- // for method handle invokers we don't look at the name in the signature
- oop atype_oop;
- if (ss.at_return_type())
- atype_oop = java_lang_invoke_MethodType::rtype(mhi_type());
- else
- atype_oop = java_lang_invoke_MethodType::ptype(mhi_type(), anum-1);
- klassOop aklass_oop = NULL;
- atype = java_lang_Class::as_BasicType(atype_oop, &aklass_oop);
- aklass = KlassHandle(THREAD, aklass_oop);
- }
- }
- if (!ss.at_return_type()) {
- err = check_argument_type_change(ptype, pklass(), atype, aklass(), anum);
- } else {
- err = check_return_type_change(atype, aklass(), ptype, pklass()); // note reversal!
- }
- if (err != NULL) break;
- }
-
- if (err != NULL) {
-#ifndef PRODUCT
- if (PrintMiscellaneous && (Verbose || WizardMode)) {
- tty->print("*** verify_method_signature failed: ");
- java_lang_invoke_MethodType::print_signature(mtype(), tty);
- tty->cr();
- tty->print_cr(" first_ptype_pos = %d, insert_ptype = "UINTX_FORMAT, first_ptype_pos, insert_ptype());
- tty->print(" Failing method: ");
- m->print();
- }
-#endif //PRODUCT
- THROW_MSG(vmSymbols::java_lang_InternalError(), err);
- }
-}
-
-// Main routine for verifying the MethodHandle.type of a proposed
-// direct or bound-direct method handle.
-void MethodHandles::verify_method_type(methodHandle m,
- Handle mtype,
- bool has_bound_recv,
- KlassHandle bound_recv_type,
- TRAPS) {
- bool m_needs_receiver = !m->is_static();
-
- const char* err = NULL;
-
- int first_ptype_pos = m_needs_receiver ? 1 : 0;
- if (has_bound_recv) {
- first_ptype_pos -= 1; // ptypes do not include the bound argument; start earlier in them
- if (m_needs_receiver && bound_recv_type.is_null())
- { err = "bound receiver is not an object"; goto die; }
- }
-
- if (m_needs_receiver && err == NULL) {
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(mtype());
- if (ptypes->length() < first_ptype_pos)
- { err = "receiver argument is missing"; goto die; }
- if (has_bound_recv)
- err = check_method_receiver(m(), bound_recv_type->as_klassOop());
- else
- err = check_method_receiver(m(), java_lang_Class::as_klassOop(ptypes->obj_at(first_ptype_pos-1)));
- if (err != NULL) goto die;
- }
-
- // Check the other arguments for mistypes.
- verify_method_signature(m, mtype, first_ptype_pos, bound_recv_type, CHECK);
- return;
-
- die:
- THROW_MSG(vmSymbols::java_lang_InternalError(), err);
-}
-
-void MethodHandles::verify_vmslots(Handle mh, TRAPS) {
- // Verify vmslots.
- int check_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(mh()));
- if (java_lang_invoke_MethodHandle::vmslots(mh()) != check_slots) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH");
- }
-}
-
-void MethodHandles::verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS) {
- // Verify that argslot points at the given argnum.
- int check_slot = argument_slot(java_lang_invoke_MethodHandle::type(mh()), argnum);
- if (argslot != check_slot || argslot < 0) {
- ResourceMark rm;
- const char* fmt = "for argnum of %d, vmargslot is %d, should be %d";
- size_t msglen = strlen(fmt) + 3*11 + 1;
- char* msg = NEW_RESOURCE_ARRAY(char, msglen);
- jio_snprintf(msg, msglen, fmt, argnum, argslot, check_slot);
- THROW_MSG(vmSymbols::java_lang_InternalError(), msg);
- }
-}
-
-// Verify the correspondence between two method types.
-// Apart from the advertised changes, caller method type X must
-// be able to invoke the callee method Y type with no violations
-// of type integrity.
-// Return NULL if all is well, else a short error message.
-const char* MethodHandles::check_method_type_change(oop src_mtype, int src_beg, int src_end,
- int insert_argnum, oop insert_type,
- int change_argnum, oop change_type,
- int delete_argnum,
- oop dst_mtype, int dst_beg, int dst_end,
- bool raw) {
- objArrayOop src_ptypes = java_lang_invoke_MethodType::ptypes(src_mtype);
- objArrayOop dst_ptypes = java_lang_invoke_MethodType::ptypes(dst_mtype);
-
- int src_max = src_ptypes->length();
- int dst_max = dst_ptypes->length();
-
- if (src_end == -1) src_end = src_max;
- if (dst_end == -1) dst_end = dst_max;
-
- assert(0 <= src_beg && src_beg <= src_end && src_end <= src_max, "oob");
- assert(0 <= dst_beg && dst_beg <= dst_end && dst_end <= dst_max, "oob");
-
- // pending actions; set to -1 when done:
- int ins_idx = insert_argnum, chg_idx = change_argnum, del_idx = delete_argnum;
-
- const char* err = NULL;
-
- // Walk along each array of parameter types, including a virtual
- // NULL end marker at the end of each.
- for (int src_idx = src_beg, dst_idx = dst_beg;
- (src_idx <= src_end && dst_idx <= dst_end);
- src_idx++, dst_idx++) {
- oop src_type = (src_idx == src_end) ? oop(NULL) : src_ptypes->obj_at(src_idx);
- oop dst_type = (dst_idx == dst_end) ? oop(NULL) : dst_ptypes->obj_at(dst_idx);
- bool fix_null_src_type = false;
-
- // Perform requested edits.
- if (ins_idx == src_idx) {
- // note that the inserted guy is never affected by a change or deletion
- ins_idx = -1;
- src_type = insert_type;
- fix_null_src_type = true;
- --src_idx; // back up to process src type on next loop
- src_idx = src_end;
- } else {
- // note that the changed guy can be immediately deleted
- if (chg_idx == src_idx) {
- chg_idx = -1;
- assert(src_idx < src_end, "oob");
- src_type = change_type;
- fix_null_src_type = true;
- }
- if (del_idx == src_idx) {
- del_idx = -1;
- assert(src_idx < src_end, "oob");
- --dst_idx;
- continue; // rerun loop after skipping this position
- }
- }
-
- if (src_type == NULL && fix_null_src_type)
- // explicit null in this case matches any dest reference
- src_type = (java_lang_Class::is_primitive(dst_type) ? object_java_mirror() : dst_type);
-
- // Compare the two argument types.
- if (src_type != dst_type) {
- if (src_type == NULL) return "not enough arguments";
- if (dst_type == NULL) return "too many arguments";
- err = check_argument_type_change(src_type, dst_type, dst_idx, raw);
- if (err != NULL) return err;
- }
- }
-
- // Now compare return types also.
- oop src_rtype = java_lang_invoke_MethodType::rtype(src_mtype);
- oop dst_rtype = java_lang_invoke_MethodType::rtype(dst_mtype);
- if (src_rtype != dst_rtype) {
- err = check_return_type_change(dst_rtype, src_rtype, raw); // note reversal!
- if (err != NULL) return err;
- }
-
- assert(err == NULL, "");
- return NULL; // all is well
-}
-
-
-const char* MethodHandles::check_argument_type_change(BasicType src_type,
- klassOop src_klass,
- BasicType dst_type,
- klassOop dst_klass,
- int argnum,
- bool raw) {
- const char* err = NULL;
- const bool for_return = (argnum < 0);
-
- // just in case:
- if (src_type == T_ARRAY) src_type = T_OBJECT;
- if (dst_type == T_ARRAY) dst_type = T_OBJECT;
-
- // Produce some nice messages if VerifyMethodHandles is turned on:
- if (!same_basic_type_for_arguments(src_type, dst_type, raw, for_return)) {
- if (src_type == T_OBJECT) {
- 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 = (!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 = (!for_return
- ? "type mismatch: passing a %s for method argument #%d, which expects %s"
- : "type mismatch: returning a %s, but caller expects %s");
- }
- } else if (src_type == T_OBJECT && dst_type == T_OBJECT &&
- class_cast_needed(src_klass, dst_klass)) {
- 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 = (!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 = (!for_return
- ? "reference mismatch: passing a %s for method argument #%d, which expects %s"
- : "reference mismatch: returning a %s, but caller expects %s");
- }
- } else {
- // passed the obstacle course
- return NULL;
- }
-
- // format, format, format
- const char* src_name = type2name(src_type);
- const char* dst_name = type2name(dst_type);
- if (src_name == NULL) src_name = "unknown type";
- if (dst_name == NULL) dst_name = "unknown type";
- if (src_type == T_OBJECT)
- src_name = (src_klass != NULL) ? Klass::cast(src_klass)->external_name() : "an unresolved class";
- if (dst_type == T_OBJECT)
- dst_name = (dst_klass != NULL) ? Klass::cast(dst_klass)->external_name() : "an unresolved class";
-
- size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11);
- char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1);
- if (!for_return) {
- assert(strstr(err, "%d") != NULL, "");
- jio_snprintf(msg, msglen, err, src_name, argnum, dst_name);
- } else {
- assert(strstr(err, "%d") == NULL, "");
- jio_snprintf(msg, msglen, err, src_name, dst_name);
- }
- return msg;
-}
-
-// Compute the depth within the stack of the given argument, i.e.,
-// the combined size of arguments to the right of the given argument.
-// For the last argument (ptypes.length-1) this will be zero.
-// For the first argument (0) this will be the size of all
-// arguments but that one. For the special number -1, this
-// will be the size of all arguments, including the first.
-// If the argument is neither -1 nor a valid argument index,
-// then return a negative number. Otherwise, the result
-// is in the range [0..vmslots] inclusive.
-int MethodHandles::argument_slot(oop method_type, int arg) {
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(method_type);
- int argslot = 0;
- int len = ptypes->length();
- if (arg < -1 || arg >= len) return -99;
- for (int i = len-1; i > arg; i--) {
- BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i));
- argslot += type2size[bt];
- }
- assert(argument_slot_to_argnum(method_type, argslot) == arg, "inverse works");
- return argslot;
-}
-
-// Given a slot number, return the argument number.
-int MethodHandles::argument_slot_to_argnum(oop method_type, int query_argslot) {
- objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(method_type);
- int argslot = 0;
- int len = ptypes->length();
- for (int i = len-1; i >= 0; i--) {
- if (query_argslot == argslot) return i;
- BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i));
- argslot += type2size[bt];
- }
- // return pseudo-arg deepest in stack:
- if (query_argslot == argslot) return -1;
- return -99; // oob slot, or splitting a double-slot arg
-}
-
-methodHandle MethodHandles::dispatch_decoded_method(methodHandle m,
- KlassHandle receiver_limit,
- int decode_flags,
- KlassHandle receiver_klass,
- TRAPS) {
- assert((decode_flags & ~_DMF_DIRECT_MASK) == 0, "must be direct method reference");
- assert((decode_flags & _dmf_has_receiver) != 0, "must have a receiver or first reference argument");
-
- if (!m->is_static() &&
- (receiver_klass.is_null() || !receiver_klass->is_subtype_of(m->method_holder())))
- // given type does not match class of method, or receiver is null!
- // caller should have checked this, but let's be extra careful...
- return methodHandle();
-
- if (receiver_limit.not_null() &&
- (receiver_klass.not_null() && !receiver_klass->is_subtype_of(receiver_limit())))
- // given type is not limited to the receiver type
- // note that a null receiver can match any reference value, for a static method
- return methodHandle();
-
- if (!(decode_flags & MethodHandles::_dmf_does_dispatch)) {
- // pre-dispatched or static method (null receiver is OK for static)
- return m;
-
- } else if (receiver_klass.is_null()) {
- // null receiver value; cannot dispatch
- return methodHandle();
-
- } else if (!(decode_flags & MethodHandles::_dmf_from_interface)) {
- // perform virtual dispatch
- int vtable_index = m->vtable_index();
- guarantee(vtable_index >= 0, "valid vtable index");
-
- // receiver_klass might be an arrayKlassOop but all vtables start at
- // the same place. The cast is to avoid virtual call and assertion.
- // See also LinkResolver::runtime_resolve_virtual_method.
- instanceKlass* inst = (instanceKlass*)Klass::cast(receiver_klass());
- DEBUG_ONLY(inst->verify_vtable_index(vtable_index));
- methodOop m_oop = inst->method_at_vtable(vtable_index);
- return methodHandle(THREAD, m_oop);
-
- } else {
- // perform interface dispatch
- int itable_index = klassItable::compute_itable_index(m());
- guarantee(itable_index >= 0, "valid itable index");
- instanceKlass* inst = instanceKlass::cast(receiver_klass());
- methodOop m_oop = inst->method_at_itable(m->method_holder(), itable_index, THREAD);
- return methodHandle(THREAD, m_oop);
- }
-}
-
-void MethodHandles::verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS) {
- // Verify type.
- Handle mtype(THREAD, java_lang_invoke_MethodHandle::type(mh()));
- verify_method_type(m, mtype, false, KlassHandle(), CHECK);
-
- // Verify vmslots.
- if (java_lang_invoke_MethodHandle::vmslots(mh()) != m->size_of_parameters()) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in DMH");
- }
-}
-
-void MethodHandles::init_DirectMethodHandle(Handle mh, methodHandle m, bool do_dispatch, TRAPS) {
- // Check arguments.
- if (mh.is_null() || m.is_null() ||
- (!do_dispatch && m->is_abstract())) {
- THROW(vmSymbols::java_lang_InternalError());
- }
-
- if (VerifyMethodHandles) {
- // The privileged code which invokes this routine should not make
- // a mistake about types, but it's better to verify.
- verify_DirectMethodHandle(mh, m, CHECK);
- }
-
- // Finally, after safety checks are done, link to the target method.
- // We will follow the same path as the latter part of
- // InterpreterRuntime::resolve_invoke(), which first finds the method
- // and then decides how to populate the constant pool cache entry
- // that links the interpreter calls to the method. We need the same
- // bits, and will use the same calling sequence code.
-
- int vmindex = methodOopDesc::garbage_vtable_index;
- Handle vmtarget;
-
- instanceKlass::cast(m->method_holder())->link_class(CHECK);
-
- MethodHandleEntry* me = NULL;
- if (do_dispatch && Klass::cast(m->method_holder())->is_interface()) {
- // We are simulating an invokeinterface instruction.
- // (We might also be simulating an invokevirtual on a miranda method,
- // but it is safe to treat it as an invokeinterface.)
- assert(!m->can_be_statically_bound(), "no final methods on interfaces");
- vmindex = klassItable::compute_itable_index(m());
- assert(vmindex >= 0, "(>=0) == do_dispatch");
- // Set up same bits as ConstantPoolCacheEntry::set_interface_call().
- vmtarget = m->method_holder(); // the interface
- me = MethodHandles::entry(MethodHandles::_invokeinterface_mh);
- } else if (!do_dispatch || m->can_be_statically_bound()) {
- // We are simulating an invokestatic or invokespecial instruction.
- // Set up the method pointer, just like ConstantPoolCacheEntry::set_method().
- vmtarget = m;
- // this does not help dispatch, but it will make it possible to parse this MH:
- vmindex = methodOopDesc::nonvirtual_vtable_index;
- assert(vmindex < 0, "(>=0) == do_dispatch");
- if (!m->is_static()) {
- me = MethodHandles::entry(MethodHandles::_invokespecial_mh);
- } else {
- me = MethodHandles::entry(MethodHandles::_invokestatic_mh);
- // Part of the semantics of a static call is an initialization barrier.
- // For a DMH, it is done now, when the handle is created.
- Klass* k = Klass::cast(m->method_holder());
- if (k->should_be_initialized()) {
- k->initialize(CHECK); // possible safepoint
- }
- }
- } else {
- // We are simulating an invokevirtual instruction.
- // Set up the vtable index, just like ConstantPoolCacheEntry::set_method().
- // The key logic is LinkResolver::runtime_resolve_virtual_method.
- vmindex = m->vtable_index();
- vmtarget = m->method_holder();
- me = MethodHandles::entry(MethodHandles::_invokevirtual_mh);
- }
-
- if (me == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
-
- java_lang_invoke_DirectMethodHandle::set_vmtarget(mh(), vmtarget());
- java_lang_invoke_DirectMethodHandle::set_vmindex( mh(), vmindex);
- DEBUG_ONLY(KlassHandle rlimit; int flags);
- assert(MethodHandles::decode_method(mh(), rlimit, flags) == m,
- "properly stored for later decoding");
- DEBUG_ONLY(bool actual_do_dispatch = ((flags & _dmf_does_dispatch) != 0));
- assert(!(actual_do_dispatch && !do_dispatch),
- "do not perform dispatch if !do_dispatch specified");
- assert(actual_do_dispatch == (vmindex >= 0), "proper later decoding of do_dispatch");
- assert(decode_MethodHandle_stack_pushes(mh()) == 0, "DMH does not move stack");
-
- // Done!
- java_lang_invoke_MethodHandle::set_vmentry(mh(), me);
-}
-
-void MethodHandles::verify_BoundMethodHandle_with_receiver(Handle mh,
- methodHandle m,
- TRAPS) {
- // Verify type.
- KlassHandle bound_recv_type;
- {
- oop receiver = java_lang_invoke_BoundMethodHandle::argument(mh());
- if (receiver != NULL)
- bound_recv_type = KlassHandle(THREAD, receiver->klass());
- }
- Handle mtype(THREAD, java_lang_invoke_MethodHandle::type(mh()));
- verify_method_type(m, mtype, true, bound_recv_type, CHECK);
-
- int receiver_pos = m->size_of_parameters() - 1;
-
- // Verify MH.vmargslot, which should point at the bound receiver.
- verify_vmargslot(mh, -1, java_lang_invoke_BoundMethodHandle::vmargslot(mh()), CHECK);
- //verify_vmslots(mh, CHECK);
-
- // Verify vmslots.
- if (java_lang_invoke_MethodHandle::vmslots(mh()) != receiver_pos) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH (receiver)");
- }
-}
-
-// Initialize a BMH with a receiver bound directly to a methodOop.
-void MethodHandles::init_BoundMethodHandle_with_receiver(Handle mh,
- methodHandle original_m,
- KlassHandle receiver_limit,
- int decode_flags,
- TRAPS) {
- // Check arguments.
- if (mh.is_null() || original_m.is_null()) {
- THROW(vmSymbols::java_lang_InternalError());
- }
-
- KlassHandle receiver_klass;
- {
- oop receiver_oop = java_lang_invoke_BoundMethodHandle::argument(mh());
- if (receiver_oop != NULL)
- receiver_klass = KlassHandle(THREAD, receiver_oop->klass());
- }
- methodHandle m = dispatch_decoded_method(original_m,
- receiver_limit, decode_flags,
- receiver_klass,
- CHECK);
- if (m.is_null()) { THROW(vmSymbols::java_lang_InternalError()); }
- if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); }
-
- int vmargslot = m->size_of_parameters() - 1;
- assert(java_lang_invoke_BoundMethodHandle::vmargslot(mh()) == vmargslot, "");
-
- if (VerifyMethodHandles) {
- verify_BoundMethodHandle_with_receiver(mh, m, CHECK);
- }
-
- java_lang_invoke_BoundMethodHandle::set_vmtarget(mh(), m());
-
- DEBUG_ONLY(KlassHandle junk1; int junk2);
- assert(MethodHandles::decode_method(mh(), junk1, junk2) == m, "properly stored for later decoding");
- assert(decode_MethodHandle_stack_pushes(mh()) == 1, "BMH pushes one stack slot");
-
- // Done!
- java_lang_invoke_MethodHandle::set_vmentry(mh(), MethodHandles::entry(MethodHandles::_bound_ref_direct_mh));
-}
-
-void MethodHandles::verify_BoundMethodHandle(Handle mh, Handle target, int argnum,
- bool direct_to_method, TRAPS) {
- ResourceMark rm;
- Handle ptype_handle(THREAD,
- java_lang_invoke_MethodType::ptype(java_lang_invoke_MethodHandle::type(target()), argnum));
- KlassHandle ptype_klass;
- BasicType ptype = java_lang_Class::as_BasicType(ptype_handle(), &ptype_klass);
- int slots_pushed = type2size[ptype];
-
- oop argument = java_lang_invoke_BoundMethodHandle::argument(mh());
-
- const char* err = NULL;
-
- switch (ptype) {
- case T_OBJECT:
- if (argument != NULL)
- // we must implicitly convert from the arg type to the outgoing ptype
- err = check_argument_type_change(T_OBJECT, argument->klass(), ptype, ptype_klass(), argnum);
- break;
-
- case T_ARRAY: case T_VOID:
- assert(false, "array, void do not appear here");
- default:
- if (ptype != T_INT && !is_subword_type(ptype)) {
- err = "unexpected parameter type";
- break;
- }
- // check subrange of Integer.value, if necessary
- if (argument == NULL || argument->klass() != SystemDictionary::Integer_klass()) {
- err = "bound integer argument must be of type java.lang.Integer";
- break;
- }
- if (ptype != T_INT) {
- int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT);
- jint value = argument->int_field(value_offset);
- int vminfo = adapter_unbox_subword_vminfo(ptype);
- jint subword = truncate_subword_from_vminfo(value, vminfo);
- if (value != subword) {
- err = "bound subword value does not fit into the subword type";
- break;
- }
- }
- break;
- case T_FLOAT:
- case T_DOUBLE:
- case T_LONG:
- {
- // we must implicitly convert from the unboxed arg type to the outgoing ptype
- BasicType argbox = java_lang_boxing_object::basic_type(argument);
- if (argbox != ptype) {
- err = check_argument_type_change(T_OBJECT, (argument == NULL
- ? SystemDictionary::Object_klass()
- : argument->klass()),
- ptype, ptype_klass(), argnum);
- assert(err != NULL, "this must be an error");
- }
- break;
- }
- }
-
- if (err == NULL) {
- DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
- if (direct_to_method) {
- assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots");
- } else {
- int target_pushes = decode_MethodHandle_stack_pushes(target());
- assert(this_pushes == slots_pushed + target_pushes, "BMH stack motion must be correct");
- }
- }
-
- if (err == NULL) {
- // Verify the rest of the method type.
- err = check_method_type_insertion(java_lang_invoke_MethodHandle::type(mh()),
- argnum, ptype_handle(),
- java_lang_invoke_MethodHandle::type(target()));
- }
-
- if (err != NULL) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), err);
- }
-}
-
-void MethodHandles::init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
- // Check arguments.
- if (mh.is_null() || target.is_null() || !java_lang_invoke_MethodHandle::is_instance(target())) {
- THROW(vmSymbols::java_lang_InternalError());
- }
-
- int argslot = java_lang_invoke_BoundMethodHandle::vmargslot(mh());
-
- if (VerifyMethodHandles) {
- int insert_after = argnum - 1;
- verify_vmargslot(mh, insert_after, argslot, CHECK);
- verify_vmslots(mh, CHECK);
- }
-
- // Get bound type and required slots.
- BasicType ptype;
- {
- oop ptype_oop = java_lang_invoke_MethodType::ptype(java_lang_invoke_MethodHandle::type(target()), argnum);
- ptype = java_lang_Class::as_BasicType(ptype_oop);
- }
- int slots_pushed = type2size[ptype];
-
- // If (a) the target is a direct non-dispatched method handle,
- // or (b) the target is a dispatched direct method handle and we
- // are binding the receiver, cut out the middle-man.
- // Do this by decoding the DMH and using its methodOop directly as vmtarget.
- bool direct_to_method = false;
- if (OptimizeMethodHandles &&
- target->klass() == SystemDictionary::DirectMethodHandle_klass() &&
- (argnum != 0 || java_lang_invoke_BoundMethodHandle::argument(mh()) != NULL) &&
- (argnum == 0 || java_lang_invoke_DirectMethodHandle::vmindex(target()) < 0)) {
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = decode_method(target(), receiver_limit, decode_flags);
- if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "DMH failed to decode"); }
- DEBUG_ONLY(int m_vmslots = m->size_of_parameters() - slots_pushed); // pos. of 1st arg.
- assert(java_lang_invoke_BoundMethodHandle::vmslots(mh()) == m_vmslots, "type w/ m sig");
- if (argnum == 0 && (decode_flags & _dmf_has_receiver) != 0) {
- init_BoundMethodHandle_with_receiver(mh, m,
- receiver_limit, decode_flags,
- CHECK);
- return;
- }
-
- // Even if it is not a bound receiver, we still might be able
- // to bind another argument and still invoke the methodOop directly.
- if (!(decode_flags & _dmf_does_dispatch)) {
- direct_to_method = true;
- java_lang_invoke_BoundMethodHandle::set_vmtarget(mh(), m());
- }
- }
- if (!direct_to_method)
- java_lang_invoke_BoundMethodHandle::set_vmtarget(mh(), target());
-
- if (VerifyMethodHandles) {
- verify_BoundMethodHandle(mh, target, argnum, direct_to_method, CHECK);
- }
-
- // Next question: Is this a ref, int, or long bound value?
- MethodHandleEntry* me = NULL;
- if (ptype == T_OBJECT) {
- if (direct_to_method) me = MethodHandles::entry(_bound_ref_direct_mh);
- else me = MethodHandles::entry(_bound_ref_mh);
- } else if (slots_pushed == 2) {
- if (direct_to_method) me = MethodHandles::entry(_bound_long_direct_mh);
- else me = MethodHandles::entry(_bound_long_mh);
- } else if (slots_pushed == 1) {
- if (direct_to_method) me = MethodHandles::entry(_bound_int_direct_mh);
- else me = MethodHandles::entry(_bound_int_mh);
- } else {
- assert(false, "");
- }
-
- // Done!
- java_lang_invoke_MethodHandle::set_vmentry(mh(), me);
-}
-
-static void throw_InternalError_for_bad_conversion(int conversion, const char* err, TRAPS) {
- char msg[200];
- jio_snprintf(msg, sizeof(msg), "bad adapter (conversion=0x%08x): %s", conversion, err);
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), msg);
-}
-
-void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
- ResourceMark rm;
- jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh());
- int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh());
-
- verify_vmargslot(mh, argnum, argslot, CHECK);
- verify_vmslots(mh, CHECK);
-
- jint conv_op = adapter_conversion_op(conversion);
- if (!conv_op_valid(conv_op)) {
- throw_InternalError_for_bad_conversion(conversion, "unknown conversion op", THREAD);
- return;
- }
- EntryKind ek = adapter_entry_kind(conv_op);
-
- int stack_move = adapter_conversion_stack_move(conversion);
- BasicType src = adapter_conversion_src_type(conversion);
- BasicType dest = adapter_conversion_dest_type(conversion);
- int vminfo = adapter_conversion_vminfo(conversion); // should be zero
-
- Handle argument(THREAD, java_lang_invoke_AdapterMethodHandle::argument(mh()));
- 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;
-
- if (err == NULL) {
- // Check that the correct argument is supplied, but only if it is required.
- switch (ek) {
- case _adapter_check_cast: // target type of cast
- case _adapter_ref_to_prim: // wrapper type from which to unbox
- case _adapter_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_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_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_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 (!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())
- { err = "adapter has spurious argument"; break; }
- break;
- }
- }
-
- 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) {
- err = "adapter requires object src/dest conversion subfields";
- }
- break;
- case _adapter_prim_to_prim:
- if (!is_java_primitive(src) || !is_java_primitive(dest) || src == dest) {
- err = "adapter requires primitive src/dest conversion subfields"; break;
- }
- if ( (src == T_FLOAT || src == T_DOUBLE) && !(dest == T_FLOAT || dest == T_DOUBLE) ||
- !(src == T_FLOAT || src == T_DOUBLE) && (dest == T_FLOAT || dest == T_DOUBLE)) {
- err = "adapter cannot convert beween floating and fixed-point"; break;
- }
- break;
- case _adapter_ref_to_prim:
- if (src != T_OBJECT || !is_java_primitive(dest)
- || argument() != Klass::cast(SystemDictionary::box_klass(dest))->java_mirror()) {
- err = "adapter requires primitive dest conversion subfield"; break;
- }
- break;
- case _adapter_prim_to_ref:
- if (!is_java_primitive(src) || dest != T_OBJECT) {
- err = "adapter requires primitive src conversion subfield"; break;
- }
- break;
- case _adapter_swap_args:
- {
- if (!src || !dest) {
- err = "adapter requires src/dest conversion subfields for swap"; break;
- }
- int src_size = type2size[src];
- if (src_size != type2size[dest]) {
- err = "adapter requires equal sizes for src/dest"; break;
- }
- int src_slot = argslot;
- int dest_slot = vminfo;
- int src_arg = argnum;
- int dest_arg = argument_slot_to_argnum(src_mtype(), dest_slot);
- verify_vmargslot(mh, dest_arg, dest_slot, CHECK);
- if (!(dest_slot >= src_slot + src_size) &&
- !(src_slot >= dest_slot + src_size)) {
- err = "source, destination slots must be distinct"; break;
- } else if (!(src_slot > dest_slot)) {
- err = "source of swap must be deeper in stack"; break;
- }
- 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);
- if (err == NULL)
- 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_rot_args:
- {
- if (!src || !dest) {
- err = "adapter requires src/dest conversion subfields for rotate"; break;
- }
- int src_slot = argslot;
- int limit_raw = vminfo;
- bool rot_down = (src_slot < limit_raw);
- int limit_bias = (rot_down ? MethodHandles::OP_ROT_ARGS_DOWN_LIMIT_BIAS : 0);
- int limit_slot = limit_raw - limit_bias;
- int src_arg = argnum;
- int limit_arg = argument_slot_to_argnum(src_mtype(), limit_slot);
- verify_vmargslot(mh, limit_arg, limit_slot, CHECK);
- if (src_slot == limit_slot) {
- err = "source, destination slots must be distinct"; break;
- }
- if (!rot_down) { // rotate slots up == shift arguments left
- // limit_slot is an inclusive lower limit
- assert((src_slot > limit_slot) && (src_arg < limit_arg), "");
- // rotate up: [limit_slot..src_slot-ss] --> [limit_slot+ss..src_slot]
- // that is: [src_arg+1..limit_arg] --> [src_arg..limit_arg-1]
- for (int i = src_arg+1; i <= limit_arg && err == NULL; i++) {
- 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 slots down == shfit arguments right
- // limit_slot is an exclusive upper limit
- assert((src_slot < limit_slot - limit_bias) && (src_arg > limit_arg + limit_bias), "");
- // rotate down: [src_slot+ss..limit_slot) --> [src_slot..limit_slot-ss)
- // that is: (limit_arg..src_arg-1] --> (dst_arg+1..src_arg]
- for (int i = limit_arg+1; 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(dst_mtype(), i+1),
- i);
- }
- }
- if (err == NULL) {
- int dest_arg = (rot_down ? limit_arg+1 : limit_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_fold_args:
- {
- 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;
- default:
- if (src != 0 || dest != 0) {
- err = "adapter has spurious src/dest conversion subfields"; break;
- }
- break;
- }
- }
-
- if (err == NULL) {
- // Check the stack_move subfield.
- // It must always report the net change in stack size, positive or negative.
- int slots_pushed = stack_move / stack_move_unit();
- switch (ek) {
- case _adapter_prim_to_prim:
- case _adapter_ref_to_prim:
- case _adapter_prim_to_ref:
- if (slots_pushed != type2size[dest] - type2size[src]) {
- err = "wrong stack motion for primitive conversion";
- }
- break;
- case _adapter_dup_args:
- if (slots_pushed <= 0) {
- err = "adapter requires conversion subfield slots_pushed > 0";
- }
- break;
- case _adapter_drop_args:
- if (slots_pushed >= 0) {
- err = "adapter requires conversion subfield slots_pushed < 0";
- }
- break;
- case _adapter_collect_args:
- case _adapter_fold_args:
- if (slots_pushed > 2) {
- err = "adapter requires conversion subfield slots_pushed <= 2";
- }
- break;
- case _adapter_spread_args:
- if (slots_pushed < -1) {
- err = "adapter requires conversion subfield slots_pushed >= -1";
- }
- break;
- default:
- if (stack_move != 0) {
- err = "adapter has spurious stack_move conversion subfield";
- }
- break;
- }
- if (err == NULL && stack_move != slots_pushed * stack_move_unit()) {
- err = "stack_move conversion subfield must be multiple of stack_move_unit";
- }
- }
-
- if (err == NULL) {
- // Make sure this adapter'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 {
- 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 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:
- case _adapter_prim_to_ref:
- case _adapter_collect_args:
- case _adapter_fold_args:
- break; // OK
- default:
- err = "vminfo subfield is reserved to the JVM";
- }
- }
-
- // Do additional ad hoc checks.
- if (err == NULL) {
- switch (ek) {
- case _adapter_retype_only:
- err = check_method_type_passthrough(src_mtype(), dst_mtype(), false);
- break;
-
- case _adapter_retype_raw:
- err = check_method_type_passthrough(src_mtype(), dst_mtype(), true);
- break;
-
- case _adapter_check_cast:
- {
- // The actual value being checked must be a reference:
- err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), argnum),
- object_java_mirror(), argnum);
- if (err != NULL) break;
-
- // The output of the cast must fit with the destination argument:
- Handle cast_class = argument;
- err = check_method_type_conversion(src_mtype(),
- argnum, cast_class(),
- dst_mtype());
- }
- break;
-
- // %%% TO DO: continue in remaining cases to verify src/dst_mtype if VerifyMethodHandles
- }
- }
-
- if (err != NULL) {
- throw_InternalError_for_bad_conversion(conversion, err, THREAD);
- return;
- }
-
-}
-
-void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
- 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())) {
- throw_InternalError_for_bad_conversion(conversion, "bad target", THREAD);
- return;
- }
- java_lang_invoke_AdapterMethodHandle::set_vmtarget(mh(), target());
-
- 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:
- case _adapter_retype_raw:
- case _adapter_check_cast:
- case _adapter_dup_args:
- case _adapter_drop_args:
- // these work fine via general case code
- break;
-
- case _adapter_prim_to_prim:
- {
- // Non-subword cases are {int,float,long,double} -> {int,float,long,double}.
- // And, the {float,double} -> {int,long} cases must be handled by Java.
- switch (type2size[src] *4+ type2size[dest]) {
- case 1 *4+ 1:
- assert(src == T_INT || is_subword_type(src), "source is not float");
- // Subword-related cases are int -> {boolean,byte,char,short}.
- ek_opt = _adapter_opt_i2i;
- vminfo = adapter_prim_to_prim_subword_vminfo(dest);
- break;
- case 2 *4+ 1:
- if (src == T_LONG && (dest == T_INT || is_subword_type(dest))) {
- ek_opt = _adapter_opt_l2i;
- vminfo = adapter_prim_to_prim_subword_vminfo(dest);
- } else if (src == T_DOUBLE && dest == T_FLOAT) {
- ek_opt = _adapter_opt_d2f;
- } else {
- goto throw_not_impl; // runs user code, hence could block
- }
- break;
- case 1 *4+ 2:
- 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 {
- goto throw_not_impl; // runs user code, hence could block
- }
- break;
- default:
- goto throw_not_impl; // runs user code, hence could block
- break;
- }
- }
- break;
-
- case _adapter_ref_to_prim:
- {
- switch (type2size[dest]) {
- case 1:
- ek_opt = _adapter_opt_unboxi;
- vminfo = adapter_unbox_subword_vminfo(dest);
- break;
- case 2:
- ek_opt = _adapter_opt_unboxl;
- break;
- default:
- goto throw_not_impl;
- break;
- }
- }
- break;
-
- case _adapter_prim_to_ref:
- {
- // 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:
- {
- int swap_slots = type2size[src];
- int src_slot = argslot;
- int dest_slot = vminfo;
- int rotate = (ek_orig == _adapter_swap_args) ? 0 : (src_slot > dest_slot) ? 1 : -1;
- switch (swap_slots) {
- case 1:
- ek_opt = (!rotate ? _adapter_opt_swap_1 :
- rotate > 0 ? _adapter_opt_rot_1_up : _adapter_opt_rot_1_down);
- break;
- case 2:
- ek_opt = (!rotate ? _adapter_opt_swap_2 :
- rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down);
- break;
- default:
- goto throw_not_impl;
- break;
- }
- }
- break;
-
- case _adapter_spread_args:
- {
- // vminfo will be the required length of the array
- int array_size = (slots_pushed + 1) / (type2size[dest] == 2 ? 2 : 1);
- vminfo = array_size;
- // 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;
- }
- 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_collect_args:
- {
- 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:
- {
- 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
- assert(false, "incomplete switch");
- // and fall through:
-
- throw_not_impl:
- 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_msg("%s: conv_op %d ek_opt %d", err, conv_op, ek_opt), THREAD);
- return;
- }
-
- // Rebuild the conversion value; maybe parts of it were changed.
- jint new_conversion = adapter_conversion(conv_op, src, dest, stack_move, vminfo);
-
- // Finalize the conversion field. (Note that it is final to Java code.)
- java_lang_invoke_AdapterMethodHandle::set_conversion(mh(), new_conversion);
-
- if (java_lang_invoke_CountingMethodHandle::is_instance(mh())) {
- assert(ek_orig == _adapter_retype_only, "only one handled");
- ek_opt = _adapter_opt_profiling;
- }
-
- // Done!
- java_lang_invoke_MethodHandle::set_vmentry(mh(), entry(ek_opt));
-
- // There should be enough memory barriers on exit from native methods
- // to ensure that the MH is fully initialized to all threads before
- // Java code can publish it in global data structures.
-}
-
-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);
- }
- }
- assert(java_lang_invoke_MethodTypeForm::vmslots(mtform()) == argument_slot_count(mtype()), "must agree");
-}
-
-#ifdef ASSERT
-
-extern "C"
-void print_method_handle(oop mh);
-
-static void stress_method_handle_walk_impl(Handle mh, TRAPS) {
- if (StressMethodHandleWalk) {
- // Exercise the MethodHandleWalk code in various ways and validate
- // the resulting method oop. Some of these produce output so they
- // are guarded under Verbose.
- ResourceMark rm;
- HandleMark hm;
- if (Verbose) {
- print_method_handle(mh());
- }
- TempNewSymbol name = SymbolTable::new_symbol("invoke", CHECK);
- Handle mt = java_lang_invoke_MethodHandle::type(mh());
- TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(mt(), true, CHECK);
- MethodHandleCompiler mhc(mh, name, signature, 10000, false, CHECK);
- methodHandle m = mhc.compile(CHECK);
- if (Verbose) {
- m->print_codes();
- }
- InterpreterOopMap mask;
- OopMapCache::compute_one_oop_map(m, m->code_size() - 1, &mask);
- // compile to object code if -Xcomp or WizardMode
- if ((WizardMode ||
- CompilationPolicy::must_be_compiled(m))
- && !instanceKlass::cast(m->method_holder())->is_not_initialized()
- && CompilationPolicy::can_be_compiled(m)) {
- // Force compilation
- CompileBroker::compile_method(m, InvocationEntryBci,
- CompilationPolicy::policy()->initial_compile_level(),
- methodHandle(), 0, "StressMethodHandleWalk",
- CHECK);
- }
- }
-}
-
-static void stress_method_handle_walk(Handle mh, TRAPS) {
- stress_method_handle_walk_impl(mh, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- oop ex = PENDING_EXCEPTION;
- CLEAR_PENDING_EXCEPTION;
- tty->print("StressMethodHandleWalk: ");
- java_lang_Throwable::print(ex, tty);
- tty->cr();
- }
-}
-#else
-
-static void stress_method_handle_walk(Handle mh, TRAPS) {}
-
-#endif
-
//
-// Here are the native methods on sun.invoke.MethodHandleImpl.
+// Here are the native methods in java.lang.invoke.MethodHandleNatives
// They are the private interface between this JVM and the HotSpot-specific
// Java code that implements JSR 292 method handles.
//
// Note: We use a JVM_ENTRY macro to define each of these, for this is the way
// that intrinsic (non-JNI) native methods are defined in HotSpot.
//
-
-// direct method handles for invokestatic or invokespecial
-// void init(DirectMethodHandle self, MemberName ref, boolean doDispatch, Class<?> caller);
-JVM_ENTRY(void, MHN_init_DMH(JNIEnv *env, jobject igcls, jobject mh_jh,
- jobject target_jh, jboolean do_dispatch, jobject caller_jh)) {
- ResourceMark rm; // for error messages
-
- // This is the guy we are initializing:
- if (mh_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "self is null"); }
- Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
-
- // Early returns out of this method leave the DMH in an unfinished state.
- assert(java_lang_invoke_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
-
- // which method are we really talking about?
- if (target_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "target is null"); }
- Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
- if (java_lang_invoke_MemberName::is_instance(target()) &&
- java_lang_invoke_MemberName::vmindex(target()) == VM_INDEX_UNINITIALIZED) {
- MethodHandles::resolve_MemberName(target, CHECK);
- }
-
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = MethodHandles::decode_method(target(), receiver_limit, decode_flags);
- if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "no such method"); }
-
- // The trusted Java code that calls this method should already have performed
- // access checks on behalf of the given caller. But, we can verify this.
- if (VerifyMethodHandles && caller_jh != NULL) {
- KlassHandle caller(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(caller_jh)));
- // If this were a bytecode, the first access check would be against
- // the "reference class" mentioned in the CONSTANT_Methodref.
- // We don't know at this point which class that was, and if we
- // check against m.method_holder we might get the wrong answer.
- // So we just make sure to handle this check when the resolution
- // happens, when we call resolve_MemberName.
- //
- // (A public class can inherit public members from private supers,
- // and it would be wrong to check access against the private super
- // if the original symbolic reference was against the public class.)
- //
- // If there were a bytecode, the next step would be to lookup the method
- // in the reference class, then then check the method's access bits.
- // Emulate LinkResolver::check_method_accessability.
- klassOop resolved_klass = m->method_holder();
- if (!Reflection::verify_field_access(caller->as_klassOop(),
- resolved_klass, resolved_klass,
- m->access_flags(),
- true)) {
- // %%% following cutout belongs in Reflection::verify_field_access?
- bool same_pm = Reflection::is_same_package_member(caller->as_klassOop(),
- resolved_klass, THREAD);
- if (!same_pm) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), m->name_and_sig_as_C_string());
- }
- }
- }
-
- MethodHandles::init_DirectMethodHandle(mh, m, (do_dispatch != JNI_FALSE), CHECK);
- stress_method_handle_walk(mh, CHECK);
-}
-JVM_END
-
-// bound method handles
-JVM_ENTRY(void, MHN_init_BMH(JNIEnv *env, jobject igcls, jobject mh_jh,
- jobject target_jh, int argnum)) {
- ResourceMark rm; // for error messages
-
- // This is the guy we are initializing:
- if (mh_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "self is null"); }
- Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
-
- // Early returns out of this method leave the BMH in an unfinished state.
- assert(java_lang_invoke_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
-
- if (target_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "target is null"); }
- Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
-
- if (!java_lang_invoke_MethodHandle::is_instance(target())) {
- // Target object is a reflective method. (%%% Do we need this alternate path?)
- Untested("init_BMH of non-MH");
- if (argnum != 0) { THROW(vmSymbols::java_lang_InternalError()); }
- KlassHandle receiver_limit; int decode_flags = 0;
- methodHandle m = MethodHandles::decode_method(target(), receiver_limit, decode_flags);
- MethodHandles::init_BoundMethodHandle_with_receiver(mh, m,
- receiver_limit,
- decode_flags,
- CHECK);
- } else {
- // Build a BMH on top of a DMH or another BMH:
- MethodHandles::init_BoundMethodHandle(mh, target, argnum, CHECK);
- }
-
- if (StressMethodHandleWalk) {
- if (mh->klass() == SystemDictionary::BoundMethodHandle_klass())
- stress_method_handle_walk(mh, CHECK);
- // else don't, since the subclass has not yet initialized its own fields
- }
-}
-JVM_END
-
-// adapter method handles
-JVM_ENTRY(void, MHN_init_AMH(JNIEnv *env, jobject igcls, jobject mh_jh,
- jobject target_jh, int argnum)) {
- // This is the guy we are initializing:
- if (mh_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "self is null"); }
- if (target_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "target is null"); }
- Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
- Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
-
- // Early returns out of this method leave the AMH in an unfinished state.
- assert(java_lang_invoke_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
-
- MethodHandles::init_AdapterMethodHandle(mh, target, argnum, CHECK);
- stress_method_handle_walk(mh, CHECK);
-}
-JVM_END
-
-// method type forms
-JVM_ENTRY(void, MHN_init_MT(JNIEnv *env, jobject igcls, jobject erased_jh)) {
- if (erased_jh == NULL) return;
- if (TraceMethodHandles) {
- tty->print("creating MethodType form ");
- if (WizardMode || Verbose) { // Warning: this calls Java code on the MH!
- // call Object.toString()
- Symbol* name = vmSymbols::toString_name();
- Symbol* sig = vmSymbols::void_string_signature();
- JavaCallArguments args(Handle(THREAD, JNIHandles::resolve_non_null(erased_jh)));
- JavaValue result(T_OBJECT);
- JavaCalls::call_virtual(&result, SystemDictionary::Object_klass(), name, sig,
- &args, CHECK);
- Handle str(THREAD, (oop)result.get_jobject());
- java_lang_String::print(str, tty);
- }
- tty->cr();
- }
-}
-JVM_END
-
-// debugging and reflection
-JVM_ENTRY(jobject, MHN_getTarget(JNIEnv *env, jobject igcls, jobject mh_jh, jint format)) {
- Handle mh(THREAD, JNIHandles::resolve(mh_jh));
- if (!java_lang_invoke_MethodHandle::is_instance(mh())) {
- THROW_NULL(vmSymbols::java_lang_IllegalArgumentException());
- }
- oop target = MethodHandles::encode_target(mh, format, CHECK_NULL);
- return JNIHandles::make_local(THREAD, target);
-}
-JVM_END
-
JVM_ENTRY(jint, MHN_getConstant(JNIEnv *env, jobject igcls, jint which)) {
switch (which) {
- case MethodHandles::GC_JVM_PUSH_LIMIT:
- guarantee(MethodHandlePushLimit >= 2 && MethodHandlePushLimit <= 0xFF,
- "MethodHandlePushLimit parameter must be in valid range");
- return MethodHandlePushLimit;
- case MethodHandles::GC_JVM_STACK_MOVE_UNIT:
- // return number of words per slot, signed according to stack direction
- return MethodHandles::stack_move_unit();
- case MethodHandles::GC_CONV_OP_IMPLEMENTED_MASK:
- return MethodHandles::adapter_conversion_ops_supported_mask();
case MethodHandles::GC_COUNT_GWT:
#ifdef COMPILER2
return true;
@@ -2872,64 +942,54 @@
JVM_END
#ifndef PRODUCT
-#define EACH_NAMED_CON(template) \
- /* 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) */ \
- /* hold back this one until JDK stabilizes */ \
- /* template(MethodHandles,GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS) */ \
- template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \
- template(MethodHandles,ETF_DIRECT_HANDLE) \
- template(MethodHandles,ETF_METHOD_NAME) \
- template(MethodHandles,ETF_REFLECT_METHOD) \
+#define EACH_NAMED_CON(template, requirement) \
+ template(MethodHandles,GC_COUNT_GWT) \
template(java_lang_invoke_MemberName,MN_IS_METHOD) \
template(java_lang_invoke_MemberName,MN_IS_CONSTRUCTOR) \
template(java_lang_invoke_MemberName,MN_IS_FIELD) \
template(java_lang_invoke_MemberName,MN_IS_TYPE) \
template(java_lang_invoke_MemberName,MN_SEARCH_SUPERCLASSES) \
template(java_lang_invoke_MemberName,MN_SEARCH_INTERFACES) \
- template(java_lang_invoke_MemberName,VM_INDEX_UNINITIALIZED) \
- template(java_lang_invoke_AdapterMethodHandle,OP_RETYPE_ONLY) \
- template(java_lang_invoke_AdapterMethodHandle,OP_RETYPE_RAW) \
- template(java_lang_invoke_AdapterMethodHandle,OP_CHECK_CAST) \
- template(java_lang_invoke_AdapterMethodHandle,OP_PRIM_TO_PRIM) \
- template(java_lang_invoke_AdapterMethodHandle,OP_REF_TO_PRIM) \
- template(java_lang_invoke_AdapterMethodHandle,OP_PRIM_TO_REF) \
- template(java_lang_invoke_AdapterMethodHandle,OP_SWAP_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_ROT_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_DUP_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_DROP_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_COLLECT_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_SPREAD_ARGS) \
- /* 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) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_OP_SHIFT) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_DEST_TYPE_SHIFT) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_SRC_TYPE_SHIFT) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_STACK_MOVE_SHIFT) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_STACK_MOVE_MASK) \
+ template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \
+ template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \
+ template(MethodHandles,GC_LAMBDA_SUPPORT) \
/*end*/
+#define IGNORE_REQ(req_expr) /* req_expr */
#define ONE_PLUS(scope,value) 1+
-static const int con_value_count = EACH_NAMED_CON(ONE_PLUS) 0;
+static const int con_value_count = EACH_NAMED_CON(ONE_PLUS, IGNORE_REQ) 0;
#define VALUE_COMMA(scope,value) scope::value,
-static const int con_values[con_value_count+1] = { EACH_NAMED_CON(VALUE_COMMA) 0 };
+static const int con_values[con_value_count+1] = { EACH_NAMED_CON(VALUE_COMMA, IGNORE_REQ) 0 };
#define STRING_NULL(scope,value) #value "\0"
-static const char con_names[] = { EACH_NAMED_CON(STRING_NULL) };
+static const char con_names[] = { EACH_NAMED_CON(STRING_NULL, IGNORE_REQ) };
+
+static bool advertise_con_value(int which) {
+ if (which < 0) return false;
+ bool ok = true;
+ int count = 0;
+#define INC_COUNT(scope,value) \
+ ++count;
+#define CHECK_REQ(req_expr) \
+ if (which < count) return ok; \
+ ok = (req_expr);
+ EACH_NAMED_CON(INC_COUNT, CHECK_REQ);
+#undef INC_COUNT
+#undef CHECK_REQ
+ assert(count == con_value_count, "");
+ if (which < count) return ok;
+ return false;
+}
#undef ONE_PLUS
#undef VALUE_COMMA
#undef STRING_NULL
#undef EACH_NAMED_CON
-#endif
+#endif // PRODUCT
JVM_ENTRY(jint, MHN_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) {
#ifndef PRODUCT
- if (which >= 0 && which < con_value_count) {
+ if (advertise_con_value(which)) {
+ assert(which >= 0 && which < con_value_count, "");
int con = con_values[which];
objArrayHandle box(THREAD, (objArrayOop) JNIHandles::resolve(box_jh));
if (box.not_null() && box->klass() == Universe::objectArrayKlassObj() && box->length() > 0) {
@@ -2965,13 +1025,14 @@
JVM_END
// void resolve(MemberName self, Class<?> caller)
-JVM_ENTRY(void, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh)) {
- if (mname_jh == NULL) { THROW_MSG(vmSymbols::java_lang_InternalError(), "mname is null"); }
+JVM_ENTRY(jobject, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh)) {
+ if (mname_jh == NULL) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "mname is null"); }
Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
// The trusted Java code that calls this method should already have performed
// access checks on behalf of the given caller. But, we can verify this.
- if (VerifyMethodHandles && caller_jh != NULL) {
+ if (VerifyMethodHandles && caller_jh != NULL &&
+ java_lang_invoke_MemberName::clazz(mname()) != NULL) {
klassOop reference_klass = java_lang_Class::as_klassOop(java_lang_invoke_MemberName::clazz(mname()));
if (reference_klass != NULL) {
// Emulate LinkResolver::check_klass_accessability.
@@ -2979,15 +1040,97 @@
if (!Reflection::verify_class_access(caller,
reference_klass,
true)) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), Klass::cast(reference_klass)->external_name());
+ THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), Klass::cast(reference_klass)->external_name());
}
}
}
- MethodHandles::resolve_MemberName(mname, CHECK);
+ Handle resolved = MethodHandles::resolve_MemberName(mname, CHECK_NULL);
+ if (resolved.is_null()) {
+ int flags = java_lang_invoke_MemberName::flags(mname());
+ int ref_kind = (flags >> REFERENCE_KIND_SHIFT) & REFERENCE_KIND_MASK;
+ if (!MethodHandles::ref_kind_is_valid(ref_kind)) {
+ THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "obsolete MemberName format");
+ }
+ if ((flags & ALL_KINDS) == IS_FIELD) {
+ THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), "field resolution failed");
+ } else if ((flags & ALL_KINDS) == IS_METHOD ||
+ (flags & ALL_KINDS) == IS_CONSTRUCTOR) {
+ THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), "method resolution failed");
+ } else {
+ THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "resolution failed");
+ }
+ }
+
+ return JNIHandles::make_local(THREAD, resolved());
}
JVM_END
+static jlong find_member_field_offset(oop mname, bool must_be_static, TRAPS) {
+ if (mname == NULL ||
+ java_lang_invoke_MemberName::vmtarget(mname) == NULL) {
+ THROW_MSG_0(vmSymbols::java_lang_InternalError(), "mname not resolved");
+ } else {
+ int flags = java_lang_invoke_MemberName::flags(mname);
+ if ((flags & IS_FIELD) != 0 &&
+ (must_be_static
+ ? (flags & JVM_ACC_STATIC) != 0
+ : (flags & JVM_ACC_STATIC) == 0)) {
+ int vmindex = java_lang_invoke_MemberName::vmindex(mname);
+ return (jlong) vmindex;
+ }
+ }
+ const char* msg = (must_be_static ? "static field required" : "non-static field required");
+ THROW_MSG_0(vmSymbols::java_lang_InternalError(), msg);
+ return 0;
+}
+
+JVM_ENTRY(jlong, MHN_objectFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+ return find_member_field_offset(JNIHandles::resolve(mname_jh), false, THREAD);
+}
+JVM_END
+
+JVM_ENTRY(jlong, MHN_staticFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+ return find_member_field_offset(JNIHandles::resolve(mname_jh), true, THREAD);
+}
+JVM_END
+
+JVM_ENTRY(jobject, MHN_staticFieldBase(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+ // use the other function to perform sanity checks:
+ jlong ignore = find_member_field_offset(JNIHandles::resolve(mname_jh), true, CHECK_NULL);
+ oop clazz = java_lang_invoke_MemberName::clazz(JNIHandles::resolve_non_null(mname_jh));
+ return JNIHandles::make_local(THREAD, clazz);
+}
+JVM_END
+
+JVM_ENTRY(jobject, MHN_getMemberVMInfo(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+ if (mname_jh == NULL) return NULL;
+ Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
+ intptr_t vmindex = java_lang_invoke_MemberName::vmindex(mname());
+ Handle vmtarget = java_lang_invoke_MemberName::vmtarget(mname());
+ objArrayHandle result = oopFactory::new_objArray(SystemDictionary::Object_klass(), 2, CHECK_NULL);
+ jvalue vmindex_value; vmindex_value.j = (long)vmindex;
+ oop x = java_lang_boxing_object::create(T_LONG, &vmindex_value, CHECK_NULL);
+ result->obj_at_put(0, x);
+ x = NULL;
+ if (vmtarget.is_null() || vmtarget->is_instance()) {
+ x = vmtarget();
+ } else if (vmtarget->is_klass()) {
+ x = Klass::cast((klassOop) vmtarget())->java_mirror();
+ } else {
+ Handle mname2 = MethodHandles::new_MemberName(CHECK_NULL);
+ if (vmtarget->is_method())
+ x = MethodHandles::init_method_MemberName(mname2(), methodOop(vmtarget()), false, NULL);
+ else
+ x = MethodHandles::init_MemberName(mname2(), vmtarget());
+ }
+ result->obj_at_put(1, x);
+ return JNIHandles::make_local(env, result());
+}
+JVM_END
+
+
+
// static native int getMembers(Class<?> defc, String matchName, String matchSig,
// int matchFlags, Class<?> caller, int skip, MemberName[] results);
JVM_ENTRY(jint, MHN_getMembers(JNIEnv *env, jobject igcls,
@@ -3053,60 +1196,6 @@
}
JVM_END
-methodOop MethodHandles::resolve_raise_exception_method(TRAPS) {
- if (_raise_exception_method != NULL) {
- // no need to do it twice
- return raise_exception_method();
- }
- // LinkResolver::resolve_invokedynamic can reach this point
- // because an invokedynamic has failed very early (7049415)
- KlassHandle MHN_klass = SystemDictionaryHandles::MethodHandleNatives_klass();
- if (MHN_klass.not_null()) {
- TempNewSymbol raiseException_name = SymbolTable::new_symbol("raiseException", CHECK_NULL);
- TempNewSymbol raiseException_sig = SymbolTable::new_symbol("(ILjava/lang/Object;Ljava/lang/Object;)V", CHECK_NULL);
- methodOop raiseException_method = instanceKlass::cast(MHN_klass->as_klassOop())
- ->find_method(raiseException_name, raiseException_sig);
- if (raiseException_method != NULL && raiseException_method->is_static()) {
- return raiseException_method;
- }
- }
- // not found; let the caller deal with it
- return NULL;
-}
-void MethodHandles::raise_exception(int code, oop actual, oop required, TRAPS) {
- methodOop raiseException_method = resolve_raise_exception_method(CHECK);
- if (raiseException_method != NULL &&
- instanceKlass::cast(raiseException_method->method_holder())->is_not_initialized()) {
- instanceKlass::cast(raiseException_method->method_holder())->initialize(CHECK);
- // it had better be resolved by now, or maybe JSR 292 failed to load
- raiseException_method = raise_exception_method();
- }
- if (raiseException_method == NULL) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "no raiseException method");
- }
- JavaCallArguments args;
- args.push_int(code);
- args.push_oop(actual);
- args.push_oop(required);
- JavaValue result(T_VOID);
- JavaCalls::call(&result, raiseException_method, &args, CHECK);
-}
-
-JVM_ENTRY(jobject, MH_invoke_UOE(JNIEnv *env, jobject igmh, jobjectArray igargs)) {
- TempNewSymbol UOE_name = SymbolTable::new_symbol("java/lang/UnsupportedOperationException", CHECK_NULL);
- THROW_MSG_NULL(UOE_name, "MethodHandle.invoke cannot be invoked reflectively");
- return NULL;
-}
-JVM_END
-
-JVM_ENTRY(jobject, MH_invokeExact_UOE(JNIEnv *env, jobject igmh, jobjectArray igargs)) {
- TempNewSymbol UOE_name = SymbolTable::new_symbol("java/lang/UnsupportedOperationException", CHECK_NULL);
- THROW_MSG_NULL(UOE_name, "MethodHandle.invokeExact cannot be invoked reflectively");
- return NULL;
-}
-JVM_END
-
-
/// JVM_RegisterMethodHandleMethods
#undef CS // Solaris builds complain
@@ -3121,48 +1210,32 @@
#define MT JLINV"MethodType;"
#define MH JLINV"MethodHandle;"
#define MEM JLINV"MemberName;"
-#define AMH JLINV"AdapterMethodHandle;"
-#define BMH JLINV"BoundMethodHandle;"
-#define DMH JLINV"DirectMethodHandle;"
#define CC (char*) /*cast a literal from (const char*)*/
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
// These are the native methods on java.lang.invoke.MethodHandleNatives.
-static JNINativeMethod methods[] = {
- // void init(MemberName self, AccessibleObject ref)
- {CC"init", CC"("AMH""MH"I)V", FN_PTR(MHN_init_AMH)},
- {CC"init", CC"("BMH""OBJ"I)V", FN_PTR(MHN_init_BMH)},
- {CC"init", CC"("DMH""OBJ"Z"CLS")V", FN_PTR(MHN_init_DMH)},
- {CC"init", CC"("MT")V", FN_PTR(MHN_init_MT)},
+static JNINativeMethod required_methods_JDK8[] = {
{CC"init", CC"("MEM""OBJ")V", FN_PTR(MHN_init_Mem)},
{CC"expand", CC"("MEM")V", FN_PTR(MHN_expand_Mem)},
- {CC"resolve", CC"("MEM""CLS")V", FN_PTR(MHN_resolve_Mem)},
- {CC"getTarget", CC"("MH"I)"OBJ, FN_PTR(MHN_getTarget)},
+ {CC"resolve", CC"("MEM""CLS")"MEM, FN_PTR(MHN_resolve_Mem)},
{CC"getConstant", CC"(I)I", FN_PTR(MHN_getConstant)},
// static native int getNamedCon(int which, Object[] name)
{CC"getNamedCon", CC"(I["OBJ")I", FN_PTR(MHN_getNamedCon)},
// static native int getMembers(Class<?> defc, String matchName, String matchSig,
// int matchFlags, Class<?> caller, int skip, MemberName[] results);
- {CC"getMembers", CC"("CLS""STRG""STRG"I"CLS"I["MEM")I", FN_PTR(MHN_getMembers)}
-};
-
-static JNINativeMethod call_site_methods[] = {
+ {CC"getMembers", CC"("CLS""STRG""STRG"I"CLS"I["MEM")I", FN_PTR(MHN_getMembers)},
+ {CC"objectFieldOffset", CC"("MEM")J", FN_PTR(MHN_objectFieldOffset)},
{CC"setCallSiteTargetNormal", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetNormal)},
- {CC"setCallSiteTargetVolatile", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetVolatile)}
-};
-
-static JNINativeMethod invoke_methods[] = {
- // void init(MemberName self, AccessibleObject ref)
- {CC"invoke", CC"(["OBJ")"OBJ, FN_PTR(MH_invoke_UOE)},
- {CC"invokeExact", CC"(["OBJ")"OBJ, FN_PTR(MH_invokeExact_UOE)}
+ {CC"setCallSiteTargetVolatile", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetVolatile)},
+ {CC"staticFieldOffset", CC"("MEM")J", FN_PTR(MHN_staticFieldOffset)},
+ {CC"staticFieldBase", CC"("MEM")"OBJ, FN_PTR(MHN_staticFieldBase)},
+ {CC"getMemberVMInfo", CC"("MEM")"OBJ, FN_PTR(MHN_getMemberVMInfo)}
};
// This one function is exported, used by NativeLookup.
JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
- assert(MethodHandles::spot_check_entry_names(), "entry enum is OK");
-
if (!EnableInvokeDynamic) {
warning("JSR 292 is disabled in this JVM. Use -XX:+UnlockDiagnosticVMOptions -XX:+EnableInvokeDynamic to enable.");
return; // bind nothing
@@ -3171,34 +1244,29 @@
assert(!MethodHandles::enabled(), "must not be enabled");
bool enable_MH = true;
- {
+ jclass MH_class = NULL;
+ if (SystemDictionary::MethodHandle_klass() == NULL) {
+ enable_MH = false;
+ } else {
+ oop mirror = Klass::cast(SystemDictionary::MethodHandle_klass())->java_mirror();
+ MH_class = (jclass) JNIHandles::make_local(env, mirror);
+ }
+
+ int status;
+
+ if (enable_MH) {
ThreadToNativeFromVM ttnfv(thread);
- int status = env->RegisterNatives(MHN_class, methods, sizeof(methods)/sizeof(JNINativeMethod));
- if (!env->ExceptionOccurred()) {
- const char* L_MH_name = (JLINV "MethodHandle");
- const char* MH_name = L_MH_name+1;
- jclass MH_class = env->FindClass(MH_name);
- status = env->RegisterNatives(MH_class, invoke_methods, sizeof(invoke_methods)/sizeof(JNINativeMethod));
- }
- if (!env->ExceptionOccurred()) {
- status = env->RegisterNatives(MHN_class, call_site_methods, sizeof(call_site_methods)/sizeof(JNINativeMethod));
- }
- if (env->ExceptionOccurred()) {
+
+ status = env->RegisterNatives(MHN_class, required_methods_JDK8, sizeof(required_methods_JDK8)/sizeof(JNINativeMethod));
+ if (status != JNI_OK || env->ExceptionOccurred()) {
warning("JSR 292 method handle code is mismatched to this JVM. Disabling support.");
enable_MH = false;
env->ExceptionClear();
}
-
}
- if (enable_MH) {
- methodOop raiseException_method = MethodHandles::resolve_raise_exception_method(CHECK);
- if (raiseException_method != NULL) {
- MethodHandles::set_raise_exception_method(raiseException_method);
- } else {
- warning("JSR 292 method handle code is mismatched to this JVM. Disabling support.");
- enable_MH = false;
- }
+ if (TraceInvokeDynamic) {
+ tty->print_cr("MethodHandle support loaded (using LambdaForms)");
}
if (enable_MH) {
--- a/hotspot/src/share/vm/prims/methodHandles.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -33,523 +33,36 @@
class MacroAssembler;
class Label;
-class MethodHandleEntry;
class MethodHandles: AllStatic {
// JVM support for MethodHandle, MethodType, and related types
// in java.lang.invoke and sun.invoke.
// See also javaClasses for layouts java_lang_invoke_Method{Handle,Type,Type::Form}.
public:
- enum EntryKind {
- _raise_exception, // stub for error generation from other stubs
- _invokestatic_mh, // how a MH emulates invokestatic
- _invokespecial_mh, // ditto for the other invokes...
- _invokevirtual_mh,
- _invokeinterface_mh,
- _bound_ref_mh, // reference argument is bound
- _bound_int_mh, // int argument is bound (via an Integer or Float)
- _bound_long_mh, // long argument is bound (via a Long or Double)
- _bound_ref_direct_mh, // same as above, with direct linkage to methodOop
- _bound_int_direct_mh,
- _bound_long_direct_mh,
-
- _adapter_mh_first, // adapter sequence goes here...
- _adapter_retype_only = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY,
- _adapter_retype_raw = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW,
- _adapter_check_cast = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST,
- _adapter_prim_to_prim = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM,
- _adapter_ref_to_prim = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM,
- _adapter_prim_to_ref = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF,
- _adapter_swap_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS,
- _adapter_rot_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS,
- _adapter_dup_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS,
- _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_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
-
- // argument list reordering
- _adapter_opt_swap_1,
- _adapter_opt_swap_2,
- _adapter_opt_rot_1_up,
- _adapter_opt_rot_1_down,
- _adapter_opt_rot_2_up,
- _adapter_opt_rot_2_down,
- // primitive single to single:
- _adapter_opt_i2i, // i2c, i2z, i2b, i2s
- // primitive double to single:
- _adapter_opt_l2i,
- _adapter_opt_d2f,
- // primitive single to double:
- _adapter_opt_i2l,
- _adapter_opt_f2d,
- // conversion between floating point and integer type is handled by Java
-
- // reference to primitive:
- _adapter_opt_unboxi,
- _adapter_opt_unboxl,
-
- // %% 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,
-
- _adapter_opt_profiling,
-
- _EK_LIMIT,
- _EK_FIRST = 0
- };
-
public:
static bool enabled() { return _enabled; }
static void set_enabled(bool z);
private:
- 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,
- CONV_DEST_TYPE_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_DEST_TYPE_SHIFT,
- CONV_SRC_TYPE_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_SRC_TYPE_SHIFT,
- CONV_STACK_MOVE_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_STACK_MOVE_SHIFT,
- CONV_STACK_MOVE_MASK = java_lang_invoke_AdapterMethodHandle::CONV_STACK_MOVE_MASK
- };
-
static bool _enabled;
- 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;
- static bool ek_valid(EntryKind ek) { return (uint)ek < (uint)_EK_LIMIT; }
- static bool conv_op_valid(int op) { return (uint)op < (uint)CONV_OP_LIMIT; }
-
- public:
- static bool have_entry(EntryKind ek) { return ek_valid(ek) && _entries[ek] != NULL; }
- static MethodHandleEntry* entry(EntryKind ek) { assert(ek_valid(ek), "initialized");
- return _entries[ek]; }
- static const char* entry_name(EntryKind ek) { assert(ek_valid(ek), "oob");
- return _entry_names[ek]; }
- static EntryKind adapter_entry_kind(int op) { assert(conv_op_valid(op), "oob");
- return EntryKind(_adapter_mh_first + op); }
-
- static void init_entry(EntryKind ek, MethodHandleEntry* me) {
- assert(ek_valid(ek), "oob");
- assert(_entries[ek] == NULL, "no double initialization");
- _entries[ek] = me;
- }
-
- // Some adapter helper functions.
- static EntryKind ek_original_kind(EntryKind ek) {
- if (ek <= _adapter_mh_last) return ek;
- switch (ek) {
- 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;
- }
- 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;
- if (ek == _adapter_opt_profiling)
- return _adapter_retype_only;
- 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 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_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"
- }
- }
-
- 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 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_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;
- }
- }
-
- static methodOop raise_exception_method() {
- oop rem = JNIHandles::resolve(_raise_exception_method);
- assert(rem == NULL || rem->is_method(), "");
- return (methodOop) rem;
- }
- static void set_raise_exception_method(methodOop rem) {
- assert(_raise_exception_method == NULL, "");
- _raise_exception_method = JNIHandles::make_global(Handle(rem));
- }
- static methodOop resolve_raise_exception_method(TRAPS);
- // call raise_exception_method from C code:
- static void raise_exception(int code, oop actual, oop required, TRAPS);
-
- static jint adapter_conversion(int conv_op, BasicType src, BasicType dest,
- int stack_move = 0, int vminfo = 0) {
- assert(conv_op_valid(conv_op), "oob");
- jint conv = ((conv_op << CONV_OP_SHIFT)
- | (src << CONV_SRC_TYPE_SHIFT)
- | (dest << CONV_DEST_TYPE_SHIFT)
- | (stack_move << CONV_STACK_MOVE_SHIFT)
- | (vminfo << CONV_VMINFO_SHIFT)
- );
- assert(adapter_conversion_op(conv) == conv_op, "decode conv_op");
- assert(adapter_conversion_src_type(conv) == src, "decode src");
- assert(adapter_conversion_dest_type(conv) == dest, "decode dest");
- assert(adapter_conversion_stack_move(conv) == stack_move, "decode stack_move");
- assert(adapter_conversion_vminfo(conv) == vminfo, "decode vminfo");
- return conv;
- }
- static int adapter_conversion_op(jint conv) {
- return ((conv >> CONV_OP_SHIFT) & 0xF);
- }
- static BasicType adapter_conversion_src_type(jint conv) {
- return (BasicType)((conv >> CONV_SRC_TYPE_SHIFT) & 0xF);
- }
- static BasicType adapter_conversion_dest_type(jint conv) {
- return (BasicType)((conv >> CONV_DEST_TYPE_SHIFT) & 0xF);
- }
- static int adapter_conversion_stack_move(jint conv) {
- return (conv >> CONV_STACK_MOVE_SHIFT);
- }
- static int adapter_conversion_vminfo(jint conv) {
- return (conv >> CONV_VMINFO_SHIFT) & CONV_VMINFO_MASK;
- }
-
- // 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) {
- if (dest == T_BOOLEAN) return (BitsPerInt - 1); // boolean is 1 bit
- if (dest == T_CHAR) return (BitsPerInt - BitsPerShort);
- if (dest == T_BYTE) return (BitsPerInt - BitsPerByte ) | CONV_VMINFO_SIGN_FLAG;
- if (dest == T_SHORT) return (BitsPerInt - BitsPerShort) | CONV_VMINFO_SIGN_FLAG;
- return 0; // case T_INT
- }
- // Shift values for unboxing a primitive.
- static int adapter_unbox_subword_vminfo(BasicType dest) {
- if (dest == T_BOOLEAN) return (BitsPerInt - BitsPerByte ); // implemented as 1 byte
- if (dest == T_CHAR) return (BitsPerInt - BitsPerShort);
- if (dest == T_BYTE) return (BitsPerInt - BitsPerByte ) | CONV_VMINFO_SIGN_FLAG;
- if (dest == T_SHORT) return (BitsPerInt - BitsPerShort) | CONV_VMINFO_SIGN_FLAG;
- return 0; // case T_INT
- }
- // Here is the transformation the i2i adapter must perform:
- static int truncate_subword_from_vminfo(jint value, int vminfo) {
- int shift = vminfo & ~CONV_VMINFO_SIGN_FLAG;
- jint tem = value << shift;
- if ((vminfo & CONV_VMINFO_SIGN_FLAG) != 0) {
- return (jint)tem >> shift;
- } else {
- return (juint)tem >> shift;
- }
- }
-
- static inline address from_compiled_entry(EntryKind ek);
- static inline address from_interpreted_entry(EntryKind ek);
-
- // helpers for decode_method.
- static methodOop decode_methodOop(methodOop m, int& decode_flags_result);
- static methodHandle decode_vmtarget(oop vmtarget, int vmindex, oop mtype, KlassHandle& receiver_limit_result, int& decode_flags_result);
- static methodHandle decode_MemberName(oop mname, KlassHandle& receiver_limit_result, int& decode_flags_result);
- static methodHandle decode_MethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result);
- static methodHandle decode_DirectMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result);
- static methodHandle decode_BoundMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result);
- static methodHandle decode_AdapterMethodHandle(oop mh, KlassHandle& receiver_limit_result, int& decode_flags_result);
-
- // Find out how many stack slots an mh pushes or pops.
- // The result is *not* reported as a multiple of stack_move_unit();
- // It is a signed net number of pushes (a difference in vmslots).
- // To compare with a stack_move value, first multiply by stack_move_unit().
- static int decode_MethodHandle_stack_pushes(oop mh);
-
public:
// working with member names
- static void resolve_MemberName(Handle mname, TRAPS); // compute vmtarget/vmindex from name/type
+ static Handle resolve_MemberName(Handle mname, TRAPS); // compute vmtarget/vmindex from name/type
static void expand_MemberName(Handle mname, int suppress, TRAPS); // expand defc/name/type if missing
static Handle new_MemberName(TRAPS); // must be followed by init_MemberName
- static void init_MemberName(oop mname_oop, oop target); // compute vmtarget/vmindex from target
- static void init_MemberName(oop mname_oop, methodOop m, bool do_dispatch = true);
- static void init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset);
+ static oop init_MemberName(oop mname_oop, oop target_oop); // compute vmtarget/vmindex from target
+ static oop init_method_MemberName(oop mname_oop, methodOop m, bool do_dispatch,
+ klassOop receiver_limit);
+ static oop init_field_MemberName(oop mname_oop, klassOop field_holder,
+ AccessFlags mods, oop type, oop name,
+ intptr_t offset, bool is_setter = false);
+ static Handle init_method_MemberName(oop mname_oop, CallInfo& info, TRAPS);
+ static Handle init_field_MemberName(oop mname_oop, FieldAccessInfo& info, TRAPS);
+ static int method_ref_kind(methodOop m, bool do_dispatch_if_possible = true);
static int find_MemberNames(klassOop k, Symbol* name, Symbol* sig,
int mflags, klassOop caller,
int skip, objArrayOop results);
@@ -559,169 +72,113 @@
// Generate MethodHandles adapters.
static void generate_adapters();
- // Called from InterpreterGenerator and MethodHandlesAdapterGenerator.
- static address generate_method_handle_interpreter_entry(MacroAssembler* _masm);
- static void generate_method_handle_stub(MacroAssembler* _masm, EntryKind ek);
-
- // argument list parsing
- static int argument_slot(oop method_type, int arg);
- static int argument_slot_count(oop method_type) { return argument_slot(method_type, -1); }
- static int argument_slot_to_argnum(oop method_type, int argslot);
+ // Called from MethodHandlesAdapterGenerator.
+ static address generate_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid);
+ static void generate_method_handle_dispatch(MacroAssembler* _masm,
+ vmIntrinsics::ID iid,
+ Register receiver_reg,
+ Register member_reg,
+ bool for_compiler_entry);
- // Runtime support
- enum { // bit-encoded flags from decode_method or decode_vmref
- _dmf_has_receiver = 0x01, // target method has leading reference argument
- _dmf_does_dispatch = 0x02, // method handle performs virtual or interface dispatch
- _dmf_from_interface = 0x04, // peforms interface dispatch
- _DMF_DIRECT_MASK = (_dmf_from_interface*2 - _dmf_has_receiver),
- _dmf_binds_method = 0x08,
- _dmf_binds_argument = 0x10,
- _DMF_BOUND_MASK = (_dmf_binds_argument*2 - _dmf_binds_method),
- _dmf_adapter_lsb = 0x20,
- _DMF_ADAPTER_MASK = (_dmf_adapter_lsb << CONV_OP_LIMIT) - _dmf_adapter_lsb
- };
- static methodHandle decode_method(oop x, KlassHandle& receiver_limit_result, int& decode_flags_result);
- enum {
- // format of query to getConstant:
- GC_JVM_PUSH_LIMIT = 0,
- GC_JVM_STACK_MOVE_UNIT = 1,
- GC_CONV_OP_IMPLEMENTED_MASK = 2,
- GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS = 3,
- GC_COUNT_GWT = 4,
+ // Queries
+ static bool is_signature_polymorphic(vmIntrinsics::ID iid) {
+ return (iid >= vmIntrinsics::FIRST_MH_SIG_POLY &&
+ iid <= vmIntrinsics::LAST_MH_SIG_POLY);
+ }
- // format of result from getTarget / encode_target:
- ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
- ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
- ETF_METHOD_NAME = 2, // ultimate method as MemberName
- ETF_REFLECT_METHOD = 3, // ultimate method as java.lang.reflect object (sans refClass)
- ETF_FORCE_DIRECT_HANDLE = 64,
- ETF_COMPILE_DIRECT_HANDLE = 65,
-
- // ad hoc constants
- OP_ROT_ARGS_DOWN_LIMIT_BIAS = -1
- };
- static int get_named_constant(int which, Handle name_box, TRAPS);
- static oop encode_target(Handle mh, int format, TRAPS); // report vmtarget (to Java code)
- static bool class_cast_needed(klassOop src, klassOop dst);
-
- static instanceKlassHandle resolve_instance_klass(oop java_mirror_oop, TRAPS);
- static instanceKlassHandle resolve_instance_klass(jclass java_mirror_jh, TRAPS) {
- return resolve_instance_klass(JNIHandles::resolve(java_mirror_jh), THREAD);
+ static bool is_signature_polymorphic_intrinsic(vmIntrinsics::ID iid) {
+ assert(is_signature_polymorphic(iid), "");
+ // Most sig-poly methods are intrinsics which do not require an
+ // appeal to Java for adapter code.
+ return (iid != vmIntrinsics::_invokeGeneric);
}
- private:
- // These checkers operate on a pair of whole MethodTypes:
- static const char* check_method_type_change(oop src_mtype, int src_beg, int src_end,
- int insert_argnum, oop insert_type,
- int change_argnum, oop change_type,
- int delete_argnum,
- oop dst_mtype, int dst_beg, int dst_end,
- bool raw = false);
- static const char* check_method_type_insertion(oop src_mtype,
- int insert_argnum, oop insert_type,
- oop dst_mtype) {
- oop no_ref = NULL;
- return check_method_type_change(src_mtype, 0, -1,
- insert_argnum, insert_type,
- -1, no_ref, -1, dst_mtype, 0, -1);
+ static bool is_signature_polymorphic_static(vmIntrinsics::ID iid) {
+ assert(is_signature_polymorphic(iid), "");
+ return (iid >= vmIntrinsics::FIRST_MH_STATIC &&
+ iid <= vmIntrinsics::LAST_MH_SIG_POLY);
+ }
+
+ static bool has_member_arg(vmIntrinsics::ID iid) {
+ assert(is_signature_polymorphic(iid), "");
+ return (iid >= vmIntrinsics::_linkToVirtual &&
+ iid <= vmIntrinsics::_linkToInterface);
}
- static const char* check_method_type_conversion(oop src_mtype,
- int change_argnum, oop change_type,
- oop dst_mtype) {
- oop no_ref = NULL;
- return check_method_type_change(src_mtype, 0, -1, -1, no_ref,
- change_argnum, change_type,
- -1, dst_mtype, 0, -1);
+ static bool has_member_arg(Symbol* klass, Symbol* name) {
+ if ((klass == vmSymbols::java_lang_invoke_MethodHandle()) &&
+ is_signature_polymorphic_name(name)) {
+ vmIntrinsics::ID iid = signature_polymorphic_name_id(name);
+ return has_member_arg(iid);
+ }
+ return false;
}
- static const char* check_method_type_passthrough(oop src_mtype, oop dst_mtype, bool raw) {
- oop no_ref = NULL;
- return check_method_type_change(src_mtype, 0, -1,
- -1, no_ref, -1, no_ref, -1,
- dst_mtype, 0, -1, raw);
+
+ static Symbol* signature_polymorphic_intrinsic_name(vmIntrinsics::ID iid);
+ static int signature_polymorphic_intrinsic_ref_kind(vmIntrinsics::ID iid);
+
+ static vmIntrinsics::ID signature_polymorphic_name_id(klassOop klass, Symbol* name);
+ static vmIntrinsics::ID signature_polymorphic_name_id(Symbol* name);
+ static bool is_signature_polymorphic_name(Symbol* name) {
+ return signature_polymorphic_name_id(name) != vmIntrinsics::_none;
+ }
+ static bool is_method_handle_invoke_name(klassOop klass, Symbol* name);
+ static bool is_signature_polymorphic_name(klassOop klass, Symbol* name) {
+ return signature_polymorphic_name_id(klass, name) != vmIntrinsics::_none;
}
- // These checkers operate on pairs of argument or return types:
- static const char* check_argument_type_change(BasicType src_type, klassOop src_klass,
- BasicType dst_type, klassOop dst_klass,
- int argnum, bool raw = false);
+ enum {
+ // format of query to getConstant:
+ GC_COUNT_GWT = 4,
+ GC_LAMBDA_SUPPORT = 5
+ };
+ static int get_named_constant(int which, Handle name_box, TRAPS);
+
+public:
+ static Symbol* lookup_signature(oop type_str, bool polymorphic, TRAPS); // use TempNewSymbol
+ static Symbol* lookup_basic_type_signature(Symbol* sig, bool keep_last_arg, TRAPS); // use TempNewSymbol
+ static Symbol* lookup_basic_type_signature(Symbol* sig, TRAPS) {
+ return lookup_basic_type_signature(sig, false, THREAD);
+ }
+ static bool is_basic_type_signature(Symbol* sig);
+
+ static Symbol* lookup_method_type(Symbol* msig, Handle mtype, TRAPS);
+
+ static void print_as_method_type_on(outputStream* st, Symbol* sig) {
+ print_as_basic_type_signature_on(st, sig, true, true);
+ }
+ static void print_as_basic_type_signature_on(outputStream* st, Symbol* sig, bool keep_arrays = false, bool keep_basic_names = false);
- static const char* check_argument_type_change(oop src_type, oop dst_type,
- int argnum, bool raw = false) {
- klassOop src_klass = NULL, dst_klass = NULL;
- BasicType src_bt = java_lang_Class::as_BasicType(src_type, &src_klass);
- BasicType dst_bt = java_lang_Class::as_BasicType(dst_type, &dst_klass);
- return check_argument_type_change(src_bt, src_klass,
- dst_bt, dst_klass, argnum, raw);
+ // decoding CONSTANT_MethodHandle constants
+ enum { JVM_REF_MIN = JVM_REF_getField, JVM_REF_MAX = JVM_REF_invokeInterface };
+ static bool ref_kind_is_valid(int ref_kind) {
+ return (ref_kind >= JVM_REF_MIN && ref_kind <= JVM_REF_MAX);
+ }
+ static bool ref_kind_is_field(int ref_kind) {
+ assert(ref_kind_is_valid(ref_kind), "");
+ return (ref_kind <= JVM_REF_putStatic);
+ }
+ static bool ref_kind_is_getter(int ref_kind) {
+ assert(ref_kind_is_valid(ref_kind), "");
+ return (ref_kind <= JVM_REF_getStatic);
}
-
- static const char* check_return_type_change(oop src_type, oop dst_type, bool raw = false) {
- return check_argument_type_change(src_type, dst_type, -1, raw);
+ static bool ref_kind_is_setter(int ref_kind) {
+ return ref_kind_is_field(ref_kind) && !ref_kind_is_getter(ref_kind);
+ }
+ static bool ref_kind_is_method(int ref_kind) {
+ return !ref_kind_is_field(ref_kind) && (ref_kind != JVM_REF_newInvokeSpecial);
}
-
- static const char* check_return_type_change(BasicType src_type, klassOop src_klass,
- BasicType dst_type, klassOop dst_klass) {
- return check_argument_type_change(src_type, src_klass, dst_type, dst_klass, -1);
+ static bool ref_kind_has_receiver(int ref_kind) {
+ assert(ref_kind_is_valid(ref_kind), "");
+ return (ref_kind & 1) != 0;
+ }
+ static bool ref_kind_is_static(int ref_kind) {
+ return !ref_kind_has_receiver(ref_kind) && (ref_kind != JVM_REF_newInvokeSpecial);
+ }
+ static bool ref_kind_does_dispatch(int ref_kind) {
+ return (ref_kind == JVM_REF_invokeVirtual ||
+ ref_kind == JVM_REF_invokeInterface);
}
- static const char* check_method_receiver(methodOop m, klassOop passed_recv_type);
-
- // These verifiers can block, and will throw an error if the checking fails:
- static void verify_vmslots(Handle mh, TRAPS);
- static void verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS);
-
- static void verify_method_type(methodHandle m, Handle mtype,
- bool has_bound_oop,
- KlassHandle bound_oop_type,
- TRAPS);
-
- static void verify_method_signature(methodHandle m, Handle mtype,
- int first_ptype_pos,
- KlassHandle insert_ptype, TRAPS);
-
- static void verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS);
- static void verify_BoundMethodHandle(Handle mh, Handle target, int argnum,
- bool direct_to_method, TRAPS);
- static void verify_BoundMethodHandle_with_receiver(Handle mh, methodHandle m, TRAPS);
- static void verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS);
-
- public:
-
- // Fill in the fields of a DirectMethodHandle mh. (MH.type must be pre-filled.)
- static void init_DirectMethodHandle(Handle mh, methodHandle method, bool do_dispatch, TRAPS);
-
- // Fill in the fields of a BoundMethodHandle mh. (MH.type, BMH.argument must be pre-filled.)
- static void init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
- static void init_BoundMethodHandle_with_receiver(Handle mh,
- methodHandle original_m,
- KlassHandle receiver_limit,
- int decode_flags,
- TRAPS);
-
- // Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.)
- static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
- static void ensure_vmlayout_field(Handle target, TRAPS);
-
-#ifdef ASSERT
- static bool spot_check_entry_names();
-#endif
-
- private:
- static methodHandle dispatch_decoded_method(methodHandle m,
- KlassHandle receiver_limit,
- int decode_flags,
- KlassHandle receiver_klass,
- TRAPS);
-
-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);
- static bool same_basic_type_for_returns(BasicType src, BasicType dst, bool raw = false) {
- return same_basic_type_for_arguments(src, dst, raw, true);
- }
-
- static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS);
#ifdef TARGET_ARCH_x86
# include "methodHandles_x86.hpp"
@@ -738,62 +195,10 @@
#ifdef TARGET_ARCH_ppc
# include "methodHandles_ppc.hpp"
#endif
-};
-// Access methods for the "entry" field of a java.lang.invoke.MethodHandle.
-// The field is primarily a jump target for compiled calls.
-// However, we squirrel away some nice pointers for other uses,
-// just before the jump target.
-// Aspects of a method handle entry:
-// - from_compiled_entry - stub used when compiled code calls the MH
-// - from_interpreted_entry - stub used when the interpreter calls the MH
-// - type_checking_entry - stub for runtime casting between MHForm siblings (NYI)
-class MethodHandleEntry {
- public:
- class Data {
- friend class MethodHandleEntry;
- size_t _total_size; // size including Data and code stub
- MethodHandleEntry* _type_checking_entry;
- address _from_interpreted_entry;
- MethodHandleEntry* method_entry() { return (MethodHandleEntry*)(this + 1); }
- };
-
- Data* data() { return (Data*)this - 1; }
-
- address start_address() { return (address) data(); }
- address end_address() { return start_address() + data()->_total_size; }
-
- address from_compiled_entry() { return (address) this; }
-
- address from_interpreted_entry() { return data()->_from_interpreted_entry; }
- void set_from_interpreted_entry(address e) { data()->_from_interpreted_entry = e; }
-
- MethodHandleEntry* type_checking_entry() { return data()->_type_checking_entry; }
- void set_type_checking_entry(MethodHandleEntry* e) { data()->_type_checking_entry = e; }
-
- void set_end_address(address end_addr) {
- size_t total_size = end_addr - start_address();
- assert(total_size > 0 && total_size < 0x1000, "reasonable end address");
- data()->_total_size = total_size;
- }
-
- // Compiler support:
- static int from_interpreted_entry_offset_in_bytes() {
- return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) );
- }
- static int type_checking_entry_offset_in_bytes() {
- return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) );
- }
-
- static address start_compiled_entry(MacroAssembler* _masm,
- address interpreted_entry = NULL);
- static MethodHandleEntry* finish_compiled_entry(MacroAssembler* masm, address start_addr);
};
-address MethodHandles::from_compiled_entry(EntryKind ek) { return entry(ek)->from_compiled_entry(); }
-address MethodHandles::from_interpreted_entry(EntryKind ek) { return entry(ek)->from_interpreted_entry(); }
-
//------------------------------------------------------------------------------
// MethodHandlesAdapterGenerator
--- a/hotspot/src/share/vm/prims/nativeLookup.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/nativeLookup.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -381,7 +381,10 @@
address NativeLookup::lookup(methodHandle method, bool& in_base_library, TRAPS) {
if (!method->has_native_function()) {
- address entry = lookup_base(method, in_base_library, CHECK_NULL);
+ address entry =
+ method->intrinsic_id() == vmIntrinsics::_invokeGeneric ?
+ SharedRuntime::native_method_throw_unsupported_operation_exception_entry() :
+ lookup_base(method, in_base_library, CHECK_NULL);
method->set_native_function(entry,
methodOopDesc::native_bind_event_is_interesting);
// -verbose:jni printing
--- a/hotspot/src/share/vm/prims/unsafe.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/prims/unsafe.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -761,16 +761,33 @@
return JNIHandles::make_local(env, JNIHandles::resolve_non_null(clazz));
UNSAFE_END
-UNSAFE_ENTRY(void, Unsafe_EnsureClassInitialized(JNIEnv *env, jobject unsafe, jobject clazz))
+UNSAFE_ENTRY(void, Unsafe_EnsureClassInitialized(JNIEnv *env, jobject unsafe, jobject clazz)) {
UnsafeWrapper("Unsafe_EnsureClassInitialized");
if (clazz == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
oop mirror = JNIHandles::resolve_non_null(clazz);
- instanceKlass* k = instanceKlass::cast(java_lang_Class::as_klassOop(mirror));
- if (k != NULL) {
+
+ klassOop klass = java_lang_Class::as_klassOop(mirror);
+ if (klass != NULL && Klass::cast(klass)->should_be_initialized()) {
+ instanceKlass* k = instanceKlass::cast(klass);
k->initialize(CHECK);
}
+}
+UNSAFE_END
+
+UNSAFE_ENTRY(jboolean, Unsafe_ShouldBeInitialized(JNIEnv *env, jobject unsafe, jobject clazz)) {
+ UnsafeWrapper("Unsafe_ShouldBeInitialized");
+ if (clazz == NULL) {
+ THROW_(vmSymbols::java_lang_NullPointerException(), false);
+ }
+ oop mirror = JNIHandles::resolve_non_null(clazz);
+ klassOop klass = java_lang_Class::as_klassOop(mirror);
+ if (klass != NULL && Klass::cast(klass)->should_be_initialized()) {
+ return true;
+ }
+ return false;
+}
UNSAFE_END
static void getBaseAndScale(int& base, int& scale, jclass acls, TRAPS) {
@@ -1566,6 +1583,10 @@
{CC"defineAnonymousClass", CC"("DAC_Args")"CLS, FN_PTR(Unsafe_DefineAnonymousClass)},
};
+JNINativeMethod lform_methods[] = {
+ {CC"shouldBeInitialized",CC"("CLS")Z", FN_PTR(Unsafe_ShouldBeInitialized)},
+};
+
#undef CC
#undef FN_PTR
@@ -1636,6 +1657,15 @@
env->ExceptionClear();
}
}
+ if (EnableInvokeDynamic) {
+ env->RegisterNatives(unsafecls, lform_methods, sizeof(lform_methods)/sizeof(JNINativeMethod));
+ if (env->ExceptionOccurred()) {
+ if (PrintMiscellaneous && (Verbose || WizardMode)) {
+ tty->print_cr("Warning: SDK 1.7 LambdaForm support in Unsafe not found.");
+ }
+ env->ExceptionClear();
+ }
+ }
int status = env->RegisterNatives(unsafecls, methods, sizeof(methods)/sizeof(JNINativeMethod));
if (env->ExceptionOccurred()) {
if (PrintMiscellaneous && (Verbose || WizardMode)) {
--- a/hotspot/src/share/vm/runtime/arguments.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -3087,15 +3087,6 @@
}
#endif // PRODUCT
- // Transitional
- if (EnableMethodHandles || AnonymousClasses) {
- if (!EnableInvokeDynamic && !FLAG_IS_DEFAULT(EnableInvokeDynamic)) {
- warning("EnableMethodHandles and AnonymousClasses are obsolete. Keeping EnableInvokeDynamic disabled.");
- } else {
- EnableInvokeDynamic = true;
- }
- }
-
// JSR 292 is not supported before 1.7
if (!JDK_Version::is_gte_jdk17x_version()) {
if (EnableInvokeDynamic) {
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -388,7 +388,7 @@
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());
- if (cur.is_method_handle_invoke()) {
+ if (cur.is_invokedynamic() || cur.is_invokehandle()) {
// Method handle invokes may involve fairly arbitrary chains of
// calls so it's impossible to know how much actual space the
// caller has for locals.
--- a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -116,6 +116,7 @@
void initialize(klassOop k, int index);
// Print
+ void print() { print_on(tty); }
void print_on(outputStream* st) const PRODUCT_RETURN;
void print_on_for(outputStream* st, oop obj) PRODUCT_RETURN;
};
--- a/hotspot/src/share/vm/runtime/fprofiler.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/fprofiler.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -337,11 +337,13 @@
char c = (char) n->byte_at(i);
st->print("%c", c);
}
- if( Verbose ) {
+ if (Verbose || WizardMode) {
// Disambiguate overloaded methods
Symbol* sig = m->signature();
sig->print_symbol_on(st);
- }
+ } else if (MethodHandles::is_signature_polymorphic(m->intrinsic_id()))
+ // compare with methodOopDesc::print_short_name
+ MethodHandles::print_as_basic_type_signature_on(st, m->signature(), true);
}
virtual void print(outputStream* st, int total_ticks) {
--- a/hotspot/src/share/vm/runtime/frame.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/frame.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -170,11 +170,9 @@
}
// 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_ignored_frame() const {
+ return false; // FIXME: some LambdaForm frames should be ignored
}
-
bool frame::is_deoptimized_frame() const {
assert(_deopt_state != unknown, "not answerable");
return _deopt_state == is_deoptimized;
@@ -348,17 +346,12 @@
frame frame::real_sender(RegisterMap* map) const {
frame result = sender(map);
while (result.is_runtime_frame() ||
- result.is_ricochet_frame()) {
+ result.is_ignored_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
@@ -541,7 +534,6 @@
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";
@@ -728,8 +720,6 @@
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()) {
@@ -993,9 +983,6 @@
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);
@@ -1014,11 +1001,6 @@
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;
@@ -1087,7 +1069,7 @@
// First consult the ADLC on where it puts parameter 0 for this signature.
VMReg reg = SharedRuntime::name_for_receiver();
oop r = *caller.oopmapreg_to_location(reg, reg_map);
- assert( Universe::heap()->is_in_or_null(r), "bad receiver" );
+ assert(Universe::heap()->is_in_or_null(r), err_msg("bad receiver: " INTPTR_FORMAT " (" INTX_FORMAT ")", (intptr_t) r, (intptr_t) r));
return r;
}
@@ -1407,8 +1389,6 @@
values.describe(-1, info_address,
FormatBuffer<1024>("#%d nmethod " INTPTR_FORMAT " for native method %s", frame_no,
nm, nm->method()->name_and_sig_as_C_string()), 2);
- } else if (is_ricochet_frame()) {
- values.describe(-1, info_address, err_msg("#%d ricochet frame", frame_no), 2);
} else {
// provide default info if not handled before
char *info = (char *) "special frame";
--- a/hotspot/src/share/vm/runtime/frame.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/frame.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -135,7 +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_ignored_frame() const;
bool is_native_frame() const;
bool is_runtime_frame() const;
bool is_compiled_frame() const;
@@ -176,7 +176,6 @@
// 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;
@@ -415,7 +414,6 @@
// 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);
--- a/hotspot/src/share/vm/runtime/globals.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/globals.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -930,6 +930,9 @@
diagnostic(bool, PrintAdapterHandlers, false, \
"Print code generated for i2c/c2i adapters") \
\
+ diagnostic(bool, VerifyAdapterCalls, trueInDebug, \
+ "Verify that i2c/c2i adapters are called properly") \
+ \
develop(bool, VerifyAdapterSharing, false, \
"Verify that the code for shared adapters is the equivalent") \
\
@@ -3832,12 +3835,6 @@
product(bool, AnonymousClasses, false, \
"support sun.misc.Unsafe.defineAnonymousClass (deprecated)") \
\
- experimental(bool, EnableMethodHandles, false, \
- "support method handles (deprecated)") \
- \
- diagnostic(intx, MethodHandlePushLimit, 3, \
- "number of additional stack slots a method handle may push") \
- \
diagnostic(bool, PrintMethodHandleStubs, false, \
"Print generated stub code for method handles") \
\
@@ -3847,19 +3844,12 @@
diagnostic(bool, VerifyMethodHandles, trueInDebug, \
"perform extra checks when constructing method handles") \
\
- diagnostic(bool, OptimizeMethodHandles, true, \
- "when constructing method handles, try to improve them") \
- \
- develop(bool, StressMethodHandleWalk, false, \
- "Process all method handles with MethodHandleWalk") \
+ diagnostic(bool, ShowHiddenFrames, false, \
+ "show method handle implementation frames (usually hidden)") \
\
experimental(bool, TrustFinalNonStaticFields, false, \
"trust final non-static declarations for constant folding") \
\
- experimental(bool, AllowInvokeGeneric, false, \
- "accept MethodHandle.invoke and MethodHandle.invokeGeneric " \
- "as equivalent methods") \
- \
develop(bool, TraceInvokeDynamic, false, \
"trace internal invoke dynamic operations") \
\
--- a/hotspot/src/share/vm/runtime/os.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/os.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -822,7 +822,7 @@
// the interpreter is generated into a buffer blob
InterpreterCodelet* i = Interpreter::codelet_containing(addr);
if (i != NULL) {
- st->print_cr(INTPTR_FORMAT " is an Interpreter codelet", addr);
+ st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an Interpreter codelet", addr, (int)(addr - i->code_begin()));
i->print_on(st);
return;
}
@@ -833,14 +833,15 @@
}
//
if (AdapterHandlerLibrary::contains(b)) {
- st->print_cr(INTPTR_FORMAT " is an AdapterHandler", addr);
+ st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an AdapterHandler", addr, (int)(addr - b->code_begin()));
AdapterHandlerLibrary::print_handler_on(st, b);
}
// the stubroutines are generated into a buffer blob
StubCodeDesc* d = StubCodeDesc::desc_for(addr);
if (d != NULL) {
+ st->print_cr(INTPTR_FORMAT " is at begin+%d in a stub", addr, (int)(addr - d->begin()));
d->print_on(st);
- if (verbose) st->cr();
+ st->cr();
return;
}
if (StubRoutines::contains(addr)) {
@@ -855,26 +856,25 @@
}
VtableStub* v = VtableStubs::stub_containing(addr);
if (v != NULL) {
+ st->print_cr(INTPTR_FORMAT " is at entry_point+%d in a vtable stub", addr, (int)(addr - v->entry_point()));
v->print_on(st);
+ st->cr();
return;
}
}
- if (verbose && b->is_nmethod()) {
+ nmethod* nm = b->as_nmethod_or_null();
+ if (nm != NULL) {
ResourceMark rm;
- st->print("%#p: Compiled ", addr);
- ((nmethod*)b)->method()->print_value_on(st);
- st->print(" = (CodeBlob*)" INTPTR_FORMAT, b);
- st->cr();
+ st->print(INTPTR_FORMAT " is at entry_point+%d in (nmethod*)" INTPTR_FORMAT,
+ addr, (int)(addr - nm->entry_point()), nm);
+ if (verbose) {
+ st->print(" for ");
+ nm->method()->print_value_on(st);
+ }
+ nm->print_nmethod(verbose);
return;
}
- st->print(INTPTR_FORMAT " ", b);
- if ( b->is_nmethod()) {
- if (b->is_zombie()) {
- st->print_cr("is zombie nmethod");
- } else if (b->is_not_entrant()) {
- st->print_cr("is non-entrant nmethod");
- }
- }
+ st->print_cr(INTPTR_FORMAT " is at code_begin+%d in ", addr, (int)(addr - b->code_begin()));
b->print_on(st);
return;
}
--- a/hotspot/src/share/vm/runtime/reflection.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/reflection.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -36,7 +36,6 @@
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "prims/jvm.h"
-#include "prims/methodHandleWalk.hpp"
#include "runtime/arguments.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
@@ -502,11 +501,6 @@
under_host_klass(accessee_ik, accessor))
return true;
- // Adapter frames can access anything.
- if (MethodHandleCompiler::klass_is_method_handle_adapter_holder(accessor))
- // This is an internal adapter frame from the MethodHandleCompiler.
- return true;
-
if (RelaxAccessControlCheck ||
(accessor_ik->major_version() < JAVA_1_5_VERSION &&
accessee_ik->major_version() < JAVA_1_5_VERSION)) {
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -88,8 +88,6 @@
RuntimeStub* SharedRuntime::_resolve_static_call_blob;
DeoptimizationBlob* SharedRuntime::_deopt_blob;
-RicochetBlob* SharedRuntime::_ricochet_blob;
-
SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob;
SafepointBlob* SharedRuntime::_polling_page_return_handler_blob;
@@ -109,7 +107,6 @@
_polling_page_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), false);
_polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), true);
- generate_ricochet_blob();
generate_deopt_blob();
#ifdef COMPILER2
@@ -117,33 +114,6 @@
#endif // COMPILER2
}
-//----------------------------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 LP64_ONLY(+ 256), 256); // XXX x86 LP64L: 512, 512
- MacroAssembler* masm = new MacroAssembler(&buffer);
-
- int bounce_offset = -1, exception_offset = -1, frame_size_in_words = -1;
- MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &bounce_offset, &exception_offset, &frame_size_in_words);
-
- // -------------
- // make sure all code is generated
- masm->flush();
-
- // failed to generate?
- if (bounce_offset < 0 || exception_offset < 0 || frame_size_in_words < 0) {
- assert(false, "bad ricochet blob");
- return;
- }
-
- _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
-}
-
-
#include <math.h>
#ifndef USDT2
@@ -527,10 +497,6 @@
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!");
@@ -768,13 +734,6 @@
throw_and_post_jvmti_exception(thread, exception);
JRT_END
-JRT_ENTRY(void, SharedRuntime::throw_WrongMethodTypeException(JavaThread* thread, oopDesc* required, oopDesc* actual))
- assert(thread == JavaThread::current() && required->is_oop() && actual->is_oop(), "bad args");
- ResourceMark rm;
- char* message = SharedRuntime::generate_wrong_method_type_message(thread, required, actual);
- throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_invoke_WrongMethodTypeException(), message);
-JRT_END
-
address SharedRuntime::continuation_for_implicit_exception(JavaThread* thread,
address pc,
SharedRuntime::ImplicitExceptionKind exception_kind)
@@ -857,6 +816,12 @@
return StubRoutines::throw_NullPointerException_at_call_entry();
}
+ if (nm->method()->is_method_handle_intrinsic()) {
+ // exception happened inside MH dispatch code, similar to a vtable stub
+ Events::log_exception(thread, "NullPointerException in MH adapter " INTPTR_FORMAT, pc);
+ return StubRoutines::throw_NullPointerException_at_call_entry();
+ }
+
#ifndef PRODUCT
_implicit_null_throws++;
#endif
@@ -909,11 +874,20 @@
}
JNI_END
+JNI_ENTRY(void, throw_unsupported_operation_exception(JNIEnv* env, ...))
+{
+ THROW(vmSymbols::java_lang_UnsupportedOperationException());
+}
+JNI_END
address SharedRuntime::native_method_throw_unsatisfied_link_error_entry() {
return CAST_FROM_FN_PTR(address, &throw_unsatisfied_link_error);
}
+address SharedRuntime::native_method_throw_unsupported_operation_exception_entry() {
+ return CAST_FROM_FN_PTR(address, &throw_unsupported_operation_exception);
+}
+
#ifndef PRODUCT
JRT_ENTRY(intptr_t, SharedRuntime::trace_bytecode(JavaThread* thread, intptr_t preserve_this_value, intptr_t tos, intptr_t tos2))
@@ -1045,16 +1019,17 @@
assert(!vfst.at_end(), "Java frame must exist");
// Find caller and bci from vframe
- methodHandle caller (THREAD, vfst.method());
- int bci = vfst.bci();
+ methodHandle caller(THREAD, vfst.method());
+ int bci = vfst.bci();
// Find bytecode
Bytecode_invoke bytecode(caller, bci);
- bc = bytecode.java_code();
+ bc = bytecode.invoke_code();
int bytecode_index = bytecode.index();
// Find receiver for non-static call
- if (bc != Bytecodes::_invokestatic) {
+ if (bc != Bytecodes::_invokestatic &&
+ bc != Bytecodes::_invokedynamic) {
// This register map must be update since we need to find the receiver for
// compiled frames. The receiver might be in a register.
RegisterMap reg_map2(thread);
@@ -1075,25 +1050,32 @@
}
// Resolve method. This is parameterized by bytecode.
- constantPoolHandle constants (THREAD, caller->constants());
- assert (receiver.is_null() || receiver->is_oop(), "wrong receiver");
+ constantPoolHandle constants(THREAD, caller->constants());
+ assert(receiver.is_null() || receiver->is_oop(), "wrong receiver");
LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_(nullHandle));
#ifdef ASSERT
// Check that the receiver klass is of the right subtype and that it is initialized for virtual calls
if (bc != Bytecodes::_invokestatic && bc != Bytecodes::_invokedynamic) {
assert(receiver.not_null(), "should have thrown exception");
- KlassHandle receiver_klass (THREAD, receiver->klass());
+ KlassHandle receiver_klass(THREAD, receiver->klass());
klassOop rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle));
// klass is already loaded
- KlassHandle static_receiver_klass (THREAD, rk);
- assert(receiver_klass->is_subtype_of(static_receiver_klass()), "actual receiver must be subclass of static receiver klass");
+ KlassHandle static_receiver_klass(THREAD, rk);
+ // Method handle invokes might have been optimized to a direct call
+ // so don't check for the receiver class.
+ // FIXME this weakens the assert too much
+ methodHandle callee = callinfo.selected_method();
+ assert(receiver_klass->is_subtype_of(static_receiver_klass()) ||
+ callee->is_method_handle_intrinsic() ||
+ callee->is_compiled_lambda_form(),
+ "actual receiver must be subclass of static receiver klass");
if (receiver_klass->oop_is_instance()) {
if (instanceKlass::cast(receiver_klass())->is_not_initialized()) {
tty->print_cr("ERROR: Klass not yet initialized!!");
receiver_klass.print();
}
- assert (!instanceKlass::cast(receiver_klass())->is_not_initialized(), "receiver_klass must be initialized");
+ assert(!instanceKlass::cast(receiver_klass())->is_not_initialized(), "receiver_klass must be initialized");
}
}
#endif
@@ -1186,8 +1168,10 @@
call_info, CHECK_(methodHandle()));
methodHandle callee_method = call_info.selected_method();
- assert((!is_virtual && invoke_code == Bytecodes::_invokestatic) ||
- ( is_virtual && invoke_code != Bytecodes::_invokestatic), "inconsistent bytecode");
+ assert((!is_virtual && invoke_code == Bytecodes::_invokestatic ) ||
+ (!is_virtual && invoke_code == Bytecodes::_invokehandle ) ||
+ (!is_virtual && invoke_code == Bytecodes::_invokedynamic) ||
+ ( is_virtual && invoke_code != Bytecodes::_invokestatic ), "inconsistent bytecode");
#ifndef PRODUCT
// tracing/debugging/statistics
@@ -1202,16 +1186,17 @@
(is_optimized) ? "optimized " : "", (is_virtual) ? "virtual" : "static",
Bytecodes::name(invoke_code));
callee_method->print_short_name(tty);
- tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code());
+ tty->print_cr(" at pc: " INTPTR_FORMAT " to code: " INTPTR_FORMAT, caller_frame.pc(), callee_method->code());
}
#endif
- // JSR 292
+ // JSR 292 key invariant:
// If the resolved method is a MethodHandle invoke target the call
- // site must be a MethodHandle call site.
- if (callee_method->is_method_handle_invoke()) {
- assert(caller_nm->is_method_handle_return(caller_frame.pc()), "must be MH call site");
- }
+ // site must be a MethodHandle call site, because the lambda form might tail-call
+ // leaving the stack in a state unknown to either caller or callee
+ // TODO detune for now but we might need it again
+// assert(!callee_method->is_compiled_lambda_form() ||
+// caller_nm->is_method_handle_return(caller_frame.pc()), "must be MH call site");
// Compute entry points. This might require generation of C2I converter
// frames, so we cannot be holding any locks here. Furthermore, the
@@ -1284,7 +1269,6 @@
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;
@@ -1320,21 +1304,9 @@
address sender_pc = caller_frame.pc();
CodeBlob* sender_cb = caller_frame.cb();
nmethod* sender_nm = sender_cb->as_nmethod_or_null();
- bool is_mh_invoke_via_adapter = false; // Direct c2c call or via adapter?
- if (sender_nm != NULL && sender_nm->is_method_handle_return(sender_pc)) {
- // If the callee_target is set, then we have come here via an i2c
- // adapter.
- methodOop callee = thread->callee_target();
- if (callee != NULL) {
- assert(callee->is_method(), "sanity");
- is_mh_invoke_via_adapter = true;
- }
- }
if (caller_frame.is_interpreted_frame() ||
- caller_frame.is_entry_frame() ||
- caller_frame.is_ricochet_frame() ||
- is_mh_invoke_via_adapter) {
+ caller_frame.is_entry_frame()) {
methodOop callee = thread->callee_target();
guarantee(callee != NULL && callee->is_method(), "bad handshake");
thread->set_vm_result(callee);
@@ -1677,12 +1649,6 @@
// Get the return PC for the passed caller PC.
address return_pc = caller_pc + frame::pc_return_offset;
- // Don't fixup method handle call sites as the executed method
- // handle adapters are doing the required MethodHandle chain work.
- if (nm->is_method_handle_return(return_pc)) {
- return;
- }
-
// There is a benign race here. We could be attempting to patch to a compiled
// entry point at the same time the callee is being deoptimized. If that is
// the case then entry_point may in fact point to a c2i and we'd patch the
@@ -1788,97 +1754,6 @@
return generate_class_cast_message(objName, targetKlass->external_name());
}
-char* SharedRuntime::generate_wrong_method_type_message(JavaThread* thread,
- oopDesc* required,
- oopDesc* actual) {
- if (TraceMethodHandles) {
- tty->print_cr("WrongMethodType thread="PTR_FORMAT" req="PTR_FORMAT" act="PTR_FORMAT"",
- thread, required, actual);
- }
- assert(EnableInvokeDynamic, "");
- oop singleKlass = wrong_method_type_is_for_single_argument(thread, required);
- char* message = NULL;
- if (singleKlass != NULL) {
- const char* objName = "argument or return value";
- if (actual != NULL) {
- // be flexible about the junk passed in:
- klassOop ak = (actual->is_klass()
- ? (klassOop)actual
- : actual->klass());
- objName = Klass::cast(ak)->external_name();
- }
- Klass* targetKlass = Klass::cast(required->is_klass()
- ? (klassOop)required
- : java_lang_Class::as_klassOop(required));
- message = generate_class_cast_message(objName, targetKlass->external_name());
- } else {
- // %%% need to get the MethodType string, without messing around too much
- const char* desc = NULL;
- // Get a signature from the invoke instruction
- const char* mhName = "method handle";
- const char* targetType = "the required signature";
- int targetArity = -1, mhArity = -1;
- vframeStream vfst(thread, true);
- if (!vfst.at_end()) {
- Bytecode_invoke call(vfst.method(), vfst.bci());
- methodHandle target;
- {
- EXCEPTION_MARK;
- target = call.static_target(THREAD);
- if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; }
- }
- if (target.not_null()
- && target->is_method_handle_invoke()
- && required == target->method_handle_type()) {
- targetType = target->signature()->as_C_string();
- targetArity = ArgumentCount(target->signature()).size();
- }
- }
- KlassHandle kignore; int dmf_flags = 0;
- methodHandle actual_method = MethodHandles::decode_method(actual, kignore, dmf_flags);
- if ((dmf_flags & ~(MethodHandles::_dmf_has_receiver |
- MethodHandles::_dmf_does_dispatch |
- MethodHandles::_dmf_from_interface)) != 0)
- actual_method = methodHandle(); // MH does extra binds, drops, etc.
- bool has_receiver = ((dmf_flags & MethodHandles::_dmf_has_receiver) != 0);
- if (actual_method.not_null()) {
- mhName = actual_method->signature()->as_C_string();
- mhArity = ArgumentCount(actual_method->signature()).size();
- if (!actual_method->is_static()) mhArity += 1;
- } else if (java_lang_invoke_MethodHandle::is_instance(actual)) {
- oopDesc* mhType = java_lang_invoke_MethodHandle::type(actual);
- mhArity = java_lang_invoke_MethodType::ptype_count(mhType);
- stringStream st;
- java_lang_invoke_MethodType::print_signature(mhType, &st);
- mhName = st.as_string();
- }
- if (targetArity != -1 && targetArity != mhArity) {
- if (has_receiver && targetArity == mhArity-1)
- desc = " cannot be called without a receiver argument as ";
- else
- desc = " cannot be called with a different arity as ";
- }
- message = generate_class_cast_message(mhName, targetType,
- desc != NULL ? desc :
- " cannot be called as ");
- }
- if (TraceMethodHandles) {
- tty->print_cr("WrongMethodType => message=%s", message);
- }
- return message;
-}
-
-oop SharedRuntime::wrong_method_type_is_for_single_argument(JavaThread* thr,
- oopDesc* required) {
- if (required == NULL) return NULL;
- if (required->klass() == SystemDictionary::Class_klass())
- return required;
- if (required->is_klass())
- return Klass::cast(klassOop(required))->java_mirror();
- return NULL;
-}
-
-
char* SharedRuntime::generate_class_cast_message(
const char* objName, const char* targetKlassName, const char* desc) {
size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1;
@@ -2119,8 +1994,17 @@
// that allows sharing of adapters for the same calling convention.
class AdapterFingerPrint : public CHeapObj<mtCode> {
private:
+ enum {
+ _basic_type_bits = 4,
+ _basic_type_mask = right_n_bits(_basic_type_bits),
+ _basic_types_per_int = BitsPerInt / _basic_type_bits,
+ _compact_int_count = 3
+ };
+ // TO DO: Consider integrating this with a more global scheme for compressing signatures.
+ // For now, 4 bits per components (plus T_VOID gaps after double/long) is not excessive.
+
union {
- int _compact[3];
+ int _compact[_compact_int_count];
int* _fingerprint;
} _value;
int _length; // A negative length indicates the fingerprint is in the compact form,
@@ -2129,8 +2013,7 @@
// Remap BasicTypes that are handled equivalently by the adapters.
// These are correct for the current system but someday it might be
// necessary to make this mapping platform dependent.
- static BasicType adapter_encoding(BasicType in) {
- assert((~0xf & in) == 0, "must fit in 4 bits");
+ static int adapter_encoding(BasicType in) {
switch(in) {
case T_BOOLEAN:
case T_BYTE:
@@ -2141,6 +2024,8 @@
case T_OBJECT:
case T_ARRAY:
+ // In other words, we assume that any register good enough for
+ // an int or long is good enough for a managed pointer.
#ifdef _LP64
return T_LONG;
#else
@@ -2165,8 +2050,9 @@
// The fingerprint is based on the BasicType signature encoded
// into an array of ints with eight entries per int.
int* ptr;
- int len = (total_args_passed + 7) >> 3;
- if (len <= (int)(sizeof(_value._compact) / sizeof(int))) {
+ int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int;
+ if (len <= _compact_int_count) {
+ assert(_compact_int_count == 3, "else change next line");
_value._compact[0] = _value._compact[1] = _value._compact[2] = 0;
// Storing the signature encoded as signed chars hits about 98%
// of the time.
@@ -2182,10 +2068,12 @@
int sig_index = 0;
for (int index = 0; index < len; index++) {
int value = 0;
- for (int byte = 0; byte < 8; byte++) {
- if (sig_index < total_args_passed) {
- value = (value << 4) | adapter_encoding(sig_bt[sig_index++]);
- }
+ for (int byte = 0; byte < _basic_types_per_int; byte++) {
+ int bt = ((sig_index < total_args_passed)
+ ? adapter_encoding(sig_bt[sig_index++])
+ : 0);
+ assert((bt & _basic_type_mask) == bt, "must fit in 4 bits");
+ value = (value << _basic_type_bits) | bt;
}
ptr[index] = value;
}
@@ -2235,6 +2123,7 @@
return false;
}
if (_length < 0) {
+ assert(_compact_int_count == 3, "else change next line");
return _value._compact[0] == other->_value._compact[0] &&
_value._compact[1] == other->_value._compact[1] &&
_value._compact[2] == other->_value._compact[2];
@@ -2531,13 +2420,17 @@
entry->relocate(B->content_begin());
#ifndef PRODUCT
// debugging suppport
- if (PrintAdapterHandlers) {
- tty->cr();
- tty->print_cr("i2c argument handler #%d for: %s %s (fingerprint = %s, %d bytes generated)",
+ if (PrintAdapterHandlers || PrintStubCode) {
+ entry->print_adapter_on(tty);
+ tty->print_cr("i2c argument handler #%d for: %s %s (%d bytes generated)",
_adapters->number_of_entries(), (method->is_static() ? "static" : "receiver"),
- method->signature()->as_C_string(), fingerprint->as_string(), insts_size );
+ method->signature()->as_C_string(), insts_size);
tty->print_cr("c2i argument handler starts at %p",entry->get_c2i_entry());
- Disassembler::decode(entry->get_i2c_entry(), entry->get_i2c_entry() + insts_size);
+ if (Verbose || PrintStubCode) {
+ address first_pc = entry->base_address();
+ if (first_pc != NULL)
+ Disassembler::decode(first_pc, first_pc + insts_size);
+ }
}
#endif
@@ -2561,11 +2454,25 @@
return entry;
}
+address AdapterHandlerEntry::base_address() {
+ address base = _i2c_entry;
+ if (base == NULL) base = _c2i_entry;
+ assert(base <= _c2i_entry || _c2i_entry == NULL, "");
+ assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
+ return base;
+}
+
void AdapterHandlerEntry::relocate(address new_base) {
- ptrdiff_t delta = new_base - _i2c_entry;
+ address old_base = base_address();
+ assert(old_base != NULL, "");
+ ptrdiff_t delta = new_base - old_base;
+ if (_i2c_entry != NULL)
_i2c_entry += delta;
+ if (_c2i_entry != NULL)
_c2i_entry += delta;
+ if (_c2i_unverified_entry != NULL)
_c2i_unverified_entry += delta;
+ assert(base_address() == new_base, "");
}
@@ -2614,7 +2521,9 @@
ResourceMark rm;
nmethod* nm = NULL;
- assert(method->has_native_function(), "must have something valid to call!");
+ assert(method->is_native(), "must be native");
+ assert(method->is_method_handle_intrinsic() ||
+ method->has_native_function(), "must have something valid to call!");
{
// perform the work while holding the lock, but perform any printing outside the lock
@@ -2651,9 +2560,11 @@
assert( i==total_args_passed, "" );
BasicType ret_type = ss.type();
- // Now get the compiled-Java layout as input arguments
- int comp_args_on_stack;
- comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false);
+ // Now get the compiled-Java layout as input (or output) arguments.
+ // NOTE: Stubs for compiled entry points of method handle intrinsics
+ // are just trampolines so the argument registers must be outgoing ones.
+ const bool is_outgoing = method->is_method_handle_intrinsic();
+ int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, is_outgoing);
// Generate the compiled-to-native wrapper code
nm = SharedRuntime::generate_native_wrapper(&_masm,
@@ -2939,18 +2850,22 @@
AdapterHandlerTableIterator iter(_adapters);
while (iter.has_next()) {
AdapterHandlerEntry* a = iter.next();
- if ( b == CodeCache::find_blob(a->get_i2c_entry()) ) {
+ if (b == CodeCache::find_blob(a->get_i2c_entry())) {
st->print("Adapter for signature: ");
- st->print_cr("%s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
- a->fingerprint()->as_string(),
- a->get_i2c_entry(), a->get_c2i_entry(), a->get_c2i_unverified_entry());
-
+ a->print_adapter_on(tty);
return;
}
}
assert(false, "Should have found handler");
}
+void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
+ st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
+ (intptr_t) this, fingerprint()->as_string(),
+ get_i2c_entry(), get_c2i_entry(), get_c2i_unverified_entry());
+
+}
+
#ifndef PRODUCT
void AdapterHandlerLibrary::print_statistics() {
--- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -61,7 +61,6 @@
static RuntimeStub* _resolve_static_call_blob;
static DeoptimizationBlob* _deopt_blob;
- static RicochetBlob* _ricochet_blob;
static SafepointBlob* _polling_page_safepoint_handler_blob;
static SafepointBlob* _polling_page_return_handler_blob;
@@ -187,7 +186,6 @@
static void throw_NullPointerException(JavaThread* thread);
static void throw_NullPointerException_at_call(JavaThread* thread);
static void throw_StackOverflowError(JavaThread* thread);
- static void throw_WrongMethodTypeException(JavaThread* thread, oopDesc* required, oopDesc* actual);
static address continuation_for_implicit_exception(JavaThread* thread,
address faulting_pc,
ImplicitExceptionKind exception_kind);
@@ -223,16 +221,6 @@
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; }
@@ -250,6 +238,7 @@
// To be used as the entry point for unresolved native methods.
static address native_method_throw_unsatisfied_link_error_entry();
+ static address native_method_throw_unsupported_operation_exception_entry();
// bytecode tracing is only used by the TraceBytecodes
static intptr_t trace_bytecode(JavaThread* thread, intptr_t preserve_this_value, intptr_t tos, intptr_t tos2) PRODUCT_RETURN0;
@@ -291,27 +280,6 @@
static char* generate_class_cast_message(JavaThread* thr, const char* name);
/**
- * Fill in the message for a WrongMethodTypeException
- *
- * @param thr the current thread
- * @param mtype (optional) expected method type (or argument class)
- * @param mhandle (optional) actual method handle (or argument)
- * @return the dynamically allocated exception message
- *
- * BCP for the frame on top of the stack must refer to an
- * 'invokevirtual' op for a method handle, or an 'invokedyamic' op.
- * The caller (or one of its callers) must use a ResourceMark
- * in order to correctly free the result.
- */
- static char* generate_wrong_method_type_message(JavaThread* thr,
- oopDesc* mtype = NULL,
- oopDesc* mhandle = NULL);
-
- /** Return non-null if the mtype is a klass or Class, not a MethodType. */
- static oop wrong_method_type_is_for_single_argument(JavaThread* thr,
- oopDesc* mtype);
-
- /**
* Fill in the "X cannot be cast to a Y" message for ClassCastException
*
* @param name the name of the class of the object attempted to be cast
@@ -453,6 +421,10 @@
// convention (handlizes oops, etc), transitions to native, makes the call,
// returns to java state (possibly blocking), unhandlizes any result and
// returns.
+ //
+ // The wrapper may contain special-case code if the given method
+ // is a JNI critical method, or a compiled method handle adapter,
+ // such as _invokeBasic, _linkToVirtual, etc.
static nmethod *generate_native_wrapper(MacroAssembler* masm,
methodHandle method,
int compile_id,
@@ -647,13 +619,14 @@
AdapterHandlerEntry();
public:
- address get_i2c_entry() { return _i2c_entry; }
- address get_c2i_entry() { return _c2i_entry; }
- address get_c2i_unverified_entry() { return _c2i_unverified_entry; }
+ address get_i2c_entry() const { return _i2c_entry; }
+ address get_c2i_entry() const { return _c2i_entry; }
+ address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+ address base_address();
void relocate(address new_base);
- AdapterFingerPrint* fingerprint() { return _fingerprint; }
+ AdapterFingerPrint* fingerprint() const { return _fingerprint; }
AdapterHandlerEntry* next() {
return (AdapterHandlerEntry*)BasicHashtableEntry<mtCode>::next();
@@ -665,7 +638,8 @@
bool compare_code(unsigned char* code, int length, int total_args_passed, BasicType* sig_bt);
#endif
- void print();
+ //virtual void print_on(outputStream* st) const; DO NOT USE
+ void print_adapter_on(outputStream* st) const;
};
class AdapterHandlerLibrary: public AllStatic {
--- a/hotspot/src/share/vm/runtime/signature.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/signature.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -396,6 +396,8 @@
enum FailureMode { ReturnNull, CNFException, NCDFError };
klassOop as_klass(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
oop as_java_mirror(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
+ const jbyte* raw_bytes() { return _signature->bytes() + _begin; }
+ int raw_length() { return _end - _begin; }
// return same as_symbol except allocation of new symbols is avoided.
Symbol* as_symbol_or_null();
--- a/hotspot/src/share/vm/runtime/stubRoutines.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -53,7 +53,6 @@
address StubRoutines::_throw_IncompatibleClassChangeError_entry = NULL;
address StubRoutines::_throw_NullPointerException_at_call_entry = NULL;
address StubRoutines::_throw_StackOverflowError_entry = NULL;
-address StubRoutines::_throw_WrongMethodTypeException_entry = NULL;
address StubRoutines::_handler_for_unsafe_access_entry = NULL;
jint StubRoutines::_verify_oop_count = 0;
address StubRoutines::_verify_oop_subroutine_entry = NULL;
--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -130,7 +130,6 @@
static address _throw_IncompatibleClassChangeError_entry;
static address _throw_NullPointerException_at_call_entry;
static address _throw_StackOverflowError_entry;
- static address _throw_WrongMethodTypeException_entry;
static address _handler_for_unsafe_access_entry;
static address _atomic_xchg_entry;
@@ -225,6 +224,9 @@
(_code2 != NULL && _code2->blob_contains(addr)) ;
}
+ static CodeBlob* code1() { return _code1; }
+ static CodeBlob* code2() { return _code2; }
+
// Debugging
static jint verify_oop_count() { return _verify_oop_count; }
static jint* verify_oop_count_addr() { return &_verify_oop_count; }
@@ -254,7 +256,6 @@
static address throw_IncompatibleClassChangeError_entry(){ return _throw_IncompatibleClassChangeError_entry; }
static address throw_NullPointerException_at_call_entry(){ return _throw_NullPointerException_at_call_entry; }
static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; }
- static address throw_WrongMethodTypeException_entry() { return _throw_WrongMethodTypeException_entry; }
// Exceptions during unsafe access - should throw Java exception rather
// than crash.
--- a/hotspot/src/share/vm/runtime/vframe.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/vframe.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -410,8 +410,9 @@
Klass::cast(method()->method_holder())
->is_subclass_of(SystemDictionary::reflect_MethodAccessorImpl_klass())) {
// This is an auxilary frame -- skip it
- } else if (method()->is_method_handle_adapter()) {
- // This is an internal adapter frame from the MethodHandleCompiler -- skip it
+ } else if (method()->is_method_handle_intrinsic() ||
+ method()->is_compiled_lambda_form()) {
+ // This is an internal adapter frame for method handles -- skip it
} else {
// This is non-excluded frame, we need to count it against the depth
if (depth-- <= 0) {
--- a/hotspot/src/share/vm/runtime/vframeArray.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/vframeArray.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/vmSymbols.hpp"
+#include "interpreter/bytecode.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
@@ -510,7 +511,8 @@
// in the above picture.
// Find the skeletal interpreter frames to unpack into
- RegisterMap map(JavaThread::current(), false);
+ JavaThread* THREAD = JavaThread::current();
+ RegisterMap map(THREAD, false);
// Get the youngest frame we will unpack (last to be unpacked)
frame me = unpack_frame.sender(&map);
int index;
@@ -520,29 +522,37 @@
me = me.sender(&map);
}
+ // Do the unpacking of interpreter frames; the frame at index 0 represents the top activation, so it has no callee
+ // Unpack the frames from the oldest (frames() -1) to the youngest (0)
frame caller_frame = me;
-
- // Do the unpacking of interpreter frames; the frame at index 0 represents the top activation, so it has no callee
-
- // Unpack the frames from the oldest (frames() -1) to the youngest (0)
-
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(caller_actual_parameters,
- callee_parameters,
- callee_locals,
- &caller_frame,
- index == 0,
- exec_mode);
+ vframeArrayElement* elem = element(index); // caller
+ int callee_parameters, callee_locals;
+ if (index == 0) {
+ callee_parameters = callee_locals = 0;
+ } else {
+ methodHandle caller = elem->method();
+ methodHandle callee = element(index - 1)->method();
+ Bytecode_invoke inv(caller, elem->bci());
+ // invokedynamic instructions don't have a class but obviously don't have a MemberName appendix.
+ // NOTE: Use machinery here that avoids resolving of any kind.
+ const bool has_member_arg =
+ !inv.is_invokedynamic() && MethodHandles::has_member_arg(inv.klass(), inv.name());
+ callee_parameters = callee->size_of_parameters() + (has_member_arg ? 1 : 0);
+ callee_locals = callee->max_locals();
+ }
+ elem->unpack_on_stack(caller_actual_parameters,
+ callee_parameters,
+ callee_locals,
+ &caller_frame,
+ index == 0,
+ exec_mode);
if (index == frames() - 1) {
- Deoptimization::unwind_callee_save_values(element(index)->iframe(), this);
+ Deoptimization::unwind_callee_save_values(elem->iframe(), this);
}
- caller_frame = *element(index)->iframe();
+ caller_frame = *elem->iframe();
caller_actual_parameters = callee_parameters;
}
-
-
deallocate_monitor_chunks();
}
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -836,13 +836,6 @@
/* CodeBlobs (NOTE: incomplete, but only a little) */ \
/***************************************************/ \
\
- X86_ONLY(nonstatic_field(MethodHandles::RicochetFrame, _sender_pc, address)) \
- X86_ONLY(nonstatic_field(MethodHandles::RicochetFrame, _exact_sender_sp, intptr_t*)) \
- X86_ONLY(nonstatic_field(MethodHandles::RicochetFrame, _sender_link, intptr_t*)) \
- X86_ONLY(nonstatic_field(MethodHandles::RicochetFrame, _saved_args_base, intptr_t*)) \
- \
- static_field(SharedRuntime, _ricochet_blob, RicochetBlob*) \
- \
nonstatic_field(CodeBlob, _name, const char*) \
nonstatic_field(CodeBlob, _size, int) \
nonstatic_field(CodeBlob, _header_size, int) \
@@ -887,11 +880,8 @@
nonstatic_field(nmethod, _compile_id, int) \
nonstatic_field(nmethod, _exception_cache, ExceptionCache*) \
nonstatic_field(nmethod, _marked_for_deoptimization, bool) \
- \
- nonstatic_field(RicochetBlob, _bounce_offset, int) \
- nonstatic_field(RicochetBlob, _exception_offset, int) \
- \
- unchecked_c2_static_field(Deoptimization, _trap_reason_name, void*) \
+ \
+ unchecked_c2_static_field(Deoptimization, _trap_reason_name, void*) \
\
/********************************/ \
/* JavaCalls (NOTE: incomplete) */ \
@@ -1633,7 +1623,6 @@
/*************************************************************/ \
\
declare_toplevel_type(SharedRuntime) \
- X86_ONLY(declare_toplevel_type(MethodHandles::RicochetFrame)) \
\
declare_toplevel_type(CodeBlob) \
declare_type(BufferBlob, CodeBlob) \
@@ -1644,7 +1633,6 @@
declare_type(SingletonBlob, CodeBlob) \
declare_type(SafepointBlob, SingletonBlob) \
declare_type(DeoptimizationBlob, SingletonBlob) \
- declare_type(RicochetBlob, SingletonBlob) \
declare_c2_type(ExceptionBlob, SingletonBlob) \
declare_c2_type(UncommonTrapBlob, CodeBlob) \
\
@@ -2347,7 +2335,7 @@
declare_constant(instanceKlass::initialization_error) \
\
/*********************************/ \
- /* Symbol* - symbol max length */ \
+ /* Symbol* - symbol max length */ \
/*********************************/ \
\
declare_constant(Symbol::max_symbol_length) \
@@ -2360,21 +2348,16 @@
declare_constant(constantPoolOopDesc::_indy_argc_offset) \
declare_constant(constantPoolOopDesc::_indy_argv_offset) \
\
- /*********************************************/ \
- /* ConstantPoolCacheEntry FlagBitValues enum */ \
- /*********************************************/ \
+ /********************************/ \
+ /* ConstantPoolCacheEntry enums */ \
+ /********************************/ \
\
- declare_constant(ConstantPoolCacheEntry::hotSwapBit) \
- declare_constant(ConstantPoolCacheEntry::methodInterface) \
- declare_constant(ConstantPoolCacheEntry::volatileField) \
- declare_constant(ConstantPoolCacheEntry::vfinalMethod) \
- declare_constant(ConstantPoolCacheEntry::finalField) \
- \
- /******************************************/ \
- /* ConstantPoolCacheEntry FlagValues enum */ \
- /******************************************/ \
- \
- declare_constant(ConstantPoolCacheEntry::tosBits) \
+ declare_constant(ConstantPoolCacheEntry::is_volatile_shift) \
+ declare_constant(ConstantPoolCacheEntry::is_final_shift) \
+ declare_constant(ConstantPoolCacheEntry::is_forced_virtual_shift) \
+ declare_constant(ConstantPoolCacheEntry::is_vfinal_shift) \
+ declare_constant(ConstantPoolCacheEntry::is_field_entry_shift) \
+ declare_constant(ConstantPoolCacheEntry::tos_state_shift) \
\
/***************************************/ \
/* java_lang_Thread::ThreadStatus enum */ \
--- a/hotspot/src/share/vm/services/heapDumper.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/services/heapDumper.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -1650,9 +1650,6 @@
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/utilities/accessFlags.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/utilities/accessFlags.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -55,9 +55,6 @@
JVM_ACC_IS_OBSOLETE = 0x00020000, // RedefineClasses() has made method obsolete
JVM_ACC_IS_PREFIXED_NATIVE = 0x00040000, // JVMTI has prefixed this native method
- JVM_MH_INVOKE_BITS // = 0x10001100 // MethodHandle.invoke quasi-native
- = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_MONITOR_MATCH),
-
// klassOop flags
JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable
JVM_ACC_HAS_VANILLA_CONSTRUCTOR = 0x20000000, // True if klass has a vanilla default constructor
@@ -133,15 +130,6 @@
bool is_obsolete () const { return (_flags & JVM_ACC_IS_OBSOLETE ) != 0; }
bool is_prefixed_native () const { return (_flags & JVM_ACC_IS_PREFIXED_NATIVE ) != 0; }
- // JSR 292: A method of the form MethodHandle.invoke(A...)R method is
- // neither bytecoded nor a JNI native, but rather a fast call through
- // a lightweight method handle object. Because it is not bytecoded,
- // it has the native bit set, but the monitor-match bit is also set
- // to distinguish it from a JNI native (which never has the match bit set).
- // The synthetic bit is also present, because such a method is never
- // explicitly defined in Java code.
- bool is_method_handle_invoke () const { return (_flags & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS; }
-
// klassOop flags
bool has_miranda_methods () const { return (_flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; }
bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; }
--- a/hotspot/src/share/vm/utilities/debug.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/utilities/debug.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -91,6 +91,13 @@
# endif
#endif // PRODUCT
+FormatBufferResource::FormatBufferResource(const char * format, ...)
+ : FormatBufferBase((char*)resource_allocate_bytes(RES_BUFSZ)) {
+ va_list argp;
+ va_start(argp, format);
+ jio_vsnprintf(_buf, RES_BUFSZ, format, argp);
+ va_end(argp);
+}
void warning(const char* format, ...) {
if (PrintWarnings) {
--- a/hotspot/src/share/vm/utilities/debug.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/utilities/debug.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -31,29 +31,43 @@
#include <stdarg.h>
// Simple class to format the ctor arguments into a fixed-sized buffer.
+class FormatBufferBase {
+ protected:
+ char* _buf;
+ inline FormatBufferBase(char* buf) : _buf(buf) {}
+ public:
+ operator const char *() const { return _buf; }
+};
+
+// Use resource area for buffer
+#define RES_BUFSZ 256
+class FormatBufferResource : public FormatBufferBase {
+ public:
+ FormatBufferResource(const char * format, ...);
+};
+
+// Use stack for buffer
template <size_t bufsz = 256>
-class FormatBuffer {
+class FormatBuffer : public FormatBufferBase {
public:
inline FormatBuffer(const char * format, ...);
inline void append(const char* format, ...);
inline void print(const char* format, ...);
inline void printv(const char* format, va_list ap);
- operator const char *() const { return _buf; }
char* buffer() { return _buf; }
int size() { return bufsz; }
private:
FormatBuffer(const FormatBuffer &); // prevent copies
+ char _buffer[bufsz];
protected:
- char _buf[bufsz];
-
inline FormatBuffer();
};
template <size_t bufsz>
-FormatBuffer<bufsz>::FormatBuffer(const char * format, ...) {
+FormatBuffer<bufsz>::FormatBuffer(const char * format, ...) : FormatBufferBase(_buffer) {
va_list argp;
va_start(argp, format);
jio_vsnprintf(_buf, bufsz, format, argp);
@@ -61,7 +75,7 @@
}
template <size_t bufsz>
-FormatBuffer<bufsz>::FormatBuffer() {
+FormatBuffer<bufsz>::FormatBuffer() : FormatBufferBase(_buffer) {
_buf[0] = '\0';
}
@@ -93,6 +107,7 @@
// Used to format messages for assert(), guarantee(), fatal(), etc.
typedef FormatBuffer<> err_msg;
+typedef FormatBufferResource err_msg_res;
// assertions
#ifdef ASSERT
--- a/hotspot/src/share/vm/utilities/exceptions.cpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/utilities/exceptions.cpp Mon Aug 27 10:58:40 2012 -0700
@@ -164,52 +164,58 @@
}
-void Exceptions::_throw_msg(Thread* thread, const char* file, int line, Symbol* h_name, const char* message, Handle h_loader, Handle h_protection_domain) {
+void Exceptions::_throw_msg(Thread* thread, const char* file, int line, Symbol* name, const char* message,
+ Handle h_loader, Handle h_protection_domain) {
// Check for special boot-strapping/vm-thread handling
- if (special_exception(thread, file, line, h_name, message)) return;
+ if (special_exception(thread, file, line, name, message)) return;
// Create and throw exception
Handle h_cause(thread, NULL);
- Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain);
+ Handle h_exception = new_exception(thread, name, message, h_cause, h_loader, h_protection_domain);
_throw(thread, file, line, h_exception, message);
}
-// Throw an exception with a message and a cause
-void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, Symbol* h_name, const char* message, Handle h_cause, Handle h_loader, Handle h_protection_domain) {
+void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, Symbol* name, const char* message, Handle h_cause,
+ Handle h_loader, Handle h_protection_domain) {
// Check for special boot-strapping/vm-thread handling
- if (special_exception(thread, file, line, h_name, message)) return;
+ if (special_exception(thread, file, line, name, message)) return;
// Create and throw exception and init cause
- Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain);
+ Handle h_exception = new_exception(thread, name, message, h_cause, h_loader, h_protection_domain);
_throw(thread, file, line, h_exception, message);
}
-// This version already has a handle for name
-void Exceptions::_throw_msg(Thread* thread, const char* file, int line,
- Symbol* name, const char* message) {
- Handle h_loader(thread, NULL);
- Handle h_protection_domain(thread, NULL);
- Exceptions::_throw_msg(thread, file, line, name, message, h_loader, h_protection_domain);
+void Exceptions::_throw_cause(Thread* thread, const char* file, int line, Symbol* name, Handle h_cause,
+ Handle h_loader, Handle h_protection_domain) {
+ // Check for special boot-strapping/vm-thread handling
+ if (special_exception(thread, file, line, h_cause)) return;
+ // Create and throw exception
+ Handle h_exception = new_exception(thread, name, h_cause, h_loader, h_protection_domain);
+ _throw(thread, file, line, h_exception, NULL);
}
-// This version already has a handle for name
-void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line,
- Symbol* name, const char* message, Handle cause) {
- Handle h_loader(thread, NULL);
- Handle h_protection_domain(thread, NULL);
- Exceptions::_throw_msg_cause(thread, file, line, name, message, cause, h_loader, h_protection_domain);
-}
-
-void Exceptions::_throw_args(Thread* thread, const char* file, int line, Symbol* h_name, Symbol* h_signature, JavaCallArguments *args) {
+void Exceptions::_throw_args(Thread* thread, const char* file, int line, Symbol* name, Symbol* signature, JavaCallArguments *args) {
// Check for special boot-strapping/vm-thread handling
- if (special_exception(thread, file, line, h_name, NULL)) return;
+ if (special_exception(thread, file, line, name, NULL)) return;
// Create and throw exception
Handle h_loader(thread, NULL);
Handle h_prot(thread, NULL);
- Handle h_cause(thread, NULL);
- Handle exception = new_exception(thread, h_name, h_signature, args, h_cause, h_loader, h_prot);
+ Handle exception = new_exception(thread, name, signature, args, h_loader, h_prot);
_throw(thread, file, line, exception);
}
+// Methods for default parameters.
+// NOTE: These must be here (and not in the header file) because of include circularities.
+void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, Symbol* name, const char* message, Handle h_cause) {
+ _throw_msg_cause(thread, file, line, name, message, h_cause, Handle(thread, NULL), Handle(thread, NULL));
+}
+void Exceptions::_throw_msg(Thread* thread, const char* file, int line, Symbol* name, const char* message) {
+ _throw_msg(thread, file, line, name, message, Handle(thread, NULL), Handle(thread, NULL));
+}
+void Exceptions::_throw_cause(Thread* thread, const char* file, int line, Symbol* name, Handle h_cause) {
+ _throw_cause(thread, file, line, name, h_cause, Handle(thread, NULL), Handle(thread, NULL));
+}
+
+
void Exceptions::throw_stack_overflow_exception(Thread* THREAD, const char* file, int line, methodHandle method) {
Handle exception;
if (!THREAD->has_pending_exception()) {
@@ -240,12 +246,9 @@
// Creates an exception oop, calls the <init> method with the given signature.
// and returns a Handle
-// Initializes the cause if cause non-null
-Handle Exceptions::new_exception(Thread *thread, Symbol* h_name,
- Symbol* signature,
- JavaCallArguments *args,
- Handle h_cause, Handle h_loader,
- Handle h_protection_domain) {
+Handle Exceptions::new_exception(Thread *thread, Symbol* name,
+ Symbol* signature, JavaCallArguments *args,
+ Handle h_loader, Handle h_protection_domain) {
assert(Universe::is_fully_initialized(),
"cannot be called during initialization");
assert(thread->is_Java_thread(), "can only be called by a Java thread");
@@ -254,8 +257,8 @@
Handle h_exception;
// Resolve exception klass
- klassOop ik = SystemDictionary::resolve_or_fail(h_name, h_loader, h_protection_domain, true, thread);
- instanceKlassHandle klass (thread, ik);
+ klassOop ik = SystemDictionary::resolve_or_fail(name, h_loader, h_protection_domain, true, thread);
+ instanceKlassHandle klass(thread, ik);
if (!thread->has_pending_exception()) {
assert(klass.not_null(), "klass must exist");
@@ -273,24 +276,8 @@
signature,
args,
thread);
-
}
}
-
- // Future: object initializer should take a cause argument
- if (h_cause() != NULL) {
- assert(h_cause->is_a(SystemDictionary::Throwable_klass()),
- "exception cause is not a subclass of java/lang/Throwable");
- JavaValue result1(T_OBJECT);
- JavaCallArguments args1;
- args1.set_receiver(h_exception);
- args1.push_oop(h_cause);
- JavaCalls::call_virtual(&result1, klass,
- vmSymbols::initCause_name(),
- vmSymbols::throwable_throwable_signature(),
- &args1,
- thread);
- }
}
// Check if another exception was thrown in the process, if so rethrow that one
@@ -301,12 +288,60 @@
return h_exception;
}
+// Creates an exception oop, calls the <init> method with the given signature.
+// and returns a Handle
+// Initializes the cause if cause non-null
+Handle Exceptions::new_exception(Thread *thread, Symbol* name,
+ Symbol* signature, JavaCallArguments *args,
+ Handle h_cause,
+ Handle h_loader, Handle h_protection_domain) {
+ Handle h_exception = new_exception(thread, name, signature, args, h_loader, h_protection_domain);
+
+ // Future: object initializer should take a cause argument
+ if (h_cause.not_null()) {
+ assert(h_cause->is_a(SystemDictionary::Throwable_klass()),
+ "exception cause is not a subclass of java/lang/Throwable");
+ JavaValue result1(T_OBJECT);
+ JavaCallArguments args1;
+ args1.set_receiver(h_exception);
+ args1.push_oop(h_cause);
+ JavaCalls::call_virtual(&result1, h_exception->klass(),
+ vmSymbols::initCause_name(),
+ vmSymbols::throwable_throwable_signature(),
+ &args1,
+ thread);
+ }
+
+ // Check if another exception was thrown in the process, if so rethrow that one
+ if (thread->has_pending_exception()) {
+ h_exception = Handle(thread, thread->pending_exception());
+ thread->clear_pending_exception();
+ }
+ return h_exception;
+}
+
+// Convenience method. Calls either the <init>() or <init>(Throwable) method when
+// creating a new exception
+Handle Exceptions::new_exception(Thread* thread, Symbol* name,
+ Handle h_cause,
+ Handle h_loader, Handle h_protection_domain,
+ ExceptionMsgToUtf8Mode to_utf8_safe) {
+ JavaCallArguments args;
+ Symbol* signature = NULL;
+ if (h_cause.is_null()) {
+ signature = vmSymbols::void_method_signature();
+ } else {
+ signature = vmSymbols::throwable_void_signature();
+ args.push_oop(h_cause);
+ }
+ return new_exception(thread, name, signature, &args, h_loader, h_protection_domain);
+}
+
// Convenience method. Calls either the <init>() or <init>(String) method when
// creating a new exception
-Handle Exceptions::new_exception(Thread* thread, Symbol* h_name,
+Handle Exceptions::new_exception(Thread* thread, Symbol* name,
const char* message, Handle h_cause,
- Handle h_loader,
- Handle h_protection_domain,
+ Handle h_loader, Handle h_protection_domain,
ExceptionMsgToUtf8Mode to_utf8_safe) {
JavaCallArguments args;
Symbol* signature = NULL;
@@ -320,7 +355,7 @@
// the exception we are trying to build, or the pending exception.
// This is sort of like what PRESERVE_EXCEPTION_MARK does, except
// for the preferencing and the early returns.
- Handle incoming_exception (thread, NULL);
+ Handle incoming_exception(thread, NULL);
if (thread->has_pending_exception()) {
incoming_exception = Handle(thread, thread->pending_exception());
thread->clear_pending_exception();
@@ -344,7 +379,7 @@
args.push_oop(msg);
signature = vmSymbols::string_void_signature();
}
- return new_exception(thread, h_name, signature, &args, h_cause, h_loader, h_protection_domain);
+ return new_exception(thread, name, signature, &args, h_cause, h_loader, h_protection_domain);
}
// Another convenience method that creates handles for null class loaders and
@@ -355,8 +390,7 @@
// encoding scheme of the string into account. One thing we should do at some
// point is to push this flag down to class java_lang_String since other
// classes may need similar functionalities.
-Handle Exceptions::new_exception(Thread* thread,
- Symbol* name,
+Handle Exceptions::new_exception(Thread* thread, Symbol* name,
const char* message,
ExceptionMsgToUtf8Mode to_utf8_safe) {
--- a/hotspot/src/share/vm/utilities/exceptions.hpp Mon Aug 27 10:23:43 2012 +0800
+++ b/hotspot/src/share/vm/utilities/exceptions.hpp Mon Aug 27 10:58:40 2012 -0700
@@ -112,19 +112,22 @@
// Throw exceptions: w/o message, w/ message & with formatted message.
static void _throw_oop(Thread* thread, const char* file, int line, oop exception);
static void _throw(Thread* thread, const char* file, int line, Handle exception, const char* msg = NULL);
- static void _throw_msg(Thread* thread, const char* file, int line,
- Symbol* name, const char* message, Handle loader,
- Handle protection_domain);
- static void _throw_msg(Thread* thread, const char* file, int line,
- Symbol* name, const char* message);
+
+ static void _throw_msg(Thread* thread, const char* file, int line, Symbol* name, const char* message);
+ static void _throw_msg(Thread* thread, const char* file, int line, Symbol* name, const char* message,
+ Handle loader, Handle protection_domain);
+
+ static void _throw_msg_cause(Thread* thread, const char* file, int line, Symbol* name, const char* message, Handle h_cause);
+ static void _throw_msg_cause(Thread* thread, const char* file, int line, Symbol* name, const char* message, Handle h_cause,
+ Handle h_loader, Handle h_protection_domain);
+
+ static void _throw_cause(Thread* thread, const char* file, int line, Symbol* name, Handle h_cause);
+ static void _throw_cause(Thread* thread, const char* file, int line, Symbol* name, Handle h_cause,
+ Handle h_loader, Handle h_protection_domain);
+
static void _throw_args(Thread* thread, const char* file, int line,
Symbol* name, Symbol* signature,
JavaCallArguments* args);
- static void _throw_msg_cause(Thread* thread, const char* file,
- int line, Symbol* h_name, const char* message,
- Handle h_cause, Handle h_loader, Handle h_protection_domain);
- static void _throw_msg_cause(Thread* thread, const char* file, int line,
- Symbol* name, const char* message, Handle cause);
// There is no THROW... macro for this method. Caller should remember
// to do a return after calling it.
@@ -134,17 +137,26 @@
// Create and initialize a new exception
static Handle new_exception(Thread* thread, Symbol* name,
Symbol* signature, JavaCallArguments* args,
- Handle cause, Handle loader,
- Handle protection_domain);
+ Handle loader, Handle protection_domain);
+
+ static Handle new_exception(Thread* thread, Symbol* name,
+ Symbol* signature, JavaCallArguments* args,
+ Handle cause,
+ Handle loader, Handle protection_domain);
static Handle new_exception(Thread* thread, Symbol* name,
- const char* message, Handle cause, Handle loader,
- Handle protection_domain,
+ Handle cause,
+ Handle loader, Handle protection_domain,
ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8);
- static Handle new_exception(Thread* thread, Symbol* name,
- const char* message,
- ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8);
+ static Handle new_exception(Thread* thread, Symbol* name,
+ const char* message, Handle cause,
+ Handle loader, Handle protection_domain,
+ ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8);
+
+ static Handle new_exception(Thread* thread, Symbol* name,
+ const char* message,
+ ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8);
static void throw_stack_overflow_exception(Thread* thread, const char* file, int line, methodHandle method);
@@ -214,6 +226,9 @@
#define THROW_MSG(name, message) \
{ Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message); return; }
+#define THROW_CAUSE(name, cause) \
+ { Exceptions::_throw_cause(THREAD_AND_LOCATION, name, cause); return; }
+
#define THROW_MSG_LOADER(name, message, loader, protection_domain) \
{ Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message, loader, protection_domain); return; }
@@ -238,6 +253,9 @@
#define THROW_ARG_(name, signature, args, result) \
{ Exceptions::_throw_args(THREAD_AND_LOCATION, name, signature, args); return result; }
+#define THROW_MSG_CAUSE(name, message, cause) \
+ { Exceptions::_throw_msg_cause(THREAD_AND_LOCATION, name, message, cause); return; }
+
#define THROW_MSG_CAUSE_(name, message, cause, result) \
{ Exceptions::_throw_msg_cause(THREAD_AND_LOCATION, name, message, cause); return result; }
--- a/jaxp/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/jaxp/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
1c88da9a1365797e49be77ae42c34bbc0a3c3f0c jdk8-b48
f81e981eca7b63316cf9d778f93903a4fc62161d jdk8-b49
2791ec55f66b57a702349c649567a391e6301f4e jdk8-b50
+dc1ea77ed9d9746e0f98bb1268987c3596c8b4b5 jdk8-b51
+bd3c00d5761408954cc29ffb82016a76cbc90b43 jdk8-b52
--- a/jaxws/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/jaxws/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
efb564de8a8ee397a65fab77d45cb20200f6ddd8 jdk8-b48
b48865af8ac559ba6f60fb86fa3fe0ebdd22746c jdk8-b49
bdab72e87b83bcccf3abe6eaaa4cdc7b1cd2d92b jdk8-b50
+1a70b6333ebe12e1c6dbca30e58bc1ba894ab898 jdk8-b51
+f62bc618122e87a8bea69865cc02074e9d850426 jdk8-b52
--- a/jdk/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -173,3 +173,5 @@
51707c3b75c0f521794d9ab425f4e5b2351c70c1 jdk8-b49
e4bae5c53fca8fcb9393d47fd36a34b9e2e8d4ec jdk8-b50
e865efbc71059a414b3b2dd2e0adfcb3d2ab6ff9 jdk8-b51
+e8569a473cee7f4955bd9e76a9bdf6c6a07ced27 jdk8-b52
+2c6933c5106b81a8578b70996fe5b735fb3adb60 jdk8-b53
--- a/jdk/src/share/classes/java/lang/ClassValue.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/ClassValue.java Mon Aug 27 10:58:40 2012 -0700
@@ -489,9 +489,18 @@
/** Remove an entry. */
synchronized
void removeEntry(ClassValue<?> classValue) {
- // make all cache elements for this guy go stale:
- if (remove(classValue.identity) != null) {
+ Entry<?> e = remove(classValue.identity);
+ if (e == null) {
+ // Uninitialized, and no pending calls to computeValue. No change.
+ } else if (e.isPromise()) {
+ // State is uninitialized, with a pending call to finishEntry.
+ // Since remove is a no-op in such a state, keep the promise
+ // by putting it back into the map.
+ put(classValue.identity, e);
+ } else {
+ // In an initialized state. Bump forward, and de-initialize.
classValue.bumpVersion();
+ // Make all cache elements for this guy go stale.
removeStaleEntries(classValue);
}
}
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1204 +0,0 @@
-/*
- * 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
- * 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.
- */
-
-package java.lang.invoke;
-
-import sun.invoke.util.VerifyType;
-import sun.invoke.util.Wrapper;
-import sun.invoke.util.ValueConversions;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Collections;
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-
-/**
- * This method handle performs simple conversion or checking of a single argument.
- * @author jrose
- */
-class AdapterMethodHandle extends BoundMethodHandle {
-
- //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH
- //Object argument; // parameter to the conversion if needed
- //int vmargslot; // which argument slot is affected
- private final int conversion; // the type of conversion: RETYPE_ONLY, etc.
-
- // Constructors in this class *must* be package scoped or private.
- private AdapterMethodHandle(MethodHandle target, MethodType newType,
- long conv, Object convArg) {
- super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv)));
- this.conversion = convCode(conv);
- // JVM might update VM-specific bits of conversion (ignore)
- MethodHandleNatives.init(this, target, convArgPos(conv));
- }
- AdapterMethodHandle(MethodHandle target, MethodType newType,
- long conv) {
- 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 fixed-arity MethodHandle.asType?
- */
- 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.
- Class<?> exp = newType.returnType();
- Class<?> ret = oldType.returnType();
- 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, level))
- return false;
- }
-
- return true;
- }
-
- /** Can a JVM-level adapter directly implement the proposed
- * argument conversion, as if by fixed-arity MethodHandle.asType?
- */
- 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);
- else
- return canBoxArgument(src, dst);
- } else {
- if (dst.isPrimitive())
- return canUnboxArgument(src, dst, level);
- else
- return true; // any two refs can be interconverted
- }
- }
-
- /**
- * 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.
- * 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, int level) {
- MethodType oldType = target.type();
- if (newType == oldType) return target;
-
- if (!canPairwiseConvert(newType, oldType, level))
- return null;
- // (after this point, it is an assertion error to fail to convert)
-
- // Find last non-trivial conversion (if any).
- int lastConv = newType.parameterCount()-1;
- while (lastConv >= 0) {
- Class<?> src = newType.parameterType(lastConv); // source type
- Class<?> dst = oldType.parameterType(lastConv); // destination type
- if (isTrivialConversion(src, dst, level)) {
- --lastConv;
- } else {
- break;
- }
- }
-
- Class<?> needReturn = newType.returnType();
- Class<?> haveReturn = oldType.returnType();
- boolean retConv = !isTrivialConversion(haveReturn, needReturn, level);
-
- // Now build a chain of one or more adapters.
- 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
- if (isTrivialConversion(src, dst, level)) {
- // do nothing: difference is trivial
- continue;
- }
- // Work the current type backward toward the desired caller type:
- midType = midType.changeParameterType(i, src);
- if (i == lastConv) {
- // When doing the last (or only) real conversion,
- // force all remaining null conversions to happen also.
- 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()) {
- adapter2 = makePrimCast(midType, adapter, i, dst);
- } else {
- adapter2 = makeBoxArgument(midType, adapter, i, src);
- }
- } else {
- if (dst.isPrimitive()) {
- // Caller has boxed a primitive. Unbox it for the target.
- // The box type must correspond exactly to the primitive type.
- // This is simpler than the powerful set of widening
- // 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.
- 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.
- adapter2 = makeCheckCast(midType, adapter, i, dst);
- }
- }
- 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.
- adapter2 = makeRetypeOnly(newType, adapter);
- assert(adapter2 != null);
- adapter = adapter2;
- // Actually, that's because there were no non-trivial ones:
- assert(lastConv == -1 || retConv);
- }
- assert(adapter.type() == newType);
- return adapter;
- }
-
- private static boolean isTrivialConversion(Class<?> src, Class<?> dst, int level) {
- if (src == dst || dst == void.class) return true;
- if (!VerifyType.isNullConversion(src, dst)) return false;
- if (level > 1) return true; // explicitCastArguments
- boolean sp = src.isPrimitive();
- boolean dp = dst.isPrimitive();
- if (sp != dp) return false;
- if (sp) {
- // in addition to being a null conversion, forbid boolean->int etc.
- return Wrapper.forPrimitiveType(dst)
- .isConvertibleFrom(Wrapper.forPrimitiveType(src));
- } else {
- return dst.isAssignableFrom(src);
- }
- }
-
- 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);
- }
- return makeCollectArguments(adjustReturn, target, 0, false);
- }
-
- /**
- * Create a JVM-level adapter method handle to permute the arguments
- * of the given method.
- * @param newType required call type
- * @param target original method handle
- * @param argumentMap for each target argument, position of its source in newType
- * @return an adapter to the original handle with the desired new type,
- * or the original target if the types are already identical
- * and the permutation is null
- * @throws IllegalArgumentException if the adaptation cannot be made
- * directly by a JVM-level adapter, without help from Java code
- */
- static MethodHandle makePermutation(MethodType newType, MethodHandle target,
- int[] argumentMap) {
- MethodType oldType = target.type();
- boolean nullPermutation = true;
- for (int i = 0; i < argumentMap.length; i++) {
- int pos = argumentMap[i];
- if (pos != i)
- nullPermutation = false;
- if (pos < 0 || pos >= newType.parameterCount()) {
- argumentMap = new int[0]; break;
- }
- }
- if (argumentMap.length != oldType.parameterCount())
- throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
- if (nullPermutation) {
- MethodHandle res = makePairwiseConvert(newType, target, 0);
- // well, that was easy
- if (res == null)
- throw newIllegalArgumentException("cannot convert pairwise: "+newType);
- return res;
- }
-
- // Check return type. (Not much can be done with it.)
- Class<?> exp = newType.returnType();
- Class<?> ret = oldType.returnType();
- if (!VerifyType.isNullConversion(ret, exp))
- throw newIllegalArgumentException("bad return conversion for "+newType);
-
- // See if the argument types match up.
- for (int i = 0; i < argumentMap.length; i++) {
- int j = argumentMap[i];
- Class<?> src = newType.parameterType(j);
- Class<?> dst = oldType.parameterType(i);
- if (!VerifyType.isNullConversion(src, dst))
- throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType);
- }
-
- // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters.
- // A workable greedy algorithm is as follows:
- // Drop unused outgoing arguments (right to left: shallowest first).
- // Duplicate doubly-used outgoing arguments (left to right: deepest first).
- // Then the remaining problem is a true argument permutation.
- // Marshal the outgoing arguments as required from left to right.
- // That is, find the deepest outgoing stack position that does not yet
- // have the correct argument value, and correct at least that position
- // by swapping or rotating in the misplaced value (from a shallower place).
- // If the misplaced value is followed by one or more consecutive values
- // (also misplaced) issue a rotation which brings as many as possible
- // into position. Otherwise make progress with either a swap or a
- // rotation. Prefer the swap as cheaper, but do not use it if it
- // breaks a slot pair. Prefer the rotation over the swap if it would
- // preserve more consecutive values shallower than the target position.
- // When more than one rotation will work (because the required value
- // is already adjacent to the target position), then use a rotation
- // which moves the old value in the target position adjacent to
- // one of its consecutive values. Also, prefer shorter rotation
- // spans, since they use fewer memory cycles for shuffling.
-
- throw new UnsupportedOperationException("NYI");
- }
-
- private static byte basicType(Class<?> type) {
- if (type == null) return T_VOID;
- switch (Wrapper.forBasicType(type)) {
- case BOOLEAN: return T_BOOLEAN;
- case CHAR: return T_CHAR;
- case FLOAT: return T_FLOAT;
- case DOUBLE: return T_DOUBLE;
- case BYTE: return T_BYTE;
- case SHORT: return T_SHORT;
- case INT: return T_INT;
- case LONG: return T_LONG;
- case OBJECT: return T_OBJECT;
- case VOID: return T_VOID;
- }
- return 99; // T_ILLEGAL or some such
- }
-
- /** Number of stack slots for the given type.
- * Two for T_DOUBLE and T_FLOAT, one for the rest.
- */
- private static int type2size(int type) {
- assert(type >= T_BOOLEAN && type <= T_OBJECT);
- return (type == T_LONG || type == T_DOUBLE) ? 2 : 1;
- }
- private static int type2size(Class<?> type) {
- return type2size(basicType(type));
- }
-
- /** The given stackMove is the number of slots pushed.
- * It might be negative. Scale it (multiply) by the
- * VM's notion of how an address changes with a push,
- * to get the raw SP change for stackMove.
- * Then shift and mask it into the correct field.
- */
- private static long insertStackMove(int stackMove) {
- // following variable must be long to avoid sign extension after '<<'
- long spChange = stackMove * MethodHandleNatives.JVM_STACK_MOVE_UNIT;
- 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. */
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeConv(int convOp, int argnum, int src, int dest) {
- 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 |
- (int) src << CONV_SRC_TYPE_SHIFT |
- (int) dest << CONV_DEST_TYPE_SHIFT |
- insertStackMove(stackMove)
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- 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;
- return ((long) argnum << 32 |
- (long) convOp << CONV_OP_SHIFT |
- (int) src << CONV_SRC_TYPE_SHIFT |
- (int) dest << CONV_DEST_TYPE_SHIFT |
- insertStackMove(stackMove)
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeSwapConv(int convOp, int srcArg, byte srcType, int destSlot, byte destType) {
- // 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) srcType << CONV_SRC_TYPE_SHIFT |
- (int) destType << CONV_DEST_TYPE_SHIFT |
- (int) destSlot << CONV_VMINFO_SHIFT
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- 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)
- );
- }
- 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
- }
- private static int convCode(long conv) {
- return (int)conv;
- }
- private static int convArgPos(long conv) {
- return (int)(conv >>> 32);
- }
- private static boolean convOpSupported(int convOp) {
- assert(convOp >= 0 && convOp <= CONV_OP_LIMIT);
- return ((1<<convOp) & MethodHandleNatives.CONV_OP_IMPLEMENTED_MASK) != 0;
- }
-
- /** One of OP_RETYPE_ONLY, etc. */
- int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; }
-
- /* Return one plus the position of the first non-trivial difference
- * between the given types. This is not a symmetric operation;
- * we are considering adapting the targetType to adapterType.
- * Trivial differences are those which could be ignored by the JVM
- * without subverting the verifier. Otherwise, adaptable differences
- * are ones for which we could create an adapter to make the type change.
- * Return zero if there are no differences (other than trivial ones).
- * Return 1+N if N is the only adaptable argument difference.
- * Return the -2-N where N is the first of several adaptable
- * argument differences.
- * Return -1 if there there are differences which are not adaptable.
- */
- private static int diffTypes(MethodType adapterType,
- MethodType targetType,
- boolean raw) {
- int diff;
- diff = diffReturnTypes(adapterType, targetType, raw);
- if (diff != 0) return diff;
- int nargs = adapterType.parameterCount();
- if (nargs != targetType.parameterCount())
- return -1;
- diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
- //System.out.println("diff "+adapterType);
- //System.out.println(" "+diff+" "+targetType);
- return diff;
- }
- private static int diffReturnTypes(MethodType adapterType,
- MethodType targetType,
- boolean raw) {
- Class<?> src = targetType.returnType();
- Class<?> dst = adapterType.returnType();
- if ((!raw
- ? VerifyType.canPassUnchecked(src, dst)
- : VerifyType.canPassRaw(src, dst)
- ) > 0)
- return 0; // no significant difference
- if (raw && !src.isPrimitive() && !dst.isPrimitive())
- return 0; // can force a reference return (very carefully!)
- //if (false) return 1; // never adaptable!
- return -1; // some significant difference
- }
- private static int diffParamTypes(MethodType adapterType, int astart,
- MethodType targetType, int tstart,
- int nargs, boolean raw) {
- assert(nargs >= 0);
- int res = 0;
- for (int i = 0; i < nargs; i++) {
- Class<?> src = adapterType.parameterType(astart+i);
- Class<?> dest = targetType.parameterType(tstart+i);
- if ((!raw
- ? VerifyType.canPassUnchecked(src, dest)
- : VerifyType.canPassRaw(src, dest)
- ) <= 0) {
- // found a difference; is it the only one so far?
- if (res != 0)
- return -1-res; // return -2-i for prev. i
- res = 1+i;
- }
- }
- return res;
- }
-
- /** Can a retyping adapter (alone) validly convert the target to newType? */
- static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
- return canRetype(newType, targetType, false);
- }
- /** Can a retyping adapter (alone) convert the target to newType?
- * It is allowed to widen subword types and void to int, to make bitwise
- * conversions between float/int and double/long, and to perform unchecked
- * reference conversions on return. This last feature requires that the
- * caller be trusted, and perform explicit cast conversions on return values.
- */
- static boolean canRetypeRaw(MethodType newType, MethodType targetType) {
- return canRetype(newType, targetType, true);
- }
- static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
- if (!convOpSupported(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)) return false;
- int diff = diffTypes(newType, targetType, raw);
- // %%% This assert is too strong. Factor diff into VerifyType and reconcile.
- assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType));
- return diff == 0;
- }
-
- /** Factory method: Performs no conversions; simply retypes the adapter.
- * Allows unchecked argument conversions pairwise, if they are safe.
- * Returns null if not possible.
- */
- static MethodHandle makeRetypeOnly(MethodType newType, MethodHandle target) {
- return makeRetype(newType, target, false);
- }
- static MethodHandle makeRetypeRaw(MethodType newType, MethodHandle target) {
- return makeRetype(newType, target, true);
- }
- static MethodHandle makeRetype(MethodType newType, MethodHandle target, boolean raw) {
- MethodType oldType = target.type();
- if (oldType == newType) return target;
- if (!canRetype(newType, oldType, raw))
- return null;
- // TO DO: clone the target guy, whatever he is, with new type.
- return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
- }
-
- static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
- MethodType type = target.type();
- int last = type.parameterCount() - 1;
- if (type.parameterType(last) != arrayType)
- target = target.asType(type.changeParameterType(last, arrayType));
- target = target.asFixedArity(); // make sure this attribute is turned off
- return new AsVarargsCollector(target, arrayType);
- }
-
- static class AsVarargsCollector extends AdapterMethodHandle {
- final MethodHandle target;
- final Class<?> arrayType;
- MethodHandle cache;
-
- AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
- super(target, target.type(), makeConv(OP_RETYPE_ONLY));
- this.target = target;
- this.arrayType = arrayType;
- this.cache = target.asCollector(arrayType, 0);
- }
-
- @Override
- public boolean isVarargsCollector() {
- return true;
- }
-
- @Override
- public MethodHandle asFixedArity() {
- return target;
- }
-
- @Override
- public MethodHandle asType(MethodType newType) {
- MethodType type = this.type();
- int collectArg = type.parameterCount() - 1;
- int newArity = newType.parameterCount();
- if (newArity == collectArg+1 &&
- type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
- // if arity and trailing parameter are compatible, do normal thing
- return super.asType(newType);
- }
- // check cache
- if (cache.type().parameterCount() == newArity)
- return cache.asType(newType);
- // build and cache a collector
- int arrayLength = newArity - collectArg;
- MethodHandle collector;
- try {
- collector = target.asCollector(arrayType, arrayLength);
- } catch (IllegalArgumentException ex) {
- throw new WrongMethodTypeException("cannot build collector");
- }
- cache = collector;
- return collector.asType(newType);
- }
- }
-
- /** Can a checkcast adapter validly convert the target to newType?
- * The JVM supports all kind of reference casts, even silly ones.
- */
- static boolean canCheckCast(MethodType newType, MethodType targetType,
- int arg, Class<?> castType) {
- if (!convOpSupported(OP_CHECK_CAST)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- if (!canCheckCast(src, castType)
- || !VerifyType.isNullConversion(castType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1) || (diff == 0); // arg is sole non-trivial diff
- }
- /** Can an primitive conversion adapter validly convert src to dst? */
- static boolean canCheckCast(Class<?> src, Class<?> dst) {
- return (!src.isPrimitive() && !dst.isPrimitive());
- }
-
- /** Factory method: Forces a cast at the given argument.
- * The castType is the target of the cast, and can be any type
- * with a null conversion to the corresponding target parameter.
- * Return null if this cannot be done.
- */
- static MethodHandle makeCheckCast(MethodType newType, MethodHandle target,
- int arg, Class<?> castType) {
- if (!canCheckCast(newType, target.type(), arg, castType))
- return null;
- long conv = makeConv(OP_CHECK_CAST, arg, T_OBJECT, T_OBJECT);
- return new AdapterMethodHandle(target, newType, conv, castType);
- }
-
- /** Can an primitive conversion adapter validly convert the target to newType?
- * The JVM currently supports all conversions except those between
- * floating and integral types.
- */
- static boolean canPrimCast(MethodType newType, MethodType targetType,
- int arg, Class<?> convType) {
- if (!convOpSupported(OP_PRIM_TO_PRIM)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- if (!canPrimCast(src, convType)
- || !VerifyType.isNullConversion(convType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1); // arg is sole non-trivial diff
- }
- /** Can an primitive conversion adapter validly convert src to dst? */
- static boolean canPrimCast(Class<?> src, Class<?> dst) {
- if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
- return false;
- } else {
- boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
- boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
- return !(sflt | dflt); // no float support at present
- }
- }
-
- /** Factory method: Truncate the given argument with zero or sign extension,
- * and/or convert between single and doubleword versions of integer or float.
- * The convType is the target of the conversion, and can be any type
- * with a null conversion to the corresponding target parameter.
- * Return null if this cannot be done.
- */
- 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;
- Class<?> src = newType.parameterType(arg);
- long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType));
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an unboxing conversion validly convert src to dst?
- * The JVM currently supports all kinds of casting and unboxing.
- * 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 level) {
- if (!convOpSupported(OP_REF_TO_PRIM)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- Class<?> boxType = Wrapper.asWrapperType(convType);
- convType = Wrapper.asPrimitiveType(convType);
- if (!canCheckCast(src, boxType)
- || boxType == convType
- || !VerifyType.isNullConversion(convType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- 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, 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 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, level))
- return null;
- MethodType castDone = newType;
- if (!VerifyType.isNullConversion(src, boxType)) {
- // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int
- if (level != 0) {
- // must include additional conversions
- if (src == Object.class || !Wrapper.isWrapperType(src)) {
- // src must be examined at runtime, to detect Byte, Character, etc.
- MethodHandle unboxMethod = (level == 1
- ? ValueConversions.unbox(dst)
- : ValueConversions.unboxCast(dst));
- long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
- return new AdapterMethodHandle(target, newType, conv, unboxMethod);
- }
- // Example: Byte->int
- // Do this by reformulating the problem to Byte->byte.
- Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
- MethodType midType = newType.changeParameterType(arg, srcPrim);
- MethodHandle fixPrim; // makePairwiseConvert(midType, target, 0);
- if (canPrimCast(midType, oldType, arg, dst))
- fixPrim = makePrimCast(midType, target, arg, dst);
- else
- fixPrim = target;
- return makeUnboxArgument(newType, fixPrim, arg, srcPrim, 0);
- }
- castDone = newType.changeParameterType(arg, boxType);
- }
- long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
- MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
- if (castDone == newType)
- return adapter;
- 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;
- return (src.isPrimitive() && !dst.isPrimitive());
- }
-
- /** Factory method: Box the given argument.
- * Return null if this cannot be done.
- */
- static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
- int arg, Class<?> convType) {
- 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? */
- static boolean canDropArguments(MethodType newType, MethodType targetType,
- int dropArgPos, int dropArgCount) {
- if (dropArgCount == 0)
- return canRetypeOnly(newType, targetType);
- if (!convOpSupported(OP_DROP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- // parameter types must be the same up to the drop point
- if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0)
- return false;
- int afterPos = dropArgPos + dropArgCount;
- int afterCount = nptypes - afterPos;
- if (dropArgPos < 0 || dropArgPos >= nptypes ||
- dropArgCount < 1 || afterPos > nptypes ||
- targetType.parameterCount() != nptypes - dropArgCount)
- return false;
- // parameter types after the drop point must also be the same
- if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Drop selected arguments.
- * Allow unchecked retyping of remaining arguments, pairwise.
- * Return null if this is not possible.
- */
- static MethodHandle makeDropArguments(MethodType newType, MethodHandle target,
- int dropArgPos, int dropArgCount) {
- if (dropArgCount == 0)
- return makeRetypeOnly(newType, target);
- if (!canDropArguments(newType, target.type(), dropArgPos, dropArgCount))
- return null;
- // in arglist: [0: ...keep1 | dpos: drop... | dpos+dcount: keep2... ]
- // out arglist: [0: ...keep1 | dpos: keep2... ]
- int keep2InPos = dropArgPos + dropArgCount;
- int dropSlot = newType.parameterSlotDepth(keep2InPos);
- int keep1InSlot = newType.parameterSlotDepth(dropArgPos);
- int slotCount = keep1InSlot - dropSlot;
- assert(slotCount >= dropArgCount);
- assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
- long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter duplicate an argument to convert the target to newType? */
- static boolean canDupArguments(MethodType newType, MethodType targetType,
- int dupArgPos, int dupArgCount) {
- if (!convOpSupported(OP_DUP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- if (dupArgCount < 0 || dupArgPos + dupArgCount > nptypes)
- return false;
- if (targetType.parameterCount() != nptypes + dupArgCount)
- return false;
- // parameter types must be the same up to the duplicated arguments
- if (diffParamTypes(newType, 0, targetType, 0, nptypes, false) != 0)
- return false;
- // duplicated types must be, well, duplicates
- if (diffParamTypes(newType, dupArgPos, targetType, nptypes, dupArgCount, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Duplicate the selected argument.
- * Return null if this is not possible.
- */
- static MethodHandle makeDupArguments(MethodType newType, MethodHandle target,
- int dupArgPos, int dupArgCount) {
- if (!canDupArguments(newType, target.type(), dupArgPos, dupArgCount))
- return null;
- if (dupArgCount == 0)
- return target;
- // in arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... ]
- // out arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... | dup... ]
- int keep2InPos = dupArgPos + dupArgCount;
- int dupSlot = newType.parameterSlotDepth(keep2InPos);
- int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
- int slotCount = keep1InSlot - dupSlot;
- assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
- long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter swap two arguments to convert the target to newType? */
- static boolean canSwapArguments(MethodType newType, MethodType targetType,
- int swapArg1, int swapArg2) {
- if (!convOpSupported(OP_SWAP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- if (swapArg1 >= swapArg2) return false; // caller resp
- int nptypes = newType.parameterCount();
- if (targetType.parameterCount() != nptypes)
- return false;
- if (swapArg1 < 0 || swapArg2 >= nptypes)
- return false;
- if (diffParamTypes(newType, 0, targetType, 0, swapArg1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg1, targetType, swapArg2, 1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg1+1, targetType, swapArg1+1, swapArg2-swapArg1-1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg2, targetType, swapArg1, 1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg2+1, targetType, swapArg2+1, nptypes-swapArg2-1, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Swap the selected arguments.
- * Return null if this is not possible.
- */
- static MethodHandle makeSwapArguments(MethodType newType, MethodHandle target,
- int swapArg1, int swapArg2) {
- if (swapArg1 == swapArg2)
- return target;
- if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; }
- if (type2size(newType.parameterType(swapArg1)) !=
- type2size(newType.parameterType(swapArg2))) {
- // turn a swap into a pair of rotates:
- // [x a b c y] rot2(-1,argc=5) => [a b c y x] rot1(+1,argc=4) => target[y a b c x]
- int argc = swapArg2 - swapArg1 + 1;
- final int ROT = 1;
- ArrayList<Class<?>> rot1Params = new ArrayList<Class<?>>(target.type().parameterList());
- Collections.rotate(rot1Params.subList(swapArg1, swapArg1 + argc), -ROT);
- MethodType rot1Type = MethodType.methodType(target.type().returnType(), rot1Params);
- MethodHandle rot1 = makeRotateArguments(rot1Type, target, swapArg1, argc, +ROT);
- assert(rot1 != null);
- if (argc == 2) return rot1;
- MethodHandle rot2 = makeRotateArguments(newType, rot1, swapArg1, argc-1, -ROT);
- assert(rot2 != null);
- return rot2;
- }
- if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2))
- return null;
- Class<?> type1 = newType.parameterType(swapArg1);
- Class<?> type2 = newType.parameterType(swapArg2);
- // in arglist: [0: ...keep1 | pos1: a1 | pos1+1: keep2... | pos2: a2 | pos2+1: keep3... ]
- // out arglist: [0: ...keep1 | pos1: a2 | pos1+1: keep2... | pos2: a1 | pos2+1: keep3... ]
- int swapSlot2 = newType.parameterSlotDepth(swapArg2 + 1);
- long conv = makeSwapConv(OP_SWAP_ARGS, swapArg1, basicType(type1), swapSlot2, basicType(type2));
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- static int positiveRotation(int argCount, int rotateBy) {
- assert(argCount > 0);
- if (rotateBy >= 0) {
- if (rotateBy < argCount)
- return rotateBy;
- return rotateBy % argCount;
- } else if (rotateBy >= -argCount) {
- return rotateBy + argCount;
- } else {
- return (-1-((-1-rotateBy) % argCount)) + argCount;
- }
- }
-
- final static int MAX_ARG_ROTATION = 1;
-
- /** Can an adapter rotate arguments to convert the target to newType? */
- static boolean canRotateArguments(MethodType newType, MethodType targetType,
- int firstArg, int argCount, int rotateBy) {
- if (!convOpSupported(OP_ROT_ARGS)) return false;
- rotateBy = positiveRotation(argCount, rotateBy);
- if (rotateBy == 0) return false; // no rotation
- if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION)
- return false; // too many argument positions
- // Rotate incoming args right N to the out args, N in 1..(argCouunt-1).
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- if (targetType.parameterCount() != nptypes)
- return false;
- if (firstArg < 0 || firstArg >= nptypes) return false;
- int argLimit = firstArg + argCount;
- if (argLimit > nptypes) return false;
- if (diffParamTypes(newType, 0, targetType, 0, firstArg, false) != 0)
- return false;
- int newChunk1 = argCount - rotateBy, newChunk2 = rotateBy;
- // swap new chunk1 with target chunk2
- if (diffParamTypes(newType, firstArg, targetType, argLimit-newChunk1, newChunk1, false) != 0)
- return false;
- // swap new chunk2 with target chunk1
- if (diffParamTypes(newType, firstArg+newChunk1, targetType, firstArg, newChunk2, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Rotate the selected argument range.
- * Return null if this is not possible.
- */
- static MethodHandle makeRotateArguments(MethodType newType, MethodHandle target,
- int firstArg, int argCount, int rotateBy) {
- rotateBy = positiveRotation(argCount, rotateBy);
- if (!canRotateArguments(newType, target.type(), firstArg, argCount, rotateBy))
- return null;
- // Decide whether it should be done as a right or left rotation,
- // on the JVM stack. Return the number of stack slots to rotate by,
- // positive if right, negative if left.
- int limit = firstArg + argCount;
- int depth0 = newType.parameterSlotDepth(firstArg);
- int depth1 = newType.parameterSlotDepth(limit-rotateBy);
- int depth2 = newType.parameterSlotDepth(limit);
- int chunk1Slots = depth0 - depth1; assert(chunk1Slots > 0);
- int chunk2Slots = depth1 - depth2; assert(chunk2Slots > 0);
- // From here on out, it assumes a single-argument shift.
- assert(MAX_ARG_ROTATION == 1);
- int srcArg, dstArg;
- int dstSlot;
- int moveChunk;
- if (rotateBy == 1) {
- // Rotate right/down N (rotateBy = +N, N small, c2 small):
- // in arglist: [0: ...keep1 | arg1: c1... | limit-N: c2 | limit: keep2... ]
- // out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1... | limit: keep2... ]
- srcArg = limit-1;
- dstArg = firstArg;
- //dstSlot = depth0 - chunk2Slots; //chunk2Slots is not relevant
- dstSlot = depth0 + MethodHandleNatives.OP_ROT_ARGS_DOWN_LIMIT_BIAS;
- moveChunk = chunk2Slots;
- } else {
- // Rotate left/up N (rotateBy = -N, N small, c1 small):
- // in arglist: [0: ...keep1 | arg1: c1 | arg1+N: c2... | limit: keep2... ]
- // out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ]
- srcArg = firstArg;
- dstArg = limit-1;
- dstSlot = depth2;
- moveChunk = chunk1Slots;
- }
- byte srcType = basicType(newType.parameterType(srcArg));
- byte dstType = basicType(newType.parameterType(dstArg));
- assert(moveChunk == type2size(srcType));
- long conv = makeSwapConv(OP_ROT_ARGS, srcArg, srcType, dstSlot, dstType);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter spread an argument to convert the target to newType? */
- static boolean canSpreadArguments(MethodType newType, MethodType targetType,
- Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
- if (!convOpSupported(OP_SPREAD_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- // parameter types must be the same up to the spread point
- if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0)
- return false;
- int afterPos = spreadArgPos + spreadArgCount;
- int afterCount = nptypes - (spreadArgPos + 1);
- if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
- spreadArgCount < 0 ||
- targetType.parameterCount() != afterPos + afterCount)
- return false;
- // parameter types after the spread point must also be the same
- if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
- return false;
- // match the array element type to the spread arg types
- Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos);
- if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
- return false;
- for (int i = 0; i < spreadArgCount; i++) {
- Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
- Class<?> dst = targetType.parameterType(spreadArgPos + i);
- if (src == null || !canConvertArgument(src, dst, 1))
- return false;
- }
- return true;
- }
-
-
- /** 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();
- assert(canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
- : "[newType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = "
- + Arrays.asList(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount);
- // 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 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);
- 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;
- }
-
- /** 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
- String debugString() {
- return getNameString(nonAdapter((MethodHandle)vmtarget), this);
- }
-
- private static MethodHandle nonAdapter(MethodHandle mh) {
- while (mh instanceof AdapterMethodHandle) {
- mh = (MethodHandle) mh.vmtarget;
- }
- return mh;
- }
-}
--- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -25,164 +25,828 @@
package java.lang.invoke;
-import sun.invoke.util.VerifyType;
+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
+import static java.lang.invoke.LambdaForm.basicTypes;
+import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.LambdaForm.NamedFunction;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
-import static java.lang.invoke.MethodHandleStatics.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
+import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
+import com.sun.xml.internal.ws.org.objectweb.asm.Type;
/**
* The flavor of method handle which emulates an invoke instruction
* on a predetermined argument. The JVM dispatches to the correct method
* when the handle is created, not when it is invoked.
- * @author jrose
+ *
+ * All bound arguments are encapsulated in dedicated species.
*/
-class BoundMethodHandle extends MethodHandle {
- //MethodHandle vmtarget; // next BMH or final DMH or methodOop
- private final Object argument; // argument to insert
- private final int vmargslot; // position at which it is inserted
-
- // Constructors in this class *must* be package scoped or private.
+/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
- /** Bind a direct MH to its receiver (or first ref. argument).
- * The JVM will pre-dispatch the MH if it is not already static.
- */
- /*non-public*/ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
- super(mh.type().dropParameterTypes(0, 1));
- // check the type now, once for all:
- this.argument = checkReferenceArgument(argument, mh, 0);
- this.vmargslot = this.type().parameterSlotCount();
- initTarget(mh, 0);
+ /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
+ super(type, form);
}
- /** Insert an argument into an arbitrary method handle.
- * If argnum is zero, inserts the first argument, etc.
- * The argument type must be a reference.
- */
- /*non-public*/ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
- this(mh.type().dropParameterTypes(argnum, argnum+1),
- mh, argument, argnum);
- }
+ //
+ // BMH API and internals
+ //
- /** Insert an argument into an arbitrary method handle.
- * If argnum is zero, inserts the first argument, etc.
- */
- /*non-public*/ BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
- super(type);
- if (mh.type().parameterType(argnum).isPrimitive())
- this.argument = bindPrimitiveArgument(argument, mh, argnum);
- else {
- this.argument = checkReferenceArgument(argument, mh, argnum);
+ static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) {
+ // for some type signatures, there exist pre-defined concrete BMH classes
+ try {
+ switch (xtype) {
+ case 'L':
+ if (true) return bindSingle(type, form, x); // Use known fast path.
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x);
+ case 'I':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
+ case 'J':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
+ case 'F':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
+ case 'D':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
+ default : throw new InternalError("unexpected xtype: " + xtype);
+ }
+ } catch (Throwable t) {
+ throw new InternalError(t);
}
- this.vmargslot = type.parameterSlotDepth(argnum);
- initTarget(mh, argnum);
- }
-
- private void initTarget(MethodHandle mh, int argnum) {
- //this.vmtarget = mh; // maybe updated by JVM
- MethodHandleNatives.init(this, mh, argnum);
- }
-
- /** For the AdapterMethodHandle subclass.
- */
- /*non-public*/ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
- super(type);
- this.argument = argument;
- this.vmargslot = vmargslot;
- assert(this instanceof AdapterMethodHandle);
}
- /** Initialize the current object as a self-bound method handle, binding it
- * as the first argument of the method handle {@code entryPoint}.
- * The invocation type of the resulting method handle will be the
- * same as {@code entryPoint}, except that the first argument
- * type will be dropped.
- */
- /*non-public*/ BoundMethodHandle(MethodHandle entryPoint) {
- super(entryPoint.type().dropParameterTypes(0, 1));
- this.argument = this; // kludge; get rid of
- this.vmargslot = this.type().parameterSlotDepth(0);
- initTarget(entryPoint, 0);
+ static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
+ return new Species_L(type, form, x);
}
- /** Make sure the given {@code argument} can be used as {@code argnum}-th
- * parameter of the given method handle {@code mh}, which must be a reference.
- * <p>
- * If this fails, throw a suitable {@code WrongMethodTypeException},
- * which will prevent the creation of an illegally typed bound
- * method handle.
- */
- final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
- Class<?> ptype = mh.type().parameterType(argnum);
- if (ptype.isPrimitive()) {
- // fail
- } else if (argument == null) {
- return null;
- } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
- return argument;
+ MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
+ try {
+ switch (xtype) {
+ case 'L': return cloneExtendL(type, form, x);
+ case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
+ case 'J': return cloneExtendJ(type, form, (long) x);
+ case 'F': return cloneExtendF(type, form, (float) x);
+ case 'D': return cloneExtendD(type, form, (double) x);
+ }
+ } catch (Throwable t) {
+ throw new InternalError(t);
}
- throw badBoundArgumentException(argument, mh, argnum);
+ throw new InternalError("unexpected type: " + xtype);
}
- /** Make sure the given {@code argument} can be used as {@code argnum}-th
- * parameter of the given method handle {@code mh}, which must be a primitive.
- * <p>
- * If this fails, throw a suitable {@code WrongMethodTypeException},
- * which will prevent the creation of an illegally typed bound
- * method handle.
- */
- final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
- Class<?> ptype = mh.type().parameterType(argnum);
- Wrapper wrap = Wrapper.forPrimitiveType(ptype);
- Object zero = wrap.zero();
- if (zero == null) {
- // fail
- } else if (argument == null) {
- if (ptype != int.class && wrap.isSubwordOrInt())
- return Integer.valueOf(0);
- else
- return zero;
- } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
- if (ptype != int.class && wrap.isSubwordOrInt())
- return Wrapper.INT.wrap(argument);
- else
- return argument;
- }
- throw badBoundArgumentException(argument, mh, argnum);
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ MethodType type = type().dropParameterTypes(pos, pos+1);
+ LambdaForm form = internalForm().bind(1+pos, speciesData());
+ return cloneExtend(type, form, basicType, value);
}
- final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
- String atype = (argument == null) ? "null" : argument.getClass().toString();
- return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+ try {
+ return clone(srcType, form);
+ } catch (Throwable t) {
+ throw new InternalError(t);
+ }
}
@Override
- String debugString() {
- return addTypeString(baseName(), this);
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ try {
+ return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
+ } catch (Throwable t) {
+ throw new InternalError(t);
+ }
+ }
+
+ static final String EXTENSION_TYPES = "LIJFD";
+ static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
+ static byte extensionIndex(char type) {
+ int i = EXTENSION_TYPES.indexOf(type);
+ if (i < 0) throw new InternalError();
+ return (byte) i;
+ }
+
+ /**
+ * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
+ * static field containing this value, and they must accordingly implement this method.
+ */
+ protected abstract SpeciesData speciesData();
+
+ @Override
+ final Object internalValues() {
+ Object[] boundValues = new Object[speciesData().fieldCount()];
+ for (int i = 0; i < boundValues.length; ++i) {
+ boundValues[i] = arg(i);
+ }
+ return Arrays.asList(boundValues);
+ }
+
+ public final Object arg(int i) {
+ try {
+ switch (speciesData().fieldType(i)) {
+ case 'L': return argL(i);
+ case 'I': return argI(i);
+ case 'F': return argF(i);
+ case 'D': return argD(i);
+ case 'J': return argJ(i);
+ }
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ throw new InternalError("unexpected type: " + speciesData().types+"."+i);
+ }
+ public final Object argL(int i) throws Throwable { return speciesData().getters[i].invokeBasic(this); }
+ public final int argI(int i) throws Throwable { return (int) speciesData().getters[i].invokeBasic(this); }
+ public final float argF(int i) throws Throwable { return (float) speciesData().getters[i].invokeBasic(this); }
+ public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
+ public final long argJ(int i) throws Throwable { return (long) speciesData().getters[i].invokeBasic(this); }
+
+ //
+ // cloning API
+ //
+
+ public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
+
+ // The following is a grossly irregular hack:
+ @Override MethodHandle reinvokerTarget() {
+ try {
+ return (MethodHandle) argL(0);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ //
+ // concrete BMH classes required to close bootstrap loops
+ //
+
+ private // make it private to force users to access the enclosing class first
+ static final class Species_L extends BoundMethodHandle {
+ final Object argL0;
+ public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
+ super(mt, lf);
+ this.argL0 = argL0;
+ }
+ // The following is a grossly irregular hack:
+ @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_L(mt, lf, argL0);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ }
+
+/*
+ static final class Species_LL extends BoundMethodHandle {
+ final Object argL0;
+ final Object argL1;
+ public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
+ super(mt, lf);
+ this.argL0 = argL0;
+ this.argL1 = argL1;
+ }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class);
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_LL(mt, lf, argL0, argL1);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ }
+
+ static final class Species_JL extends BoundMethodHandle {
+ final long argJ0;
+ final Object argL1;
+ public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
+ super(mt, lf);
+ this.argJ0 = argJ0;
+ this.argL1 = argL1;
+ }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class);
+ @Override public final long argJ0() { return argJ0; }
+ @Override public final Object argL1() { return argL1; }
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_JL(mt, lf, argJ0, argL1);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ }
+*/
+
+ //
+ // BMH species meta-data
+ //
+
+ /**
+ * Meta-data wrapper for concrete BMH classes.
+ */
+ static class SpeciesData {
+ final String types;
+ final Class<? extends BoundMethodHandle> clazz;
+ // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
+ // Therefore, we need a non-final link in the chain. Use array elements.
+ final MethodHandle[] constructor;
+ final MethodHandle[] getters;
+ final SpeciesData[] extensions;
+
+ public int fieldCount() {
+ return types.length();
+ }
+ public char fieldType(int i) {
+ return types.charAt(i);
+ }
+
+ public String toString() {
+ return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
+ }
+
+ /**
+ * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
+ * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
+ * getter.
+ */
+ Name getterName(Name mhName, int i) {
+ MethodHandle mh = getters[i];
+ assert(mh != null) : this+"."+i;
+ return new Name(mh, mhName);
+ }
+
+ static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
+
+ private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
+ this.types = types;
+ this.clazz = clazz;
+ if (!INIT_DONE) {
+ this.constructor = new MethodHandle[1];
+ this.getters = new MethodHandle[types.length()];
+ } else {
+ this.constructor = Factory.makeCtors(clazz, types, null);
+ this.getters = Factory.makeGetters(clazz, types, null);
+ }
+ this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
+ }
+
+ private void initForBootstrap() {
+ assert(!INIT_DONE);
+ if (constructor[0] == null) {
+ Factory.makeCtors(clazz, types, this.constructor);
+ Factory.makeGetters(clazz, types, this.getters);
+ }
+ }
+
+ private SpeciesData(String types) {
+ // Placeholder only.
+ this.types = types;
+ this.clazz = null;
+ this.constructor = null;
+ this.getters = null;
+ this.extensions = null;
+ }
+ private boolean isPlaceholder() { return clazz == null; }
+
+ private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
+ private static final boolean INIT_DONE; // set after <clinit> finishes...
+
+ SpeciesData extendWithType(char type) {
+ int i = extensionIndex(type);
+ SpeciesData d = extensions[i];
+ if (d != null) return d;
+ extensions[i] = d = get(types+type);
+ return d;
+ }
+
+ SpeciesData extendWithIndex(byte index) {
+ SpeciesData d = extensions[index];
+ if (d != null) return d;
+ extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
+ return d;
+ }
+
+ private static SpeciesData get(String types) {
+ // Acquire cache lock for query.
+ SpeciesData d = lookupCache(types);
+ if (!d.isPlaceholder())
+ return d;
+ synchronized (d) {
+ // Use synch. on the placeholder to prevent multiple instantiation of one species.
+ // Creating this class forces a recursive call to getForClass.
+ if (lookupCache(types).isPlaceholder())
+ Factory.generateConcreteBMHClass(types);
+ }
+ // Reacquire cache lock.
+ d = lookupCache(types);
+ // Class loading must have upgraded the cache.
+ assert(d != null && !d.isPlaceholder());
+ return d;
+ }
+ static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
+ // clazz is a new class which is initializing its SPECIES_DATA field
+ return updateCache(types, new SpeciesData(types, clazz));
+ }
+ private static synchronized SpeciesData lookupCache(String types) {
+ SpeciesData d = CACHE.get(types);
+ if (d != null) return d;
+ d = new SpeciesData(types);
+ assert(d.isPlaceholder());
+ CACHE.put(types, d);
+ return d;
+ }
+ private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
+ SpeciesData d2;
+ assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
+ assert(!d.isPlaceholder());
+ CACHE.put(types, d);
+ return d;
+ }
+
+ static {
+ // pre-fill the BMH speciesdata cache with BMH's inner classes
+ final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
+ SpeciesData d0 = BoundMethodHandle.SPECIES_DATA; // trigger class init
+ assert(d0 == null || d0 == lookupCache("")) : d0;
+ try {
+ for (Class<?> c : rootCls.getDeclaredClasses()) {
+ if (rootCls.isAssignableFrom(c)) {
+ final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
+ SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
+ assert(d != null) : cbmh.getName();
+ assert(d.clazz == cbmh);
+ assert(d == lookupCache(d.types));
+ }
+ }
+ } catch (Throwable e) {
+ throw new InternalError(e);
+ }
+
+ for (SpeciesData d : CACHE.values()) {
+ d.initForBootstrap();
+ }
+ // Note: Do not simplify this, because INIT_DONE must not be
+ // a compile-time constant during bootstrapping.
+ INIT_DONE = Boolean.TRUE;
+ }
+ }
+
+ static SpeciesData getSpeciesData(String types) {
+ return SpeciesData.get(types);
}
- /** Component of toString() before the type string. */
- protected String baseName() {
- MethodHandle mh = this;
- while (mh instanceof BoundMethodHandle) {
- Object info = MethodHandleNatives.getTargetInfo(mh);
- if (info instanceof MethodHandle) {
- mh = (MethodHandle) info;
- } else {
- String name = null;
- if (info instanceof MemberName)
- name = ((MemberName)info).getName();
- if (name != null)
- return name;
- else
- return noParens(super.toString()); // "invoke", probably
+ /**
+ * Generation of concrete BMH classes.
+ *
+ * A concrete BMH species is fit for binding a number of values adhering to a
+ * given type pattern. Reference types are erased.
+ *
+ * BMH species are cached by type pattern.
+ *
+ * A BMH species has a number of fields with the concrete (possibly erased) types of
+ * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
+ * which can be included as names in lambda forms.
+ */
+ static class Factory {
+
+ static final String JLO_SIG = "Ljava/lang/Object;";
+ static final String JLS_SIG = "Ljava/lang/String;";
+ static final String JLC_SIG = "Ljava/lang/Class;";
+ static final String MH = "java/lang/invoke/MethodHandle";
+ static final String MH_SIG = "L"+MH+";";
+ static final String BMH = "java/lang/invoke/BoundMethodHandle";
+ static final String BMH_SIG = "L"+BMH+";";
+ static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
+ static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
+
+ static final String SPECIES_PREFIX_NAME = "Species_";
+ static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
+
+ static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
+ static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
+ static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
+ static final String VOID_SIG = "()V";
+
+ static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
+
+ static final Class<?>[] TYPES = new Class<?>[] { Object.class, int.class, long.class, float.class, double.class };
+
+ static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
+
+ /**
+ * Generate a concrete subclass of BMH for a given combination of bound types.
+ *
+ * A concrete BMH species adheres to the following schema:
+ *
+ * <pre>
+ * class Species_<<types>> extends BoundMethodHandle {
+ * <<fields>>
+ * final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); }
+ * }
+ * </pre>
+ *
+ * The {@code <<types>>} signature is precisely the string that is passed to this
+ * method.
+ *
+ * The {@code <<fields>>} section consists of one field definition per character in
+ * the type signature, adhering to the naming schema described in the definition of
+ * {@link #makeFieldName()}.
+ *
+ * For example, a concrete BMH species for two reference and one integral bound values
+ * would have the following shape:
+ *
+ * <pre>
+ * class BoundMethodHandle { ... private static
+ * final class Species_LLI extends BoundMethodHandle {
+ * final Object argL0;
+ * final Object argL1;
+ * final int argI2;
+ * public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
+ * super(mt, lf);
+ * this.argL0 = argL0;
+ * this.argL1 = argL1;
+ * this.argI2 = argI2;
+ * }
+ * public final SpeciesData speciesData() { return SPECIES_DATA; }
+ * public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
+ * public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
+ * return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
+ * }
+ * public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * }
+ * </pre>
+ *
+ * @param types the type signature, wherein reference types are erased to 'L'
+ * @return the generated concrete BMH class
+ */
+ static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
+ final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+
+ final String className = SPECIES_PREFIX_PATH + types;
+ final String sourceFile = SPECIES_PREFIX_NAME + types;
+ cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
+ cw.visitSource(sourceFile, null);
+
+ // emit static types and SPECIES_DATA fields
+ cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
+
+ // emit bound argument fields
+ for (int i = 0; i < types.length(); ++i) {
+ final char t = types.charAt(i);
+ final String fieldName = makeFieldName(types, i);
+ final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
+ cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
+ }
+
+ MethodVisitor mv;
+
+ // emit constructor
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+
+ mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true));
+
+ for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
+ // i counts the arguments, j counts corresponding argument slots
+ char t = types.charAt(i);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
+ mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
+ if (t == 'J' || t == 'D') {
+ ++j; // adjust argument register access
+ }
}
- assert(mh != this);
+
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit implementation of reinvokerTarget()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
+ mv.visitTypeInsn(CHECKCAST, MH);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit implementation of speciesData()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
+ mv.visitCode();
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit clone()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
+ mv.visitCode();
+ // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
+ // obtain constructor
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+ mv.visitInsn(ICONST_0);
+ mv.visitInsn(AALOAD);
+ // load mt, lf
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ // put fields on the stack
+ emitPushFields(types, className, mv);
+ // finally, invoke the constructor and return
+ mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // for each type, emit cloneExtendT()
+ for (Class<?> c : TYPES) {
+ char t = Wrapper.basicTypeChar(c);
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
+ mv.visitCode();
+ // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
+ // obtain constructor
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ int iconstInsn = ICONST_0 + extensionIndex(t);
+ assert(iconstInsn <= ICONST_5);
+ mv.visitInsn(iconstInsn);
+ mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
+ mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+ mv.visitInsn(ICONST_0);
+ mv.visitInsn(AALOAD);
+ // load mt, lf
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ // put fields on the stack
+ emitPushFields(types, className, mv);
+ // put narg on stack
+ mv.visitVarInsn(typeLoadOp(t), 3);
+ // finally, invoke the constructor and return
+ mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ // emit class initializer
+ mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
+ mv.visitCode();
+ mv.visitLdcInsn(types);
+ mv.visitLdcInsn(Type.getObjectType(className));
+ mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
+ mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ cw.visitEnd();
+
+ // load class
+ final byte[] classFile = cw.toByteArray();
+ InvokerBytecodeGenerator.maybeDump(className, classFile);
+ Class<? extends BoundMethodHandle> bmhClass =
+ //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
+ UNSAFE.defineClass(className, classFile, 0, classFile.length).asSubclass(BoundMethodHandle.class);
+ UNSAFE.ensureClassInitialized(bmhClass);
+
+ return bmhClass;
+ }
+
+ private static int typeLoadOp(char t) {
+ switch (t) {
+ case 'L': return ALOAD;
+ case 'I': return ILOAD;
+ case 'J': return LLOAD;
+ case 'F': return FLOAD;
+ case 'D': return DLOAD;
+ default : throw new InternalError("unrecognized type " + t);
+ }
}
- return noParens(mh.toString());
+
+ private static void emitPushFields(String types, String className, MethodVisitor mv) {
+ for (int i = 0; i < types.length(); ++i) {
+ char tc = types.charAt(i);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
+ }
+ }
+
+ static String typeSig(char t) {
+ return t == 'L' ? JLO_SIG : String.valueOf(t);
+ }
+
+ //
+ // Getter MH generation.
+ //
+
+ private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
+ String fieldName = makeFieldName(types, index);
+ Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
+ try {
+ return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
+ if (mhs == null) mhs = new MethodHandle[types.length()];
+ for (int i = 0; i < mhs.length; ++i) {
+ mhs[i] = makeGetter(cbmhClass, types, i);
+ assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
+ }
+ return mhs;
+ }
+
+ static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
+ if (mhs == null) mhs = new MethodHandle[1];
+ mhs[0] = makeCbmhCtor(cbmh, types);
+ return mhs;
+ }
+
+ //
+ // Auxiliary methods.
+ //
+
+ static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
+ try {
+ Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
+ return (SpeciesData) F_SPECIES_DATA.get(null);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ /**
+ * Field names in concrete BMHs adhere to this pattern:
+ * arg + type + index
+ * where type is a single character (L, I, J, F, D).
+ */
+ private static String makeFieldName(String types, int index) {
+ assert index >= 0 && index < types.length();
+ return "arg" + types.charAt(index) + index;
+ }
+
+ private static String makeSignature(String types, boolean ctor) {
+ StringBuilder buf = new StringBuilder(SIG_INCIPIT);
+ for (char c : types.toCharArray()) {
+ buf.append(typeSig(c));
+ }
+ return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
+ }
+
+ static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
+ try {
+ return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null)));
+ } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ /**
+ * Wrap a constructor call in a {@link LambdaForm}.
+ *
+ * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs
+ * are turned into bytecode, because the call to the allocator is routed through an MH, and the
+ * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to
+ * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through
+ * {@link MethodHandle#linkToSpecial}.
+ *
+ * The last {@link LambdaForm#Name Name} in the argument's form is expected to be the {@code void}
+ * result of the {@code <init>} invocation. This entry is replaced.
+ */
+ private static MethodHandle linkConstructor(MethodHandle cmh) {
+ final LambdaForm lf = cmh.form;
+ final int initNameIndex = lf.names.length - 1;
+ final Name initName = lf.names[initNameIndex];
+ final MemberName ctorMN = initName.function.member;
+ final MethodType ctorMT = ctorMN.getInvocationType();
+
+ // obtain function member (call target)
+ // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!)
+ final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class);
+ MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic);
+ try {
+ linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class);
+ assert(linkerMN.isStatic());
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ // extend arguments array
+ Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1);
+ newArgs[newArgs.length - 1] = ctorMN;
+ // replace function
+ final NamedFunction nf = new NamedFunction(linkerMN);
+ final Name linkedCtor = new Name(nf, newArgs);
+ linkedCtor.initIndex(initNameIndex);
+ lf.names[initNameIndex] = linkedCtor;
+ return cmh;
+ }
+
}
- private static String noParens(String str) {
- int paren = str.indexOf('(');
- if (paren >= 0) str = str.substring(0, paren);
- return str;
- }
+ private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
+
+ /**
+ * All subclasses must provide such a value describing their type signature.
+ */
+ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
}
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,7 +26,7 @@
package java.lang.invoke;
import sun.invoke.empty.Empty;
-import sun.misc.Unsafe;
+import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
@@ -86,13 +86,9 @@
public class CallSite {
static { MethodHandleImpl.initStatics(); }
- // Fields used only by the JVM. Do not use or change.
- private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
- private int vmindex; // supplied by the JVM (BCI within calling method)
-
// The actual payload of this call site:
/*package-private*/
- MethodHandle target;
+ MethodHandle target; // Note: This field is known to the JVM. Do not change.
/**
* Make a blank call site object with the given method type.
@@ -151,24 +147,6 @@
return target.type();
}
- /** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite.
- * The parameters are JVM-specific.
- */
- void initializeFromJVM(String name,
- MethodType type,
- MemberName callerMethod,
- int callerBCI) {
- if (this.vmmethod != null) {
- // FIXME
- throw new BootstrapMethodError("call site has already been linked to an invokedynamic instruction");
- }
- if (!this.type().equals(type)) {
- throw wrongTargetType(target, type);
- }
- this.vmindex = callerBCI;
- this.vmmethod = callerMethod;
- }
-
/**
* Returns the target method of the call site, according to the
* behavior defined by this call site's specific class.
@@ -233,7 +211,7 @@
public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() {
- MethodHandle getTarget = MethodHandleImpl.bindReceiver(GET_TARGET, this);
+ MethodHandle getTarget = GET_TARGET.bindReceiver(this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
@@ -255,12 +233,10 @@
}
// unsafe stuff:
- private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long TARGET_OFFSET;
-
static {
try {
- TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
} catch (Exception ex) { throw new Error(ex); }
}
@@ -270,7 +246,7 @@
}
/*package-private*/
MethodHandle getTargetVolatile() {
- return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
+ return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
}
/*package-private*/
void setTargetVolatile(MethodHandle newTarget) {
@@ -284,8 +260,7 @@
// Extra arguments for BSM, if any:
Object info,
// Caller information:
- MemberName callerMethod, int callerBCI) {
- Class<?> callerClass = callerMethod.getDeclaringClass();
+ Class<?> callerClass) {
Object caller = IMPL_LOOKUP.in(callerClass);
CallSite site;
try {
--- a/jdk/src/share/classes/java/lang/invoke/CountingMethodHandle.java Mon Aug 27 10:23:43 2012 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-package java.lang.invoke;
-
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-
-/**
- * This method handle is used to optionally provide a count of how
- * many times it was invoked.
- *
- * @author never
- */
-class CountingMethodHandle extends AdapterMethodHandle {
- private int vmcount;
-
- private CountingMethodHandle(MethodHandle target) {
- super(target, target.type(), AdapterMethodHandle.makeConv(OP_RETYPE_ONLY));
- }
-
- /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
- static MethodHandle wrap(MethodHandle mh) {
- if (MethodHandleNatives.COUNT_GWT) {
- return new CountingMethodHandle(mh);
- }
- return mh;
- }
-}
--- a/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,29 +25,635 @@
package java.lang.invoke;
+import sun.misc.Unsafe;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import sun.invoke.util.VerifyAccess;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodTypeForm.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
/**
- * The flavor of method handle which emulates invokespecial or invokestatic.
+ * The flavor of method handle which implements a constant reference
+ * to a class member.
* @author jrose
*/
class DirectMethodHandle extends MethodHandle {
- //inherited oop vmtarget; // methodOop or virtual class/interface oop
- private final int vmindex; // method index within class or interface
- { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this
+ final MemberName member;
+
+ // Constructors and factory methods in this class *must* be package scoped or private.
+ private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
+ super(mtype, form);
+ if (!member.isResolved()) throw new InternalError();
+ this.member = member;
+ }
+
+ // Factory methods:
+
+ static DirectMethodHandle make(Class<?> receiver, MemberName member) {
+ MethodType mtype = member.getMethodOrFieldType();
+ if (!member.isStatic()) {
+ if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
+ throw new InternalError(member.toString());
+ mtype = mtype.insertParameterTypes(0, receiver);
+ }
+ if (!member.isField()) {
+ LambdaForm lform = preparedLambdaForm(member);
+ return new DirectMethodHandle(mtype, lform, member);
+ } else {
+ LambdaForm lform = preparedFieldLambdaForm(member);
+ if (member.isStatic()) {
+ long offset = MethodHandleNatives.staticFieldOffset(member);
+ Object base = MethodHandleNatives.staticFieldBase(member);
+ return new StaticAccessor(mtype, lform, member, base, offset);
+ } else {
+ long offset = MethodHandleNatives.objectFieldOffset(member);
+ assert(offset == (int)offset);
+ return new Accessor(mtype, lform, member, (int)offset);
+ }
+ }
+ }
+ static DirectMethodHandle make(MemberName member) {
+ if (member.isConstructor())
+ return makeAllocator(member);
+ return make(member.getDeclaringClass(), member);
+ }
+ static DirectMethodHandle make(Method method) {
+ return make(method.getDeclaringClass(), new MemberName(method));
+ }
+ static DirectMethodHandle make(Field field) {
+ return make(field.getDeclaringClass(), new MemberName(field));
+ }
+ private static DirectMethodHandle makeAllocator(MemberName ctor) {
+ assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
+ Class<?> instanceClass = ctor.getDeclaringClass();
+ ctor = ctor.asConstructor();
+ assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
+ MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
+ LambdaForm lform = preparedLambdaForm(ctor);
+ MemberName init = ctor.asSpecial();
+ assert(init.getMethodType().returnType() == void.class);
+ return new Constructor(mtype, lform, ctor, init, instanceClass);
+ }
+
+ @Override
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ return new DirectMethodHandle(mt, lf, member);
+ }
+
+ @Override
+ String debugString() {
+ return "DMH["+member.toString()+"]="+super.debugString();
+ }
+
+ //// Implementation methods.
+ @Override
+ @ForceInline
+ MemberName internalMemberName() {
+ return member;
+ }
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ // If the member needs dispatching, do so.
+ if (pos == 0 && basicType == 'L') {
+ DirectMethodHandle concrete = maybeRebind(value);
+ if (concrete != null)
+ return concrete.bindReceiver(value);
+ }
+ return super.bindArgument(pos, basicType, value);
+ }
+
+ @Override
+ MethodHandle bindReceiver(Object receiver) {
+ // If the member needs dispatching, do so.
+ DirectMethodHandle concrete = maybeRebind(receiver);
+ if (concrete != null)
+ return concrete.bindReceiver(receiver);
+ return super.bindReceiver(receiver);
+ }
+
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+ private DirectMethodHandle maybeRebind(Object receiver) {
+ if (receiver != null) {
+ switch (member.getReferenceKind()) {
+ case REF_invokeInterface:
+ case REF_invokeVirtual:
+ // Pre-dispatch the member.
+ Class<?> concreteClass = receiver.getClass();
+ MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial);
+ concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass);
+ if (concrete != null)
+ return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
+ break;
+ }
+ }
+ return null;
+ }
- // Constructors in this class *must* be package scoped or private.
- DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) {
- super(mtype);
+ /**
+ * Create a LF which can invoke the given method.
+ * Cache and share this structure among all methods with
+ * the same basicType and refKind.
+ */
+ private static LambdaForm preparedLambdaForm(MemberName m) {
+ assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
+ MethodType mtype = m.getInvocationType().basicType();
+ assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+ int which;
+ switch (m.getReferenceKind()) {
+ case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
+ case REF_invokeStatic: which = LF_INVSTATIC; break;
+ case REF_invokeSpecial: which = LF_INVSPECIAL; break;
+ case REF_invokeInterface: which = LF_INVINTERFACE; break;
+ case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
+ default: throw new InternalError(m.toString());
+ }
+ if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
+ // precompute the barrier-free version:
+ preparedLambdaForm(mtype, which);
+ which = LF_INVSTATIC_INIT;
+ }
+ LambdaForm lform = preparedLambdaForm(mtype, which);
+ maybeCompile(lform, m);
+ assert(lform.methodType().dropParameterTypes(0, 1)
+ .equals(m.getInvocationType().basicType()))
+ : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+ return lform;
+ }
+
+ private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
+ LambdaForm lform = mtype.form().cachedLambdaForm(which);
+ if (lform != null) return lform;
+ lform = makePreparedLambdaForm(mtype, which);
+ return mtype.form().setCachedLambdaForm(which, lform);
+ }
- assert(m.isMethod() || !doDispatch && m.isConstructor());
- if (!m.isResolved())
- throw new InternalError();
+ private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
+ boolean needsInit = (which == LF_INVSTATIC_INIT);
+ boolean doesAlloc = (which == LF_NEWINVSPECIAL);
+ String linkerName, lambdaName;
+ switch (which) {
+ case LF_INVVIRTUAL: linkerName = "linkToVirtual"; lambdaName = "DMH.invokeVirtual"; break;
+ case LF_INVSTATIC: linkerName = "linkToStatic"; lambdaName = "DMH.invokeStatic"; break;
+ case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; lambdaName = "DMH.invokeStaticInit"; break;
+ case LF_INVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.invokeSpecial"; break;
+ case LF_INVINTERFACE: linkerName = "linkToInterface"; lambdaName = "DMH.invokeInterface"; break;
+ case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.newInvokeSpecial"; break;
+ default: throw new InternalError("which="+which);
+ }
+ MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
+ if (doesAlloc)
+ mtypeWithArg = mtypeWithArg
+ .insertParameterTypes(0, Object.class) // insert newly allocated obj
+ .changeReturnType(void.class); // <init> returns void
+ MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
+ try {
+ linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ final int DMH_THIS = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ int nameCursor = ARG_LIMIT;
+ final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
+ final int GET_MEMBER = nameCursor++;
+ final int LINKER_CALL = nameCursor++;
+ Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ assert(names.length == nameCursor);
+ if (doesAlloc) {
+ // names = { argx,y,z,... new C, init method }
+ names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
+ names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
+ } else if (needsInit) {
+ names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
+ } else {
+ names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
+ }
+ Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
+ assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
+ int result = LambdaForm.LAST_RESULT;
+ if (doesAlloc) {
+ assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one
+ System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
+ outArgs[0] = names[NEW_OBJ];
+ result = NEW_OBJ;
+ }
+ names[LINKER_CALL] = new Name(linker, outArgs);
+ lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
+ LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
+ // This is a tricky bit of code. Don't send it through the LF interpreter.
+ lform.compileToBytecode();
+ return lform;
+ }
- MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
+ private static void maybeCompile(LambdaForm lform, MemberName m) {
+ if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
+ // Help along bootstrapping...
+ lform.compileToBytecode();
+ }
+
+ /** Static wrapper for DirectMethodHandle.internalMemberName. */
+ @ForceInline
+ /*non-public*/ static Object internalMemberName(Object mh) {
+ return ((DirectMethodHandle)mh).member;
+ }
+
+ /** Static wrapper for DirectMethodHandle.internalMemberName.
+ * This one also forces initialization.
+ */
+ /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
+ DirectMethodHandle dmh = (DirectMethodHandle)mh;
+ dmh.ensureInitialized();
+ return dmh.member;
+ }
+
+ /*non-public*/ static
+ boolean shouldBeInitialized(MemberName member) {
+ switch (member.getReferenceKind()) {
+ case REF_invokeStatic:
+ case REF_getStatic:
+ case REF_putStatic:
+ case REF_newInvokeSpecial:
+ break;
+ default:
+ // No need to initialize the class on this kind of member.
+ return false;
+ }
+ Class<?> cls = member.getDeclaringClass();
+ if (cls == ValueConversions.class ||
+ cls == MethodHandleImpl.class ||
+ cls == Invokers.class) {
+ // These guys have lots of <clinit> DMH creation but we know
+ // the MHs will not be used until the system is booted.
+ return false;
+ }
+ if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
+ VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
+ // It is a system class. It is probably in the process of
+ // being initialized, but we will help it along just to be safe.
+ if (UNSAFE.shouldBeInitialized(cls)) {
+ UNSAFE.ensureClassInitialized(cls);
+ }
+ return false;
+ }
+ return UNSAFE.shouldBeInitialized(cls);
+ }
+
+ private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
+ @Override
+ protected WeakReference<Thread> computeValue(Class<?> type) {
+ UNSAFE.ensureClassInitialized(type);
+ if (UNSAFE.shouldBeInitialized(type))
+ // If the previous call didn't block, this can happen.
+ // We are executing inside <clinit>.
+ return new WeakReference<>(Thread.currentThread());
+ return null;
+ }
+ static final EnsureInitialized INSTANCE = new EnsureInitialized();
}
- boolean isValid() {
- return (vmindex != VM_INDEX_UNINITIALIZED);
+ private void ensureInitialized() {
+ if (checkInitialized(member)) {
+ // The coast is clear. Delete the <clinit> barrier.
+ if (member.isField())
+ updateForm(preparedFieldLambdaForm(member));
+ else
+ updateForm(preparedLambdaForm(member));
+ }
+ }
+ private static boolean checkInitialized(MemberName member) {
+ Class<?> defc = member.getDeclaringClass();
+ WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
+ if (ref == null) {
+ return true; // the final state
+ }
+ Thread clinitThread = ref.get();
+ // Somebody may still be running defc.<clinit>.
+ if (clinitThread == Thread.currentThread()) {
+ // If anybody is running defc.<clinit>, it is this thread.
+ if (UNSAFE.shouldBeInitialized(defc))
+ // Yes, we are running it; keep the barrier for now.
+ return false;
+ } else {
+ // We are in a random thread. Block.
+ UNSAFE.ensureClassInitialized(defc);
+ }
+ assert(!UNSAFE.shouldBeInitialized(defc));
+ // put it into the final state
+ EnsureInitialized.INSTANCE.remove(defc);
+ return true;
+ }
+
+ /*non-public*/ static void ensureInitialized(Object mh) {
+ ((DirectMethodHandle)mh).ensureInitialized();
+ }
+
+ /** This subclass handles constructor references. */
+ static class Constructor extends DirectMethodHandle {
+ final MemberName initMethod;
+ final Class<?> instanceClass;
+
+ private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
+ MemberName initMethod, Class<?> instanceClass) {
+ super(mtype, form, constructor);
+ this.initMethod = initMethod;
+ this.instanceClass = instanceClass;
+ assert(initMethod.isResolved());
+ }
+ }
+
+ /*non-public*/ static Object constructorMethod(Object mh) {
+ Constructor dmh = (Constructor)mh;
+ return dmh.initMethod;
+ }
+
+ /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
+ Constructor dmh = (Constructor)mh;
+ return UNSAFE.allocateInstance(dmh.instanceClass);
+ }
+
+ /** This subclass handles non-static field references. */
+ static class Accessor extends DirectMethodHandle {
+ final Class<?> fieldType;
+ final int fieldOffset;
+ private Accessor(MethodType mtype, LambdaForm form, MemberName member,
+ int fieldOffset) {
+ super(mtype, form, member);
+ this.fieldType = member.getFieldType();
+ this.fieldOffset = fieldOffset;
+ }
+
+ @Override Object checkCast(Object obj) {
+ return fieldType.cast(obj);
+ }
+ }
+
+ @ForceInline
+ /*non-public*/ static long fieldOffset(Object accessorObj) {
+ // Note: We return a long because that is what Unsafe.getObject likes.
+ // We store a plain int because it is more compact.
+ return ((Accessor)accessorObj).fieldOffset;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object checkBase(Object obj) {
+ // Note that the object's class has already been verified,
+ // since the parameter type of the Accessor method handle
+ // is either member.getDeclaringClass or a subclass.
+ // This was verified in DirectMethodHandle.make.
+ // Therefore, the only remaining check is for null.
+ // Since this check is *not* guaranteed by Unsafe.getInt
+ // and its siblings, we need to make an explicit one here.
+ obj.getClass(); // maybe throw NPE
+ return obj;
+ }
+
+ /** This subclass handles static field references. */
+ static class StaticAccessor extends DirectMethodHandle {
+ final private Class<?> fieldType;
+ final private Object staticBase;
+ final private long staticOffset;
+
+ private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
+ Object staticBase, long staticOffset) {
+ super(mtype, form, member);
+ this.fieldType = member.getFieldType();
+ this.staticBase = staticBase;
+ this.staticOffset = staticOffset;
+ }
+
+ @Override Object checkCast(Object obj) {
+ return fieldType.cast(obj);
+ }
+ }
+
+ @ForceInline
+ /*non-public*/ static Object nullCheck(Object obj) {
+ obj.getClass();
+ return obj;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object staticBase(Object accessorObj) {
+ return ((StaticAccessor)accessorObj).staticBase;
+ }
+
+ @ForceInline
+ /*non-public*/ static long staticOffset(Object accessorObj) {
+ return ((StaticAccessor)accessorObj).staticOffset;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object checkCast(Object mh, Object obj) {
+ return ((DirectMethodHandle) mh).checkCast(obj);
+ }
+
+ Object checkCast(Object obj) {
+ return member.getReturnType().cast(obj);
+ }
+
+ // Caching machinery for field accessors:
+ private static byte
+ AF_GETFIELD = 0,
+ AF_PUTFIELD = 1,
+ AF_GETSTATIC = 2,
+ AF_PUTSTATIC = 3,
+ AF_GETSTATIC_INIT = 4,
+ AF_PUTSTATIC_INIT = 5,
+ AF_LIMIT = 6;
+ // Enumerate the different field kinds using Wrapper,
+ // with an extra case added for checked references.
+ private static int
+ FT_LAST_WRAPPER = Wrapper.values().length-1,
+ FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
+ FT_CHECKED_REF = FT_LAST_WRAPPER+1,
+ FT_LIMIT = FT_LAST_WRAPPER+2;
+ private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
+ return ((formOp * FT_LIMIT * 2)
+ + (isVolatile ? FT_LIMIT : 0)
+ + ftypeKind);
+ }
+ private static final LambdaForm[] ACCESSOR_FORMS
+ = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
+ private static int ftypeKind(Class<?> ftype) {
+ if (ftype.isPrimitive())
+ return Wrapper.forPrimitiveType(ftype).ordinal();
+ else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
+ return FT_UNCHECKED_REF;
+ else
+ return FT_CHECKED_REF;
+ }
+
+ /**
+ * Create a LF which can access the given field.
+ * Cache and share this structure among all fields with
+ * the same basicType and refKind.
+ */
+ private static LambdaForm preparedFieldLambdaForm(MemberName m) {
+ Class<?> ftype = m.getFieldType();
+ boolean isVolatile = m.isVolatile();
+ byte formOp;
+ switch (m.getReferenceKind()) {
+ case REF_getField: formOp = AF_GETFIELD; break;
+ case REF_putField: formOp = AF_PUTFIELD; break;
+ case REF_getStatic: formOp = AF_GETSTATIC; break;
+ case REF_putStatic: formOp = AF_PUTSTATIC; break;
+ default: throw new InternalError(m.toString());
+ }
+ if (shouldBeInitialized(m)) {
+ // precompute the barrier-free version:
+ preparedFieldLambdaForm(formOp, isVolatile, ftype);
+ assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
+ (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
+ formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
+ }
+ LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
+ maybeCompile(lform, m);
+ assert(lform.methodType().dropParameterTypes(0, 1)
+ .equals(m.getInvocationType().basicType()))
+ : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+ return lform;
+ }
+ private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
+ int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
+ LambdaForm lform = ACCESSOR_FORMS[afIndex];
+ if (lform != null) return lform;
+ lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
+ ACCESSOR_FORMS[afIndex] = lform; // don't bother with a CAS
+ return lform;
+ }
+
+ private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+ boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
+ boolean isStatic = (formOp >= AF_GETSTATIC);
+ boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
+ boolean needsCast = (ftypeKind == FT_CHECKED_REF);
+ Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
+ Class<?> ft = fw.primitiveType();
+ assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
+ String tname = fw.primitiveSimpleName();
+ String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
+ if (isVolatile) ctname += "Volatile";
+ String getOrPut = (isGetter ? "get" : "put");
+ String linkerName = (getOrPut + ctname); // getObject, putIntVolatile, etc.
+ MethodType linkerType;
+ if (isGetter)
+ linkerType = MethodType.methodType(ft, Object.class, long.class);
+ else
+ linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
+ MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
+ try {
+ linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+
+ // What is the external type of the lambda form?
+ MethodType mtype;
+ if (isGetter)
+ mtype = MethodType.methodType(ft);
+ else
+ mtype = MethodType.methodType(void.class, ft);
+ mtype = mtype.basicType(); // erase short to int, etc.
+ if (!isStatic)
+ mtype = mtype.insertParameterTypes(0, Object.class);
+ final int DMH_THIS = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ // if this is for non-static access, the base pointer is stored at this index:
+ final int OBJ_BASE = isStatic ? -1 : ARG_BASE;
+ // if this is for write access, the value to be written is stored at this index:
+ final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1;
+ int nameCursor = ARG_LIMIT;
+ final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any
+ final int F_OFFSET = nameCursor++; // Either static offset or field offset.
+ final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
+ final int INIT_BAR = (needsInit ? nameCursor++ : -1);
+ final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1);
+ final int LINKER_CALL = nameCursor++;
+ final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
+ final int RESULT = nameCursor-1; // either the call or the cast
+ Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ if (needsInit)
+ names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
+ if (needsCast && !isGetter)
+ names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
+ Object[] outArgs = new Object[1 + linkerType.parameterCount()];
+ assert(outArgs.length == (isGetter ? 3 : 4));
+ outArgs[0] = UNSAFE;
+ if (isStatic) {
+ outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]);
+ outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]);
+ } else {
+ outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
+ outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]);
+ }
+ if (!isGetter) {
+ outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
+ }
+ for (Object a : outArgs) assert(a != null);
+ names[LINKER_CALL] = new Name(linker, outArgs);
+ if (needsCast && isGetter)
+ names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
+ for (Name n : names) assert(n != null);
+ String fieldOrStatic = (isStatic ? "Static" : "Field");
+ String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging
+ if (needsCast) lambdaName += "Cast";
+ if (needsInit) lambdaName += "Init";
+ return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
+ }
+
+ private static final NamedFunction
+ NF_internalMemberName,
+ NF_internalMemberNameEnsureInit,
+ NF_ensureInitialized,
+ NF_fieldOffset,
+ NF_checkBase,
+ NF_staticBase,
+ NF_staticOffset,
+ NF_checkCast,
+ NF_allocateInstance,
+ NF_constructorMethod;
+ static {
+ try {
+ NamedFunction nfs[] = {
+ NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("internalMemberName", Object.class)),
+ NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
+ NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("ensureInitialized", Object.class)),
+ NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("fieldOffset", Object.class)),
+ NF_checkBase = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("checkBase", Object.class)),
+ NF_staticBase = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("staticBase", Object.class)),
+ NF_staticOffset = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("staticOffset", Object.class)),
+ NF_checkCast = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("checkCast", Object.class, Object.class)),
+ NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("allocateInstance", Object.class)),
+ NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("constructorMethod", Object.class))
+ };
+ for (NamedFunction nf : nfs) {
+ // Each nf must be statically invocable or we get tied up in our bootstraps.
+ assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
+ nf.resolve();
+ }
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/DontInline.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface DontInline {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/ForceInline.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface ForceInline {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package java.lang.invoke;
+
+import sun.invoke.util.VerifyAccess;
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import sun.invoke.util.Wrapper;
+
+import java.io.*;
+import java.util.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.*;
+
+import java.lang.reflect.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+
+/**
+ * Code generation backend for LambdaForm.
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class InvokerBytecodeGenerator {
+ /** Define class names for convenience. */
+ private static final String MH = "java/lang/invoke/MethodHandle";
+ private static final String BMH = "java/lang/invoke/BoundMethodHandle";
+ private static final String LF = "java/lang/invoke/LambdaForm";
+ private static final String LFN = "java/lang/invoke/LambdaForm$Name";
+ private static final String CLS = "java/lang/Class";
+ private static final String OBJ = "java/lang/Object";
+ private static final String OBJARY = "[Ljava/lang/Object;";
+
+ private static final String LF_SIG = "L" + LF + ";";
+ private static final String LFN_SIG = "L" + LFN + ";";
+ private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
+
+ /** Name of its super class*/
+ private static final String superName = LF;
+
+ /** Name of new class */
+ private final String className;
+
+ /** Name of the source file (for stack trace printing). */
+ private final String sourceFile;
+
+ private final LambdaForm lambdaForm;
+ private final String invokerName;
+ private final MethodType invokerType;
+ private final int[] localsMap;
+
+ /** ASM bytecode generation. */
+ private ClassWriter cw;
+ private MethodVisitor mv;
+
+ private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
+ private static final Class<?> HOST_CLASS = LambdaForm.class;
+
+ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
+ String className, String invokerName, MethodType invokerType) {
+ if (invokerName.contains(".")) {
+ int p = invokerName.indexOf(".");
+ className = invokerName.substring(0, p);
+ invokerName = invokerName.substring(p+1);
+ }
+ if (DUMP_CLASS_FILES) {
+ className = makeDumpableClassName(className);
+ }
+ this.className = superName + "$" + className;
+ this.sourceFile = "LambdaForm$" + className;
+ this.lambdaForm = lambdaForm;
+ this.invokerName = invokerName;
+ this.invokerType = invokerType;
+ this.localsMap = new int[localsMapSize];
+ }
+
+ private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
+ this(null, invokerType.parameterCount(),
+ className, invokerName, invokerType);
+ // Create an array to map name indexes to locals indexes.
+ for (int i = 0; i < localsMap.length; i++) {
+ localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
+ }
+ }
+
+ private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
+ this(form, form.names.length,
+ className, form.debugName, invokerType);
+ // Create an array to map name indexes to locals indexes.
+ Name[] names = form.names;
+ for (int i = 0, index = 0; i < localsMap.length; i++) {
+ localsMap[i] = index;
+ index += Wrapper.forBasicType(names[i].type).stackSlots();
+ }
+ }
+
+
+ /** instance counters for dumped classes */
+ private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
+ /** debugging flag for saving generated class files */
+ private final static File DUMP_CLASS_FILES_DIR;
+
+ static {
+ if (DUMP_CLASS_FILES) {
+ DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
+ try {
+ File dumpDir = new File("DUMP_CLASS_FILES");
+ if (!dumpDir.exists()) {
+ dumpDir.mkdirs();
+ }
+ DUMP_CLASS_FILES_DIR = dumpDir;
+ System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
+ } catch (Exception e) {
+ throw new InternalError(e);
+ }
+ } else {
+ DUMP_CLASS_FILES_COUNTERS = null;
+ DUMP_CLASS_FILES_DIR = null;
+ }
+ }
+
+ static void maybeDump(final String className, final byte[] classFile) {
+ if (DUMP_CLASS_FILES) {
+ System.out.println("dump: " + className);
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ try {
+ String dumpName = className;
+ //dumpName = dumpName.replace('/', '-');
+ File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
+ dumpFile.getParentFile().mkdirs();
+ FileOutputStream file = new FileOutputStream(dumpFile);
+ file.write(classFile);
+ file.close();
+ return null;
+ } catch (IOException ex) {
+ throw new InternalError(ex);
+ }
+ }
+ });
+ }
+
+ }
+
+ private static String makeDumpableClassName(String className) {
+ Integer ctr;
+ synchronized (DUMP_CLASS_FILES_COUNTERS) {
+ ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
+ if (ctr == null) ctr = 0;
+ DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
+ }
+ String sfx = ctr.toString();
+ while (sfx.length() < 3)
+ sfx = "0"+sfx;
+ className += sfx;
+ return className;
+ }
+
+ class CpPatch {
+ int index;
+ Object value;
+ CpPatch(int index, Object value) {
+ this.index = index;
+ this.value = value;
+ }
+ }
+
+ Map<Object, CpPatch> cpPatches = new HashMap<>();
+
+ int cph = 0; // for counting constant placeholders
+
+ String constantPlaceholder(Object arg) {
+ String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
+ if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid
+ if (cpPatches.containsKey(cpPlaceholder)) {
+ throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
+ }
+ // insert placeholder in CP and remember the patch
+ int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool
+ cpPatches.put(cpPlaceholder, new CpPatch(index, arg));
+ return cpPlaceholder;
+ }
+
+ Object[] cpPatches(byte[] classFile) {
+ int size = getConstantPoolSize(classFile);
+ Object[] res = new Object[size];
+ for (CpPatch p : cpPatches.values()) {
+ res[p.index] = p.value;
+ }
+ return res;
+ }
+
+ /**
+ * Extract the number of constant pool entries from a given class file.
+ *
+ * @param classFile the bytes of the class file in question.
+ * @return the number of entries in the constant pool.
+ */
+ private static int getConstantPoolSize(byte[] classFile) {
+ // The first few bytes:
+ // u4 magic;
+ // u2 minor_version;
+ // u2 major_version;
+ // u2 constant_pool_count;
+ return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
+ }
+
+ /**
+ * Extract the MemberName of a newly-defined method.
+ *
+ * @param classFile
+ * @return
+ */
+ private MemberName loadMethod(byte[] classFile) {
+ Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
+ return resolveInvokerMember(invokerClass, invokerName, invokerType);
+ }
+
+ /**
+ * Define a given class as anonymous class in the runtime system.
+ *
+ * @param classBytes
+ * @param patches
+ * @return
+ */
+ private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
+ Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
+ UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
+ return invokerClass;
+ }
+
+ /**
+ * TODO
+ *
+ * @param invokerClass
+ * @param name
+ * @param type
+ * @return
+ */
+ private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
+ MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
+ //System.out.println("resolveInvokerMember => "+member);
+ //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
+ try {
+ member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ //System.out.println("resolveInvokerMember => "+member);
+ return member;
+ }
+
+ /**
+ * Set up class file generation.
+ */
+ private void classFilePrologue() {
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+ cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
+ cw.visitSource(sourceFile, null);
+
+ String invokerDesc = invokerType.toMethodDescriptorString();
+ mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
+
+ // Force inlining of this invoker method.
+ mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+ }
+
+ /**
+ * Tear down class file generation.
+ */
+ private void classFileEpilogue() {
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /*
+ * Low-level emit helpers.
+ */
+ private void emitConst(Object con) {
+ if (con == null) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ return;
+ }
+ if (con instanceof Integer) {
+ emitIconstInsn((int) con);
+ return;
+ }
+ if (con instanceof Long) {
+ long x = (long) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2L);
+ return;
+ }
+ }
+ if (con instanceof Float) {
+ float x = (float) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2F);
+ return;
+ }
+ }
+ if (con instanceof Double) {
+ double x = (double) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2D);
+ return;
+ }
+ }
+ if (con instanceof Boolean) {
+ emitIconstInsn((boolean) con ? 1 : 0);
+ return;
+ }
+ // fall through:
+ mv.visitLdcInsn(con);
+ }
+
+ private void emitIconstInsn(int i) {
+ int opcode;
+ switch (i) {
+ case 0: opcode = Opcodes.ICONST_0; break;
+ case 1: opcode = Opcodes.ICONST_1; break;
+ case 2: opcode = Opcodes.ICONST_2; break;
+ case 3: opcode = Opcodes.ICONST_3; break;
+ case 4: opcode = Opcodes.ICONST_4; break;
+ case 5: opcode = Opcodes.ICONST_5; break;
+ default:
+ if (i == (byte) i) {
+ mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
+ } else if (i == (short) i) {
+ mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
+ } else {
+ mv.visitLdcInsn(i);
+ }
+ return;
+ }
+ mv.visitInsn(opcode);
+ }
+
+ /*
+ * NOTE: These load/store methods use the localsMap to find the correct index!
+ */
+ private void emitLoadInsn(char type, int index) {
+ int opcode;
+ switch (type) {
+ case 'I': opcode = Opcodes.ILOAD; break;
+ case 'J': opcode = Opcodes.LLOAD; break;
+ case 'F': opcode = Opcodes.FLOAD; break;
+ case 'D': opcode = Opcodes.DLOAD; break;
+ case 'L': opcode = Opcodes.ALOAD; break;
+ default:
+ throw new InternalError("unknown type: " + type);
+ }
+ mv.visitVarInsn(opcode, localsMap[index]);
+ }
+ private void emitAloadInsn(int index) {
+ emitLoadInsn('L', index);
+ }
+
+ private void emitStoreInsn(char type, int index) {
+ int opcode;
+ switch (type) {
+ case 'I': opcode = Opcodes.ISTORE; break;
+ case 'J': opcode = Opcodes.LSTORE; break;
+ case 'F': opcode = Opcodes.FSTORE; break;
+ case 'D': opcode = Opcodes.DSTORE; break;
+ case 'L': opcode = Opcodes.ASTORE; break;
+ default:
+ throw new InternalError("unknown type: " + type);
+ }
+ mv.visitVarInsn(opcode, localsMap[index]);
+ }
+ private void emitAstoreInsn(int index) {
+ emitStoreInsn('L', index);
+ }
+
+ /**
+ * Emit a boxing call.
+ *
+ * @param type primitive type class to box.
+ */
+ private void emitBoxing(Class<?> type) {
+ Wrapper wrapper = Wrapper.forPrimitiveType(type);
+ String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+ String name = "valueOf";
+ String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
+ }
+
+ /**
+ * Emit an unboxing call (plus preceding checkcast).
+ *
+ * @param type wrapper type class to unbox.
+ */
+ private void emitUnboxing(Class<?> type) {
+ Wrapper wrapper = Wrapper.forWrapperType(type);
+ String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+ String name = wrapper.primitiveSimpleName() + "Value";
+ String desc = "()" + wrapper.basicTypeChar();
+ mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
+ }
+
+ /**
+ * Emit an implicit conversion.
+ *
+ * @param ptype type of value present on stack
+ * @param pclass type of value required on stack
+ */
+ private void emitImplicitConversion(char ptype, Class<?> pclass) {
+ switch (ptype) {
+ case 'L':
+ if (VerifyType.isNullConversion(Object.class, pclass))
+ return;
+ if (isStaticallyNameable(pclass)) {
+ mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
+ } else {
+ mv.visitLdcInsn(constantPlaceholder(pclass));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
+ mv.visitInsn(Opcodes.SWAP);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
+ if (pclass.isArray())
+ mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+ }
+ return;
+ case 'I':
+ if (!VerifyType.isNullConversion(int.class, pclass))
+ emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
+ return;
+ case 'J':
+ assert(pclass == long.class);
+ return;
+ case 'F':
+ assert(pclass == float.class);
+ return;
+ case 'D':
+ assert(pclass == double.class);
+ return;
+ }
+ throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+ }
+
+ /**
+ * Emits an actual return instruction conforming to the given return type.
+ */
+ private void emitReturnInsn(Class<?> type) {
+ int opcode;
+ switch (Wrapper.basicTypeChar(type)) {
+ case 'I': opcode = Opcodes.IRETURN; break;
+ case 'J': opcode = Opcodes.LRETURN; break;
+ case 'F': opcode = Opcodes.FRETURN; break;
+ case 'D': opcode = Opcodes.DRETURN; break;
+ case 'L': opcode = Opcodes.ARETURN; break;
+ case 'V': opcode = Opcodes.RETURN; break;
+ default:
+ throw new InternalError("unknown return type: " + type);
+ }
+ mv.visitInsn(opcode);
+ }
+
+ private static String getInternalName(Class<?> c) {
+ assert(VerifyAccess.isTypeVisible(c, Object.class));
+ return c.getName().replace('.', '/');
+ }
+
+ /**
+ * Generate customized bytecode for a given LambdaForm.
+ *
+ * @param form
+ * @param invokerType
+ * @return
+ */
+ static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
+ return g.loadMethod(g.generateCustomizedCodeBytes());
+ }
+
+ /**
+ * Generate an invoker method for the passed {@link LambdaForm}.
+ */
+ private byte[] generateCustomizedCodeBytes() {
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // Mark this method as a compiled LambdaForm
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
+
+ // iterate over the form's names, generating bytecode instructions for each
+ // start iterating at the first name following the arguments
+ for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
+ Name name = lambdaForm.names[i];
+ MemberName member = name.function.member();
+
+ if (isSelectAlternative(member)) {
+ // selectAlternative idiom
+ // FIXME: make sure this idiom is really present!
+ emitSelectAlternative(name, lambdaForm.names[i + 1]);
+ i++; // skip MH.invokeBasic of the selectAlternative result
+ } else if (isStaticallyInvocable(member)) {
+ emitStaticInvoke(member, name);
+ } else {
+ emitInvoke(name);
+ }
+
+ // store the result from evaluating to the target name in a local if required
+ // (if this is the last value, i.e., the one that is going to be returned,
+ // avoid store/load/return and just return)
+ if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
+ // return value - do nothing
+ } else if (name.type != 'V') {
+ // non-void: actually assign
+ emitStoreInsn(name.type, name.index());
+ }
+ }
+
+ // return statement
+ emitReturn();
+
+ classFileEpilogue();
+ bogusMethod(lambdaForm);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Emit an invoke for the given name.
+ *
+ * @param name
+ */
+ void emitInvoke(Name name) {
+ if (true) {
+ // push receiver
+ MethodHandle target = name.function.resolvedHandle;
+ assert(target != null) : name.exprString();
+ mv.visitLdcInsn(constantPlaceholder(target));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+ } else {
+ // load receiver
+ emitAloadInsn(0);
+ mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+ mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
+ mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
+ // TODO more to come
+ }
+
+ // push arguments
+ for (int i = 0; i < name.arguments.length; i++) {
+ emitPushArgument(name, i);
+ }
+
+ // invocation
+ MethodType type = name.function.methodType();
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
+ }
+
+ static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
+ // Sample classes from each package we are willing to bind to statically:
+ java.lang.Object.class,
+ java.util.Arrays.class,
+ sun.misc.Unsafe.class
+ //MethodHandle.class already covered
+ };
+
+ static boolean isStaticallyInvocable(MemberName member) {
+ if (member == null) return false;
+ if (member.isConstructor()) return false;
+ Class<?> cls = member.getDeclaringClass();
+ if (cls.isArray() || cls.isPrimitive())
+ return false; // FIXME
+ if (cls.isAnonymousClass() || cls.isLocalClass())
+ return false; // inner class of some sort
+ if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
+ return false; // not on BCP
+ if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
+ return true; // in java.lang.invoke package
+ if (member.isPublic() && isStaticallyNameable(cls))
+ return true;
+ return false;
+ }
+
+ static boolean isStaticallyNameable(Class<?> cls) {
+ while (cls.isArray())
+ cls = cls.getComponentType();
+ if (cls.isPrimitive())
+ return true; // int[].class, for example
+ if (cls.getClassLoader() != Object.class.getClassLoader())
+ return false;
+ if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
+ return true;
+ if (!Modifier.isPublic(cls.getModifiers()))
+ return false;
+ for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
+ if (VerifyAccess.isSamePackage(pkgcls, cls))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Emit an invoke for the given name, using the MemberName directly.
+ *
+ * @param name
+ */
+ void emitStaticInvoke(MemberName member, Name name) {
+ assert(member.equals(name.function.member()));
+ String cname = getInternalName(member.getDeclaringClass());
+ String mname = member.getName();
+ String mtype;
+ byte refKind = member.getReferenceKind();
+ if (refKind == REF_invokeSpecial) {
+ // in order to pass the verifier, we need to convert this to invokevirtual in all cases
+ assert(member.canBeStaticallyBound()) : member;
+ refKind = REF_invokeVirtual;
+ }
+
+ // push arguments
+ for (int i = 0; i < name.arguments.length; i++) {
+ emitPushArgument(name, i);
+ }
+
+ // invocation
+ if (member.isMethod()) {
+ mtype = member.getMethodType().toMethodDescriptorString();
+ mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
+ } else {
+ mtype = MethodType.toFieldDescriptorString(member.getFieldType());
+ mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
+ }
+ }
+ int refKindOpcode(byte refKind) {
+ switch (refKind) {
+ case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
+ case REF_invokeStatic: return Opcodes.INVOKESTATIC;
+ case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
+ case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
+ case REF_getField: return Opcodes.GETFIELD;
+ case REF_putField: return Opcodes.PUTFIELD;
+ case REF_getStatic: return Opcodes.GETSTATIC;
+ case REF_putStatic: return Opcodes.PUTSTATIC;
+ }
+ throw new InternalError("refKind="+refKind);
+ }
+
+ /**
+ * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
+ *
+ * @param member
+ * @return true if member is a call to MethodHandleImpl.selectAlternative
+ */
+ private boolean isSelectAlternative(MemberName member) {
+ return member != null &&
+ member.getDeclaringClass() == MethodHandleImpl.class &&
+ member.getName().equals("selectAlternative");
+ }
+
+ /**
+ * Emit bytecode for the selectAlternative idiom.
+ *
+ * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
+ *
+ * Lambda(a0:L,a1:I)=>{
+ * t2:I=foo.test(a1:I);
+ * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
+ * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
+ *
+ * @param selectAlternativeName
+ * @param invokeBasicName
+ */
+ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+ MethodType type = selectAlternativeName.function.methodType();
+
+ Name receiver = (Name) invokeBasicName.arguments[0];
+
+ Label L_fallback = new Label();
+ Label L_done = new Label();
+
+ // load test result
+ emitPushArgument(selectAlternativeName, 0);
+ mv.visitInsn(Opcodes.ICONST_1);
+
+ // if_icmpne L_fallback
+ mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
+
+ // invoke selectAlternativeName.arguments[1]
+ MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
+ emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
+ emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
+ emitInvoke(invokeBasicName);
+
+ // goto L_done
+ mv.visitJumpInsn(Opcodes.GOTO, L_done);
+
+ // L_fallback:
+ mv.visitLabel(L_fallback);
+
+ // invoke selectAlternativeName.arguments[2]
+ MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
+ emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
+ emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
+ emitInvoke(invokeBasicName);
+
+ // L_done:
+ mv.visitLabel(L_done);
+ }
+
+ /**
+ *
+ * @param name
+ * @param paramIndex
+ */
+ private void emitPushArgument(Name name, int paramIndex) {
+ Object arg = name.arguments[paramIndex];
+ char ptype = name.function.parameterType(paramIndex);
+ MethodType mtype = name.function.methodType();
+ if (arg instanceof Name) {
+ Name n = (Name) arg;
+ emitLoadInsn(n.type, n.index());
+ emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
+ } else if ((arg == null || arg instanceof String) && ptype == 'L') {
+ emitConst(arg);
+ } else {
+ if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
+ emitConst(arg);
+ } else {
+ mv.visitLdcInsn(constantPlaceholder(arg));
+ emitImplicitConversion('L', mtype.parameterType(paramIndex));
+ }
+ }
+ }
+
+ /**
+ * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
+ */
+ private void emitReturn() {
+ // return statement
+ if (lambdaForm.result == -1) {
+ // void
+ mv.visitInsn(Opcodes.RETURN);
+ } else {
+ LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
+ char rtype = Wrapper.basicTypeChar(invokerType.returnType());
+
+ // put return value on the stack if it is not already there
+ if (lambdaForm.result != lambdaForm.names.length - 1) {
+ emitLoadInsn(rn.type, lambdaForm.result);
+ }
+
+ // potentially generate cast
+ // rtype is the return type of the invoker - generated code must conform to this
+ // rn.type is the type of the result Name in the LF
+ if (rtype != rn.type) {
+ // need cast
+ if (rtype == 'L') {
+ // possibly cast the primitive to the correct type for boxing
+ char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
+ if (boxedType != rn.type) {
+ emitPrimCast(rn.type, boxedType);
+ }
+ // cast primitive to reference ("boxing")
+ emitBoxing(invokerType.returnType());
+ } else {
+ // to-primitive cast
+ if (rn.type != 'L') {
+ // prim-to-prim cast
+ emitPrimCast(rn.type, rtype);
+ } else {
+ // ref-to-prim cast ("unboxing")
+ throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
+ }
+ }
+ }
+
+ // generate actual return statement
+ emitReturnInsn(invokerType.returnType());
+ }
+ }
+
+ /**
+ * Emit a type conversion bytecode casting from "from" to "to".
+ */
+ private void emitPrimCast(char from, char to) {
+ // Here's how.
+ // - indicates forbidden
+ // <-> indicates implicit
+ // to ----> boolean byte short char int long float double
+ // from boolean <-> - - - - - - -
+ // byte - <-> i2s i2c <-> i2l i2f i2d
+ // short - i2b <-> i2c <-> i2l i2f i2d
+ // char - i2b i2s <-> <-> i2l i2f i2d
+ // int - i2b i2s i2c <-> i2l i2f i2d
+ // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
+ // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
+ // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
+ if (from == to) {
+ // no cast required, should be dead code anyway
+ return;
+ }
+ Wrapper wfrom = Wrapper.forBasicType(from);
+ Wrapper wto = Wrapper.forBasicType(to);
+ if (wfrom.isSubwordOrInt()) {
+ // cast from {byte,short,char,int} to anything
+ emitI2X(to);
+ } else {
+ // cast from {long,float,double} to anything
+ if (wto.isSubwordOrInt()) {
+ // cast to {byte,short,char,int}
+ emitX2I(from);
+ if (wto.bitWidth() < 32) {
+ // targets other than int require another conversion
+ emitI2X(to);
+ }
+ } else {
+ // cast to {long,float,double} - this is verbose
+ boolean error = false;
+ switch (from) {
+ case 'J':
+ if (to == 'F') { mv.visitInsn(Opcodes.L2F); }
+ else if (to == 'D') { mv.visitInsn(Opcodes.L2D); }
+ else error = true;
+ break;
+ case 'F':
+ if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
+ else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
+ else error = true;
+ break;
+ case 'D':
+ if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
+ else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
+ else error = true;
+ break;
+ default:
+ error = true;
+ break;
+ }
+ if (error) {
+ throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
+ }
+ }
+ }
+ }
+
+ private void emitI2X(char type) {
+ switch (type) {
+ case 'B': mv.visitInsn(Opcodes.I2B); break;
+ case 'S': mv.visitInsn(Opcodes.I2S); break;
+ case 'C': mv.visitInsn(Opcodes.I2C); break;
+ case 'I': /* naught */ break;
+ case 'J': mv.visitInsn(Opcodes.I2L); break;
+ case 'F': mv.visitInsn(Opcodes.I2F); break;
+ case 'D': mv.visitInsn(Opcodes.I2D); break;
+ case 'Z':
+ // For compatibility with ValueConversions and explicitCastArguments:
+ mv.visitInsn(Opcodes.ICONST_1);
+ mv.visitInsn(Opcodes.IAND);
+ break;
+ default: throw new InternalError("unknown type: " + type);
+ }
+ }
+
+ private void emitX2I(char type) {
+ switch (type) {
+ case 'J': mv.visitInsn(Opcodes.L2I); break;
+ case 'F': mv.visitInsn(Opcodes.F2I); break;
+ case 'D': mv.visitInsn(Opcodes.D2I); break;
+ default: throw new InternalError("unknown type: " + type);
+ }
+ }
+
+ private static String basicTypeCharSignature(String prefix, MethodType type) {
+ StringBuilder buf = new StringBuilder(prefix);
+ for (Class<?> ptype : type.parameterList())
+ buf.append(Wrapper.forBasicType(ptype).basicTypeChar());
+ buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar());
+ return buf.toString();
+ }
+
+ /**
+ * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
+ *
+ * @param sig
+ * @return
+ */
+ static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
+ assert(LambdaForm.isValidSignature(sig));
+ //System.out.println("generateExactInvoker "+sig);
+ // compute method type
+ // first parameter and return type
+ char tret = LambdaForm.signatureReturn(sig);
+ MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
+ // other parameter types
+ int arity = LambdaForm.signatureArity(sig);
+ for (int i = 1; i < arity; i++) {
+ type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
+ }
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
+ return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
+ }
+
+ private byte[] generateLambdaFormInterpreterEntryPointBytes() {
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // create parameter array
+ emitIconstInsn(invokerType.parameterCount());
+ mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
+
+ // fill parameter array
+ for (int i = 0; i < invokerType.parameterCount(); i++) {
+ Class<?> ptype = invokerType.parameterType(i);
+ mv.visitInsn(Opcodes.DUP);
+ emitIconstInsn(i);
+ emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
+ // box if primitive type
+ if (ptype.isPrimitive()) {
+ emitBoxing(ptype);
+ }
+ mv.visitInsn(Opcodes.AASTORE);
+ }
+ // invoke
+ emitAloadInsn(0);
+ mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
+ mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
+
+ // maybe unbox
+ Class<?> rtype = invokerType.returnType();
+ if (rtype.isPrimitive() && rtype != void.class) {
+ emitUnboxing(Wrapper.asWrapperType(rtype));
+ }
+
+ // return statement
+ emitReturnInsn(rtype);
+
+ classFileEpilogue();
+ bogusMethod(invokerType);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Generate bytecode for a NamedFunction invoker.
+ *
+ * @param srcType
+ * @param dstType
+ * @return
+ */
+ static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
+ MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
+ String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
+ return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
+ }
+
+ static int nfi = 0;
+
+ private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
+ MethodType dstType = typeForm.erasedType();
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // Load receiver
+ emitAloadInsn(0);
+
+ // Load arguments from array
+ for (int i = 0; i < dstType.parameterCount(); i++) {
+ emitAloadInsn(1);
+ emitIconstInsn(i);
+ mv.visitInsn(Opcodes.AALOAD);
+
+ // Maybe unbox
+ Class<?> dptype = dstType.parameterType(i);
+ if (dptype.isPrimitive()) {
+ Class<?> sptype = dstType.basicType().wrap().parameterType(i);
+ Wrapper dstWrapper = Wrapper.forBasicType(dptype);
+ Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int
+ emitUnboxing(srcWrapper.wrapperType());
+ emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+ }
+ }
+
+ // Invoke
+ String targetDesc = dstType.basicType().toMethodDescriptorString();
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc);
+
+ // Box primitive types
+ Class<?> rtype = dstType.returnType();
+ if (rtype != void.class && rtype.isPrimitive()) {
+ Wrapper srcWrapper = Wrapper.forBasicType(rtype);
+ Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
+ // boolean casts not allowed
+ emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+ emitBoxing(dstWrapper.primitiveType());
+ }
+
+ // If the return type is void we return a null reference.
+ if (rtype == void.class) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ }
+ emitReturnInsn(Object.class); // NOTE: NamedFunction invokers always return a reference value.
+
+ classFileEpilogue();
+ bogusMethod(dstType);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
+ * for debugging purposes.
+ */
+ private void bogusMethod(Object... os) {
+ if (DUMP_CLASS_FILES) {
+ mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
+ for (Object o : os) {
+ mv.visitLdcInsn(o.toString());
+ mv.visitInsn(Opcodes.POP);
+ }
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ }
+}
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,8 +25,11 @@
package java.lang.invoke;
+import java.util.Arrays;
import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.LambdaForm.*;
/**
* Construction and caching of often-used invokers.
@@ -36,11 +39,15 @@
// exact type (sans leading taget MH) for the outgoing call
private final MethodType targetType;
+ // FIXME: Get rid of the invokers that are not useful.
+
// exact invoker for the outgoing call
private /*lazy*/ MethodHandle exactInvoker;
// erased (partially untyped but with primitives) invoker for the outgoing call
+ // FIXME: get rid of
private /*lazy*/ MethodHandle erasedInvoker;
+ // FIXME: get rid of
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
// general invoker for the outgoing call
@@ -63,14 +70,13 @@
this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
}
- /*non-public*/ static MethodType invokerType(MethodType targetType) {
- return targetType.insertParameterTypes(0, MethodHandle.class);
- }
-
/*non-public*/ MethodHandle exactInvoker() {
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
- invoker = lookupInvoker("invokeExact");
+ MethodType mtype = targetType;
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ assert(checkInvoker(invoker));
exactInvoker = invoker;
return invoker;
}
@@ -78,29 +84,50 @@
/*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
- invoker = lookupInvoker("invoke");
+ MethodType mtype = targetType;
+ prepareForGenericCall(mtype);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ assert(checkInvoker(invoker));
generalInvoker = invoker;
return invoker;
}
- private MethodHandle lookupInvoker(String name) {
- MethodHandle invoker;
- try {
- invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType);
- } catch (ReflectiveOperationException ex) {
- throw new InternalError("JVM cannot find invoker for "+targetType, ex);
- }
- assert(invokerType(targetType) == invoker.type());
- assert(!invoker.isVarargsCollector());
+ /*non-public*/ MethodHandle makeBasicInvoker() {
+ MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType));
+ assert(targetType == targetType.basicType());
+ // Note: This is not cached here. It is cached by the calling MethodTypeForm.
+ assert(checkInvoker(invoker));
return invoker;
}
+ static MemberName invokeBasicMethod(MethodType type) {
+ String name = "invokeBasic";
+ try {
+ //Lookup.findVirtual(MethodHandle.class, name, type);
+ return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type);
+
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError("JVM cannot find invoker for "+type, ex);
+ }
+ }
+
+ private boolean checkInvoker(MethodHandle invoker) {
+ assert(targetType.invokerType().equals(invoker.type()))
+ : java.util.Arrays.asList(targetType, targetType.invokerType(), invoker);
+ assert(invoker.internalMemberName() == null ||
+ invoker.internalMemberName().getMethodType().equals(targetType));
+ assert(!invoker.isVarargsCollector());
+ return true;
+ }
+
+ // FIXME: get rid of
/*non-public*/ MethodHandle erasedInvoker() {
MethodHandle xinvoker = exactInvoker();
MethodHandle invoker = erasedInvoker;
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
- invoker = xinvoker.asType(invokerType(erasedType));
+ invoker = xinvoker.asType(erasedType.invokerType());
erasedInvoker = invoker;
return invoker;
}
@@ -118,7 +145,7 @@
/*non-public*/ MethodHandle varargsInvoker() {
MethodHandle vaInvoker = varargsInvoker;
if (vaInvoker != null) return vaInvoker;
- vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true)));
+ vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
varargsInvoker = vaInvoker;
return vaInvoker;
}
@@ -137,16 +164,18 @@
uninitializedCallSite = invoker;
return invoker;
}
- if (THROW_UCS == null) {
+ invoker = THROW_UCS;
+ if (invoker == null) {
try {
- THROW_UCS = IMPL_LOOKUP
+ THROW_UCS = invoker = IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
- invoker = AdapterMethodHandle.makeRetypeRaw(targetType, THROW_UCS);
+ invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
+ invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
assert(invoker.type().equals(targetType));
uninitializedCallSite = invoker;
return invoker;
@@ -155,4 +184,208 @@
public String toString() {
return "Invokers"+targetType;
}
+
+ private static MethodType fixMethodType(Class<?> callerClass, Object type) {
+ if (type instanceof MethodType)
+ return (MethodType) type;
+ else
+ return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
+ }
+
+ static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+ MethodType mtype = fixMethodType(callerClass, type);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
+ appendixResult[0] = mtype;
+ return lform.vmentry;
+ }
+
+ static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+ MethodType mtype = fixMethodType(callerClass, type);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER);
+ prepareForGenericCall(mtype);
+ appendixResult[0] = mtype;
+ return lform.vmentry;
+ }
+
+ private static LambdaForm invokeForm(MethodType mtype, int which) {
+ mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ boolean isLinker, isGeneric;
+ String debugName;
+ switch (which) {
+ case MethodTypeForm.LF_EX_LINKER: isLinker = true; isGeneric = false; debugName = "invokeExact_MT"; break;
+ case MethodTypeForm.LF_EX_INVOKER: isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
+ case MethodTypeForm.LF_GEN_LINKER: isLinker = true; isGeneric = true; debugName = "invoke_MT"; break;
+ case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
+ default: throw new InternalError();
+ }
+ LambdaForm lform = mtype.form().cachedLambdaForm(which);
+ if (lform != null) return lform;
+ // exactInvokerForm (Object,Object)Object
+ // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+ final int THIS_MH = 0;
+ final int CALL_MH = THIS_MH + (isLinker ? 0 : 1);
+ final int ARG_BASE = CALL_MH + 1;
+ final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ final int INARG_LIMIT = OUTARG_LIMIT + (isLinker ? 1 : 0);
+ int nameCursor = OUTARG_LIMIT;
+ final int MTYPE_ARG = nameCursor++; // might be last in-argument
+ final int CHECK_TYPE = nameCursor++;
+ final int LINKER_CALL = nameCursor++;
+ MethodType invokerFormType = mtype.invokerType();
+ if (isLinker) {
+ invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
+ } else {
+ invokerFormType = invokerFormType.invokerType();
+ }
+ Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+ assert(names.length == nameCursor);
+ if (MTYPE_ARG >= INARG_LIMIT) {
+ assert(names[MTYPE_ARG] == null);
+ names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
+ // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
+ }
+
+ // Make the final call. If isGeneric, then prepend the result of type checking.
+ MethodType outCallType;
+ Object[] outArgs;
+ if (!isGeneric) {
+ names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], names[MTYPE_ARG]);
+ // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
+ outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
+ outCallType = mtype;
+ } else {
+ names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], names[MTYPE_ARG]);
+ // mh.invokeGeneric(a*):R =>
+ // let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
+ // gamh.invokeBasic(mt, mh, a*)
+ final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2;
+ outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+ // prepend arguments:
+ System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+ outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
+ outArgs[PREPEND_MT] = names[MTYPE_ARG];
+ outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
+ }
+ names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
+ lform = new LambdaForm(debugName, INARG_LIMIT, names);
+ if (isLinker)
+ lform.compileToBytecode(); // JVM needs a real methodOop
+ lform = mtype.form().setCachedLambdaForm(which, lform);
+ return lform;
+ }
+
+ /*non-public*/ static
+ WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
+ // FIXME: merge with JVM logic for throwing WMTE
+ return new WrongMethodTypeException("expected "+expected+" but found "+actual);
+ }
+
+ /** Static definition of MethodHandle.invokeExact checking code. */
+ /*non-public*/ static
+ @ForceInline
+ void checkExactType(Object mhObj, Object expectedObj) {
+ MethodHandle mh = (MethodHandle) mhObj;
+ MethodType expected = (MethodType) expectedObj;
+ MethodType actual = mh.type();
+ if (actual != expected)
+ throw newWrongMethodTypeException(expected, actual);
+ }
+
+ /** Static definition of MethodHandle.invokeGeneric checking code. */
+ /*non-public*/ static
+ @ForceInline
+ Object checkGenericType(Object mhObj, Object expectedObj) {
+ MethodHandle mh = (MethodHandle) mhObj;
+ MethodType expected = (MethodType) expectedObj;
+ //MethodType actual = mh.type();
+ MethodHandle gamh = expected.form().genericInvoker;
+ if (gamh != null) return gamh;
+ return prepareForGenericCall(expected);
+ }
+
+ /**
+ * Returns an adapter GA for invoking a MH with type adjustments.
+ * The MethodType of the generic invocation site is prepended to MH
+ * and its arguments as follows:
+ * {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF<A*,R>, MH, A*)}
+ */
+ /*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) {
+ // force any needed adapters to be preconstructed
+ MethodTypeForm form = mtype.form();
+ MethodHandle gamh = form.genericInvoker;
+ if (gamh != null) return gamh;
+ try {
+ // Trigger adapter creation.
+ gamh = InvokeGeneric.generalInvokerOf(form.erasedType);
+ form.genericInvoker = gamh;
+ return gamh;
+ } catch (Exception ex) {
+ throw new InternalError("Exception while resolving inexact invoke", ex);
+ }
+ }
+
+ static MemberName linkToCallSiteMethod(MethodType mtype) {
+ LambdaForm lform = callSiteForm(mtype);
+ return lform.vmentry;
+ }
+
+ private static LambdaForm callSiteForm(MethodType mtype) {
+ mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER);
+ if (lform != null) return lform;
+ // exactInvokerForm (Object,Object)Object
+ // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+ final int ARG_BASE = 0;
+ final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ final int INARG_LIMIT = OUTARG_LIMIT + 1;
+ int nameCursor = OUTARG_LIMIT;
+ final int CSITE_ARG = nameCursor++; // the last in-argument
+ final int CALL_MH = nameCursor++; // result of getTarget
+ final int LINKER_CALL = nameCursor++;
+ MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class);
+ Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+ assert(names.length == nameCursor);
+ assert(names[CSITE_ARG] != null);
+ names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]);
+ // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*)
+ final int PREPEND_MH = 0, PREPEND_COUNT = 1;
+ Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+ // prepend MH argument:
+ System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+ outArgs[PREPEND_MH] = names[CALL_MH];
+ names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
+ lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
+ lform.compileToBytecode(); // JVM needs a real methodOop
+ lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
+ return lform;
+ }
+
+ /** Static definition of MethodHandle.invokeGeneric checking code. */
+ /*non-public*/ static
+ @ForceInline
+ Object getCallSiteTarget(Object site) {
+ return ((CallSite)site).getTarget();
+ }
+
+ // Local constant functions:
+ private static final NamedFunction NF_checkExactType;
+ private static final NamedFunction NF_checkGenericType;
+ private static final NamedFunction NF_getCallSiteTarget;
+ static {
+ try {
+ NF_checkExactType = new NamedFunction(Invokers.class
+ .getDeclaredMethod("checkExactType", Object.class, Object.class));
+ NF_checkGenericType = new NamedFunction(Invokers.class
+ .getDeclaredMethod("checkGenericType", Object.class, Object.class));
+ NF_getCallSiteTarget = new NamedFunction(Invokers.class
+ .getDeclaredMethod("getCallSiteTarget", Object.class));
+ NF_checkExactType.resolve();
+ NF_checkGenericType.resolve();
+ NF_getCallSiteTarget.resolve();
+ // bound
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,1620 @@
+/*
+ * Copyright (c) 2011, 2012, 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.
+ */
+
+package java.lang.invoke;
+
+import java.lang.annotation.*;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+/**
+ * The symbolic, non-executable form of a method handle's invocation semantics.
+ * It consists of a series of names.
+ * The first N (N=arity) names are parameters,
+ * while any remaining names are temporary values.
+ * Each temporary specifies the application of a function to some arguments.
+ * The functions are method handles, while the arguments are mixes of
+ * constant values and local names.
+ * The result of the lambda is defined as one of the names, often the last one.
+ * <p>
+ * Here is an approximate grammar:
+ * <pre>
+ * LambdaForm = "(" ArgName* ")=>{" TempName* Result "}"
+ * ArgName = "a" N ":" T
+ * TempName = "t" N ":" T "=" Function "(" Argument* ");"
+ * Function = ConstantValue
+ * Argument = NameRef | ConstantValue
+ * Result = NameRef | "void"
+ * NameRef = "a" N | "t" N
+ * N = (any whole number)
+ * T = "L" | "I" | "J" | "F" | "D" | "V"
+ * </pre>
+ * Names are numbered consecutively from left to right starting at zero.
+ * (The letters are merely a taste of syntax sugar.)
+ * Thus, the first temporary (if any) is always numbered N (where N=arity).
+ * Every occurrence of a name reference in an argument list must refer to
+ * a name previously defined within the same lambda.
+ * A lambda has a void result if and only if its result index is -1.
+ * If a temporary has the type "V", it cannot be the subject of a NameRef,
+ * even though possesses a number.
+ * Note that all reference types are erased to "L", which stands for {@code Object).
+ * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}.
+ * The other types stand for the usual primitive types.
+ * <p>
+ * Function invocation closely follows the static rules of the Java verifier.
+ * Arguments and return values must exactly match when their "Name" types are
+ * considered.
+ * Conversions are allowed only if they do not change the erased type.
+ * <ul>
+ * <li>L = Object: casts are used freely to convert into and out of reference types
+ * <li>I = int: subword types are forcibly narrowed when passed as arguments (see {@code explicitCastArguments})
+ * <li>J = long: no implicit conversions
+ * <li>F = float: no implicit conversions
+ * <li>D = double: no implicit conversions
+ * <li>V = void: a function result may be void if and only if its Name is of type "V"
+ * </ul>
+ * Although implicit conversions are not allowed, explicit ones can easily be
+ * encoded by using temporary expressions which call type-transformed identity functions.
+ * <p>
+ * Examples:
+ * <pre>
+ * (a0:J)=>{ a0 }
+ * == identity(long)
+ * (a0:I)=>{ t1:V = System.out#println(a0); void }
+ * == System.out#println(int)
+ * (a0:L)=>{ t1:V = System.out#println(a0); a0 }
+ * == identity, with printing side-effect
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ * t3:L = BoundMethodHandle#target(a0);
+ * t4:L = MethodHandle#invoke(t3, t2, a1); t4 }
+ * == general invoker for unary insertArgument combination
+ * (a0:L, a1:L)=>{ t2:L = FilterMethodHandle#filter(a0);
+ * t3:L = MethodHandle#invoke(t2, a1);
+ * t4:L = FilterMethodHandle#target(a0);
+ * t5:L = MethodHandle#invoke(t4, t3); t5 }
+ * == general invoker for unary filterArgument combination
+ * (a0:L, a1:L)=>{ ...(same as previous example)...
+ * t5:L = MethodHandle#invoke(t4, t3, a1); t5 }
+ * == general invoker for unary/unary foldArgument combination
+ * (a0:L, a1:I)=>{ t2:I = identity(long).asType((int)->long)(a1); t2 }
+ * == invoker for identity method handle which performs i2l
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ * t3:L = Class#cast(t2,a1); t3 }
+ * == invoker for identity method handle which performs cast
+ * </pre>
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class LambdaForm {
+ final int arity;
+ final int result;
+ final Name[] names;
+ final String debugName;
+ MemberName vmentry; // low-level behavior, or null if not yet prepared
+ private boolean isCompiled;
+
+ // Caches for common structural transforms:
+ LambdaForm[] bindCache;
+
+ public static final int VOID_RESULT = -1, LAST_RESULT = -2;
+
+ LambdaForm(String debugName,
+ int arity, Name[] names, int result) {
+ assert(namesOK(arity, names));
+ this.arity = arity;
+ this.result = fixResult(result, names);
+ this.names = names.clone();
+ this.debugName = debugName;
+ normalize();
+ }
+
+ LambdaForm(String debugName,
+ int arity, Name[] names) {
+ this(debugName,
+ arity, names, LAST_RESULT);
+ }
+
+ LambdaForm(String debugName,
+ Name[] formals, Name[] temps, Name result) {
+ this(debugName,
+ formals.length, buildNames(formals, temps, result), LAST_RESULT);
+ }
+
+ private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
+ int arity = formals.length;
+ int length = arity + temps.length + (result == null ? 0 : 1);
+ Name[] names = Arrays.copyOf(formals, length);
+ System.arraycopy(temps, 0, names, arity, temps.length);
+ if (result != null)
+ names[length - 1] = result;
+ return names;
+ }
+
+ private LambdaForm(String sig) {
+ // Make a blank lambda form, which returns a constant zero or null.
+ // It is used as a template for managing the invocation of similar forms that are non-empty.
+ // Called only from getPreparedForm.
+ assert(isValidSignature(sig));
+ this.arity = signatureArity(sig);
+ this.result = (signatureReturn(sig) == 'V' ? -1 : arity);
+ this.names = buildEmptyNames(arity, sig);
+ this.debugName = "LF.zero";
+ assert(nameRefsAreLegal());
+ assert(isEmpty());
+ assert(sig.equals(basicTypeSignature()));
+ }
+
+ private static Name[] buildEmptyNames(int arity, String basicTypeSignature) {
+ assert(isValidSignature(basicTypeSignature));
+ int resultPos = arity + 1; // skip '_'
+ if (arity < 0 || basicTypeSignature.length() != resultPos+1)
+ throw new IllegalArgumentException("bad arity for "+basicTypeSignature);
+ int numRes = (basicTypeSignature.charAt(resultPos) == 'V' ? 0 : 1);
+ Name[] names = arguments(numRes, basicTypeSignature.substring(0, arity));
+ for (int i = 0; i < numRes; i++) {
+ names[arity + i] = constantZero(arity + i, basicTypeSignature.charAt(resultPos + i));
+ }
+ return names;
+ }
+
+ private static int fixResult(int result, Name[] names) {
+ if (result >= 0) {
+ if (names[result].type == 'V')
+ return -1;
+ } else if (result == LAST_RESULT) {
+ return names.length - 1;
+ }
+ return result;
+ }
+
+ private static boolean namesOK(int arity, Name[] names) {
+ for (int i = 0; i < names.length; i++) {
+ Name n = names[i];
+ assert(n != null) : "n is null";
+ if (i < arity)
+ assert( n.isParam()) : n + " is not param at " + i;
+ else
+ assert(!n.isParam()) : n + " is param at " + i;
+ }
+ return true;
+ }
+
+ /** Renumber and/or replace params so that they are interned and canonically numbered. */
+ private void normalize() {
+ Name[] oldNames = null;
+ int changesStart = 0;
+ for (int i = 0; i < names.length; i++) {
+ Name n = names[i];
+ if (!n.initIndex(i)) {
+ if (oldNames == null) {
+ oldNames = names.clone();
+ changesStart = i;
+ }
+ names[i] = n.cloneWithIndex(i);
+ }
+ }
+ if (oldNames != null) {
+ int startFixing = arity;
+ if (startFixing <= changesStart)
+ startFixing = changesStart+1;
+ for (int i = startFixing; i < names.length; i++) {
+ Name fixed = names[i].replaceNames(oldNames, names, changesStart, i);
+ names[i] = fixed.newIndex(i);
+ }
+ }
+ assert(nameRefsAreLegal());
+ int maxInterned = Math.min(arity, INTERNED_ARGUMENT_LIMIT);
+ boolean needIntern = false;
+ for (int i = 0; i < maxInterned; i++) {
+ Name n = names[i], n2 = internArgument(n);
+ if (n != n2) {
+ names[i] = n2;
+ needIntern = true;
+ }
+ }
+ if (needIntern) {
+ for (int i = arity; i < names.length; i++) {
+ names[i].internArguments();
+ }
+ assert(nameRefsAreLegal());
+ }
+ }
+
+ /**
+ * Check that all embedded Name references are localizable to this lambda,
+ * and are properly ordered after their corresponding definitions.
+ * <p>
+ * Note that a Name can be local to multiple lambdas, as long as
+ * it possesses the same index in each use site.
+ * This allows Name references to be freely reused to construct
+ * fresh lambdas, without confusion.
+ */
+ private boolean nameRefsAreLegal() {
+ assert(arity >= 0 && arity <= names.length);
+ assert(result >= -1 && result < names.length);
+ // Do all names possess an index consistent with their local definition order?
+ for (int i = 0; i < arity; i++) {
+ Name n = names[i];
+ assert(n.index() == i) : Arrays.asList(n.index(), i);
+ assert(n.isParam());
+ }
+ // Also, do all local name references
+ for (int i = arity; i < names.length; i++) {
+ Name n = names[i];
+ assert(n.index() == i);
+ for (Object arg : n.arguments) {
+ if (arg instanceof Name) {
+ Name n2 = (Name) arg;
+ int i2 = n2.index;
+ assert(0 <= i2 && i2 < names.length) : n.debugString() + ": 0 <= i2 && i2 < names.length: 0 <= " + i2 + " < " + names.length;
+ assert(names[i2] == n2) : Arrays.asList("-1-", i, "-2-", n.debugString(), "-3-", i2, "-4-", n2.debugString(), "-5-", names[i2].debugString(), "-6-", this);
+ assert(i2 < i); // ref must come after def!
+ }
+ }
+ }
+ return true;
+ }
+
+ /** Invoke this form on the given arguments. */
+ // final Object invoke(Object... args) throws Throwable {
+ // // NYI: fit this into the fast path?
+ // return interpretWithArguments(args);
+ // }
+
+ /** Report the return type. */
+ char returnType() {
+ if (result < 0) return 'V';
+ Name n = names[result];
+ return n.type;
+ }
+
+ /** Report the N-th argument type. */
+ char parameterType(int n) {
+ assert(n < arity);
+ return names[n].type;
+ }
+
+ /** Report the arity. */
+ int arity() {
+ return arity;
+ }
+
+ /** Return the method type corresponding to my basic type signature. */
+ MethodType methodType() {
+ return signatureType(basicTypeSignature());
+ }
+ /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
+ final String basicTypeSignature() {
+ StringBuilder buf = new StringBuilder(arity() + 3);
+ for (int i = 0, a = arity(); i < a; i++)
+ buf.append(parameterType(i));
+ return buf.append('_').append(returnType()).toString();
+ }
+ static int signatureArity(String sig) {
+ assert(isValidSignature(sig));
+ return sig.indexOf('_');
+ }
+ static char signatureReturn(String sig) {
+ return sig.charAt(signatureArity(sig)+1);
+ }
+ static boolean isValidSignature(String sig) {
+ int arity = sig.indexOf('_');
+ if (arity < 0) return false; // must be of the form *_*
+ int siglen = sig.length();
+ if (siglen != arity + 2) return false; // *_X
+ for (int i = 0; i < siglen; i++) {
+ if (i == arity) continue; // skip '_'
+ char c = sig.charAt(i);
+ if (c == 'V')
+ return (i == siglen - 1 && arity == siglen - 2);
+ if (ALL_TYPES.indexOf(c) < 0) return false; // must be [LIJFD]
+ }
+ return true; // [LIJFD]*_[LIJFDV]
+ }
+ static Class<?> typeClass(char t) {
+ switch (t) {
+ case 'I': return int.class;
+ case 'J': return long.class;
+ case 'F': return float.class;
+ case 'D': return double.class;
+ case 'L': return Object.class;
+ case 'V': return void.class;
+ default: assert false;
+ }
+ return null;
+ }
+ static MethodType signatureType(String sig) {
+ Class<?>[] ptypes = new Class<?>[signatureArity(sig)];
+ for (int i = 0; i < ptypes.length; i++)
+ ptypes[i] = typeClass(sig.charAt(i));
+ Class<?> rtype = typeClass(signatureReturn(sig));
+ return MethodType.methodType(rtype, ptypes);
+ }
+
+ /*
+ * Code generation issues:
+ *
+ * Compiled LFs should be reusable in general.
+ * The biggest issue is how to decide when to pull a name into
+ * the bytecode, versus loading a reified form from the MH data.
+ *
+ * For example, an asType wrapper may require execution of a cast
+ * after a call to a MH. The target type of the cast can be placed
+ * as a constant in the LF itself. This will force the cast type
+ * to be compiled into the bytecodes and native code for the MH.
+ * Or, the target type of the cast can be erased in the LF, and
+ * loaded from the MH data. (Later on, if the MH as a whole is
+ * inlined, the data will flow into the inlined instance of the LF,
+ * as a constant, and the end result will be an optimal cast.)
+ *
+ * This erasure of cast types can be done with any use of
+ * reference types. It can also be done with whole method
+ * handles. Erasing a method handle might leave behind
+ * LF code that executes correctly for any MH of a given
+ * type, and load the required MH from the enclosing MH's data.
+ * Or, the erasure might even erase the expected MT.
+ *
+ * Also, for direct MHs, the MemberName of the target
+ * could be erased, and loaded from the containing direct MH.
+ * As a simple case, a LF for all int-valued non-static
+ * field getters would perform a cast on its input argument
+ * (to non-constant base type derived from the MemberName)
+ * and load an integer value from the input object
+ * (at a non-constant offset also derived from the MemberName).
+ * Such MN-erased LFs would be inlinable back to optimized
+ * code, whenever a constant enclosing DMH is available
+ * to supply a constant MN from its data.
+ *
+ * The main problem here is to keep LFs reasonably generic,
+ * while ensuring that hot spots will inline good instances.
+ * "Reasonably generic" means that we don't end up with
+ * repeated versions of bytecode or machine code that do
+ * not differ in their optimized form. Repeated versions
+ * of machine would have the undesirable overheads of
+ * (a) redundant compilation work and (b) extra I$ pressure.
+ * To control repeated versions, we need to be ready to
+ * erase details from LFs and move them into MH data,
+ * whevener those details are not relevant to significant
+ * optimization. "Significant" means optimization of
+ * code that is actually hot.
+ *
+ * Achieving this may require dynamic splitting of MHs, by replacing
+ * a generic LF with a more specialized one, on the same MH,
+ * if (a) the MH is frequently executed and (b) the MH cannot
+ * be inlined into a containing caller, such as an invokedynamic.
+ *
+ * Compiled LFs that are no longer used should be GC-able.
+ * If they contain non-BCP references, they should be properly
+ * interlinked with the class loader(s) that their embedded types
+ * depend on. This probably means that reusable compiled LFs
+ * will be tabulated (indexed) on relevant class loaders,
+ * or else that the tables that cache them will have weak links.
+ */
+
+ /**
+ * Make this LF directly executable, as part of a MethodHandle.
+ * Invariant: Every MH which is invoked must prepare its LF
+ * before invocation.
+ * (In principle, the JVM could do this very lazily,
+ * as a sort of pre-invocation linkage step.)
+ */
+ public void prepare() {
+ if (COMPILE_THRESHOLD == 0) {
+ compileToBytecode();
+ }
+ if (this.vmentry != null) {
+ // already prepared (e.g., a primitive DMH invoker form)
+ return;
+ }
+ LambdaForm prep = getPreparedForm(basicTypeSignature());
+ this.vmentry = prep.vmentry;
+ // TO DO: Maybe add invokeGeneric, invokeWithArguments
+ }
+
+ /** Generate optimizable bytecode for this form. */
+ MemberName compileToBytecode() {
+ MethodType invokerType = methodType();
+ assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
+ if (vmentry != null && isCompiled) {
+ return vmentry; // already compiled somehow
+ }
+ try {
+ vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
+ if (TRACE_INTERPRETER)
+ traceInterpreter("compileToBytecode", this);
+ isCompiled = true;
+ return vmentry;
+ } catch (Error | Exception ex) {
+ throw new InternalError(this.toString(), ex);
+ }
+ }
+
+ private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
+ static {
+ int capacity = 512; // expect many distinct signatures over time
+ float loadFactor = 0.75f; // normal default
+ int writers = 1;
+ PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
+ }
+
+ private static Map<String,LambdaForm> computeInitialPreparedForms() {
+ // Find all predefined invokers and associate them with canonical empty lambda forms.
+ HashMap<String,LambdaForm> forms = new HashMap<>();
+ for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
+ if (!m.isStatic() || !m.isPackage()) continue;
+ MethodType mt = m.getMethodType();
+ if (mt.parameterCount() > 0 &&
+ mt.parameterType(0) == MethodHandle.class &&
+ m.getName().startsWith("interpret_")) {
+ String sig = basicTypeSignature(mt);
+ assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
+ LambdaForm form = new LambdaForm(sig);
+ form.vmentry = m;
+ mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
+ // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
+ forms.put(sig, form);
+ }
+ }
+ //System.out.println("computeInitialPreparedForms => "+forms);
+ return forms;
+ }
+
+ // Set this false to disable use of the interpret_L methods defined in this file.
+ private static final boolean USE_PREDEFINED_INTERPRET_METHODS = true;
+
+ // The following are predefined exact invokers. The system must build
+ // a separate invoker for each distinct signature.
+ static Object interpret_L(MethodHandle mh) throws Throwable {
+ Object[] av = {mh};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "L_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ static Object interpret_L(MethodHandle mh, Object x1) throws Throwable {
+ Object[] av = {mh, x1};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "LL_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ static Object interpret_L(MethodHandle mh, Object x1, Object x2) throws Throwable {
+ Object[] av = {mh, x1, x2};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "LLL_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ private static LambdaForm getPreparedForm(String sig) {
+ MethodType mtype = signatureType(sig);
+ //LambdaForm prep = PREPARED_FORMS.get(sig);
+ LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
+ if (prep != null) return prep;
+ assert(isValidSignature(sig));
+ prep = new LambdaForm(sig);
+ prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
+ //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
+ return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
+ }
+
+ // The next few routines are called only from assert expressions
+ // They verify that the built-in invokers process the correct raw data types.
+ private static boolean argumentTypesMatch(String sig, Object[] av) {
+ int arity = signatureArity(sig);
+ assert(av.length == arity) : "av.length == arity: av.length=" + av.length + ", arity=" + arity;
+ assert(av[0] instanceof MethodHandle) : "av[0] not instace of MethodHandle: " + av[0];
+ MethodHandle mh = (MethodHandle) av[0];
+ MethodType mt = mh.type();
+ assert(mt.parameterCount() == arity-1);
+ for (int i = 0; i < av.length; i++) {
+ Class<?> pt = (i == 0 ? MethodHandle.class : mt.parameterType(i-1));
+ assert(valueMatches(sig.charAt(i), pt, av[i]));
+ }
+ return true;
+ }
+ private static boolean valueMatches(char tc, Class<?> type, Object x) {
+ // The following line is needed because (...)void method handles can use non-void invokers
+ if (type == void.class) tc = 'V'; // can drop any kind of value
+ assert tc == basicType(type) : tc + " == basicType(" + type + ")=" + basicType(type);
+ switch (tc) {
+ case 'I': assert checkInt(type, x) : "checkInt(" + type + "," + x +")"; break;
+ case 'J': assert x instanceof Long : "instanceof Long: " + x; break;
+ case 'F': assert x instanceof Float : "instanceof Float: " + x; break;
+ case 'D': assert x instanceof Double : "instanceof Double: " + x; break;
+ case 'L': assert checkRef(type, x) : "checkRef(" + type + "," + x + ")"; break;
+ case 'V': break; // allow anything here; will be dropped
+ default: assert(false);
+ }
+ return true;
+ }
+ private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
+ MethodHandle mh = (MethodHandle) av[0];
+ return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
+ }
+ private static boolean checkInt(Class<?> type, Object x) {
+ assert(x instanceof Integer);
+ if (type == int.class) return true;
+ Wrapper w = Wrapper.forBasicType(type);
+ assert(w.isSubwordOrInt());
+ Object x1 = Wrapper.INT.wrap(w.wrap(x));
+ return x.equals(x1);
+ }
+ private static boolean checkRef(Class<?> type, Object x) {
+ assert(!type.isPrimitive());
+ if (x == null) return true;
+ if (type.isInterface()) return true;
+ return type.isInstance(x);
+ }
+
+ /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
+ private static final int COMPILE_THRESHOLD;
+ static {
+ if (MethodHandleStatics.COMPILE_THRESHOLD != null)
+ COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
+ else
+ COMPILE_THRESHOLD = 30; // default value
+ }
+ private int invocationCounter = 0;
+
+ @Hidden
+ /** Interpretively invoke this form on the given arguments. */
+ Object interpretWithArguments(Object... argumentValues) throws Throwable {
+ if (TRACE_INTERPRETER)
+ return interpretWithArgumentsTracing(argumentValues);
+ if (COMPILE_THRESHOLD != 0 &&
+ invocationCounter < COMPILE_THRESHOLD) {
+ invocationCounter++; // benign race
+ if (invocationCounter >= COMPILE_THRESHOLD) {
+ // Replace vmentry with a bytecode version of this LF.
+ compileToBytecode();
+ }
+ }
+ assert(arityCheck(argumentValues));
+ Object[] values = Arrays.copyOf(argumentValues, names.length);
+ for (int i = argumentValues.length; i < values.length; i++) {
+ values[i] = interpretName(names[i], values);
+ }
+ return (result < 0) ? null : values[result];
+ }
+
+ @Hidden
+ /** Evaluate a single Name within this form, applying its function to its arguments. */
+ Object interpretName(Name name, Object[] values) throws Throwable {
+ if (TRACE_INTERPRETER)
+ traceInterpreter("| interpretName", name.debugString(), (Object[]) null);
+ Object[] arguments = Arrays.copyOf(name.arguments, name.arguments.length, Object[].class);
+ for (int i = 0; i < arguments.length; i++) {
+ Object a = arguments[i];
+ if (a instanceof Name) {
+ int i2 = ((Name)a).index();
+ assert(names[i2] == a);
+ a = values[i2];
+ arguments[i] = a;
+ }
+ }
+ return name.function.invokeWithArguments(arguments);
+ }
+
+ Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
+ traceInterpreter("[ interpretWithArguments", this, argumentValues);
+ if (invocationCounter < COMPILE_THRESHOLD) {
+ int ctr = invocationCounter++; // benign race
+ traceInterpreter("| invocationCounter", ctr);
+ if (invocationCounter >= COMPILE_THRESHOLD) {
+ compileToBytecode();
+ }
+ }
+ Object rval;
+ try {
+ assert(arityCheck(argumentValues));
+ Object[] values = Arrays.copyOf(argumentValues, names.length);
+ for (int i = argumentValues.length; i < values.length; i++) {
+ values[i] = interpretName(names[i], values);
+ }
+ rval = (result < 0) ? null : values[result];
+ } catch (Throwable ex) {
+ traceInterpreter("] throw =>", ex);
+ throw ex;
+ }
+ traceInterpreter("] return =>", rval);
+ return rval;
+ }
+
+ //** This transform is applied (statically) to every name.function. */
+ /*
+ private static MethodHandle eraseSubwordTypes(MethodHandle mh) {
+ MethodType mt = mh.type();
+ if (mt.hasPrimitives()) {
+ mt = mt.changeReturnType(eraseSubwordType(mt.returnType()));
+ for (int i = 0; i < mt.parameterCount(); i++) {
+ mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i)));
+ }
+ mh = MethodHandles.explicitCastArguments(mh, mt);
+ }
+ return mh;
+ }
+ private static Class<?> eraseSubwordType(Class<?> type) {
+ if (!type.isPrimitive()) return type;
+ if (type == int.class) return type;
+ Wrapper w = Wrapper.forPrimitiveType(type);
+ if (w.isSubwordOrInt()) return int.class;
+ return type;
+ }
+ */
+
+ static void traceInterpreter(String event, Object obj, Object... args) {
+ if (!TRACE_INTERPRETER) return;
+ System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : ""));
+ }
+ static void traceInterpreter(String event, Object obj) {
+ traceInterpreter(event, obj, (Object[])null);
+ }
+ private boolean arityCheck(Object[] argumentValues) {
+ assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length";
+ // also check that the leading (receiver) argument is somehow bound to this LF:
+ assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0];
+ assert(((MethodHandle)argumentValues[0]).internalForm() == this);
+ // note: argument #0 could also be an interface wrapper, in the future
+ return true;
+ }
+
+ private boolean isEmpty() {
+ if (result < 0)
+ return (names.length == arity);
+ else if (result == arity && names.length == arity + 1)
+ return names[arity].isConstantZero();
+ else
+ return false;
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder("Lambda(");
+ for (int i = 0; i < names.length; i++) {
+ if (i == arity) buf.append(")=>{");
+ Name n = names[i];
+ if (i >= arity) buf.append("\n ");
+ buf.append(n);
+ if (i < arity) {
+ if (i+1 < arity) buf.append(",");
+ continue;
+ }
+ buf.append("=").append(n.exprString());
+ buf.append(";");
+ }
+ buf.append(result < 0 ? "void" : names[result]).append("}");
+ if (TRACE_INTERPRETER) {
+ // Extra verbosity:
+ buf.append(":").append(basicTypeSignature());
+ buf.append("/").append(vmentry);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Apply immediate binding for a Name in this form indicated by its position relative to the form.
+ * The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not
+ * accepted as valid.
+ */
+ LambdaForm bindImmediate(int pos, char basicType, Object value) {
+ // must be an argument, and the types must match
+ assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value);
+
+ int arity2 = arity - 1;
+ Name[] names2 = new Name[names.length - 1];
+ for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2
+ Name n = names[r];
+ if (n.isParam()) {
+ if (n.index == pos) {
+ // do not copy over the argument that is to be replaced with a literal,
+ // but adjust the write index
+ --w;
+ } else {
+ names2[w] = new Name(w, n.type);
+ }
+ } else {
+ Object[] arguments2 = new Object[n.arguments.length];
+ for (int i = 0; i < n.arguments.length; ++i) {
+ Object arg = n.arguments[i];
+ if (arg instanceof Name) {
+ int ni = ((Name) arg).index;
+ if (ni == pos) {
+ arguments2[i] = value;
+ } else if (ni < pos) {
+ // replacement position not yet passed
+ arguments2[i] = names2[ni];
+ } else {
+ // replacement position passed
+ arguments2[i] = names2[ni - 1];
+ }
+ } else {
+ arguments2[i] = arg;
+ }
+ }
+ names2[w] = new Name(n.function, arguments2);
+ names2[w].initIndex(w);
+ }
+ }
+
+ int result2 = result == -1 ? -1 : result - 1;
+ return new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) {
+ Name name = names[namePos];
+ BoundMethodHandle.SpeciesData newData = oldData.extendWithType(name.type);
+ return bind(name, newData.getterName(names[0], oldData.fieldCount()), oldData, newData);
+ }
+ LambdaForm bind(Name name, Name binding,
+ BoundMethodHandle.SpeciesData oldData,
+ BoundMethodHandle.SpeciesData newData) {
+ int pos = name.index;
+ assert(name.isParam());
+ assert(!binding.isParam());
+ assert(name.type == binding.type);
+ assert(0 <= pos && pos < arity && names[pos] == name);
+ assert(binding.function.memberDeclaringClassOrNull() == newData.clazz);
+ assert(oldData.getters.length == newData.getters.length-1);
+ if (bindCache != null) {
+ LambdaForm form = bindCache[pos];
+ if (form != null) {
+ assert(form.contains(binding)) : "form << " + form + " >> does not contain binding << " + binding + " >>";
+ return form;
+ }
+ } else {
+ bindCache = new LambdaForm[arity];
+ }
+ assert(nameRefsAreLegal());
+ int arity2 = arity-1;
+ Name[] names2 = names.clone();
+ names2[pos] = binding; // we might move this in a moment
+
+ // The newly created LF will run with a different BMH.
+ // Switch over any pre-existing BMH field references to the new BMH class.
+ int firstOldRef = -1;
+ for (int i = 0; i < names2.length; i++) {
+ Name n = names[i];
+ if (n.function != null &&
+ n.function.memberDeclaringClassOrNull() == oldData.clazz) {
+ MethodHandle oldGetter = n.function.resolvedHandle;
+ MethodHandle newGetter = null;
+ for (int j = 0; j < oldData.getters.length; j++) {
+ if (oldGetter == oldData.getters[j])
+ newGetter = newData.getters[j];
+ }
+ if (newGetter != null) {
+ if (firstOldRef < 0) firstOldRef = i;
+ Name n2 = new Name(newGetter, n.arguments);
+ names2[i] = n2;
+ }
+ }
+ }
+
+ // Walk over the new list of names once, in forward order.
+ // Replace references to 'name' with 'binding'.
+ // Replace data structure references to the old BMH species with the new.
+ // This might cause a ripple effect, but it will settle in one pass.
+ assert(firstOldRef < 0 || firstOldRef > pos);
+ for (int i = pos+1; i < names2.length; i++) {
+ if (i <= arity2) continue;
+ names2[i] = names2[i].replaceNames(names, names2, pos, i);
+ }
+
+ // (a0, a1, name=a2, a3, a4) => (a0, a1, a3, a4, binding)
+ int insPos = pos;
+ for (; insPos+1 < names2.length; insPos++) {
+ Name n = names2[insPos+1];
+ if (n.isSiblingBindingBefore(binding)) {
+ names2[insPos] = n;
+ } else {
+ break;
+ }
+ }
+ names2[insPos] = binding;
+
+ // Since we moved some stuff, maybe update the result reference:
+ int result2 = result;
+ if (result2 == pos)
+ result2 = insPos;
+ else if (result2 > pos && result2 <= insPos)
+ result2 -= 1;
+
+ return bindCache[pos] = new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ boolean contains(Name name) {
+ int pos = name.index();
+ if (pos >= 0) {
+ return pos < names.length && name.equals(names[pos]);
+ }
+ for (int i = arity; i < names.length; i++) {
+ if (name.equals(names[i]))
+ return true;
+ }
+ return false;
+ }
+
+ LambdaForm addArguments(int pos, char... types) {
+ assert(pos <= arity);
+ int length = names.length;
+ int inTypes = types.length;
+ Name[] names2 = Arrays.copyOf(names, length + inTypes);
+ int arity2 = arity + inTypes;
+ int result2 = result;
+ if (result2 >= arity)
+ result2 += inTypes;
+ // names array has MH in slot 0; skip it.
+ int argpos = pos + 1;
+ // Note: The LF constructor will rename names2[argpos...].
+ // Make space for new arguments (shift temporaries).
+ System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos);
+ for (int i = 0; i < inTypes; i++) {
+ names2[argpos + i] = new Name(types[i]);
+ }
+ return new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ LambdaForm addArguments(int pos, List<Class<?>> types) {
+ char[] basicTypes = new char[types.size()];
+ for (int i = 0; i < basicTypes.length; i++)
+ basicTypes[i] = basicType(types.get(i));
+ return addArguments(pos, basicTypes);
+ }
+
+ LambdaForm permuteArguments(int skip, int[] reorder, char[] types) {
+ // Note: When inArg = reorder[outArg], outArg is fed by a copy of inArg.
+ // The types are the types of the new (incoming) arguments.
+ int length = names.length;
+ int inTypes = types.length;
+ int outArgs = reorder.length;
+ assert(skip+outArgs == arity);
+ assert(permutedTypesMatch(reorder, types, names, skip));
+ int pos = 0;
+ // skip trivial first part of reordering:
+ while (pos < outArgs && reorder[pos] == pos) pos += 1;
+ Name[] names2 = new Name[length - outArgs + inTypes];
+ System.arraycopy(names, 0, names2, 0, skip+pos);
+ // copy the body:
+ int bodyLength = length - arity;
+ System.arraycopy(names, skip+outArgs, names2, skip+inTypes, bodyLength);
+ int arity2 = names2.length - bodyLength;
+ int result2 = result;
+ if (result2 >= 0) {
+ if (result2 < skip+outArgs) {
+ // return the corresponding inArg
+ result2 = reorder[result2-skip];
+ } else {
+ result2 = result2 - outArgs + inTypes;
+ }
+ }
+ // rework names in the body:
+ for (int j = pos; j < outArgs; j++) {
+ Name n = names[skip+j];
+ int i = reorder[j];
+ // replace names[skip+j] by names2[skip+i]
+ Name n2 = names2[skip+i];
+ if (n2 == null)
+ names2[skip+i] = n2 = new Name(types[i]);
+ else
+ assert(n2.type == types[i]);
+ for (int k = arity2; k < names2.length; k++) {
+ names2[k] = names2[k].replaceName(n, n2);
+ }
+ }
+ // some names are unused, but must be filled in
+ for (int i = skip+pos; i < arity2; i++) {
+ if (names2[i] == null)
+ names2[i] = argument(i, types[i - skip]);
+ }
+ for (int j = arity; j < names.length; j++) {
+ int i = j - arity + arity2;
+ // replace names2[i] by names[j]
+ Name n = names[j];
+ Name n2 = names2[i];
+ if (n != n2) {
+ for (int k = i+1; k < names2.length; k++) {
+ names2[k] = names2[k].replaceName(n, n2);
+ }
+ }
+ }
+ return new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ static boolean permutedTypesMatch(int[] reorder, char[] types, Name[] names, int skip) {
+ int inTypes = types.length;
+ int outArgs = reorder.length;
+ for (int i = 0; i < outArgs; i++) {
+ assert(names[skip+i].isParam());
+ assert(names[skip+i].type == types[reorder[i]]);
+ }
+ return true;
+ }
+
+ static class NamedFunction {
+ final MemberName member;
+ MethodHandle resolvedHandle;
+ MethodHandle invoker;
+
+ NamedFunction(MethodHandle resolvedHandle) {
+ this(resolvedHandle.internalMemberName(), resolvedHandle);
+ }
+ NamedFunction(MemberName member, MethodHandle resolvedHandle) {
+ this.member = member;
+ //resolvedHandle = eraseSubwordTypes(resolvedHandle);
+ this.resolvedHandle = resolvedHandle;
+ }
+
+ // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
+ // Any LambdaForm containing such a member is not interpretable.
+ // This is OK, since all such LFs are prepared with special primitive vmentry points.
+ // And even without the resolvedHandle, the name can still be compiled and optimized.
+ NamedFunction(Method method) {
+ this(new MemberName(method));
+ }
+ NamedFunction(Field field) {
+ this(new MemberName(field));
+ }
+ NamedFunction(MemberName member) {
+ this.member = member;
+ this.resolvedHandle = null;
+ }
+
+ MethodHandle resolvedHandle() {
+ if (resolvedHandle == null) resolve();
+ return resolvedHandle;
+ }
+
+ void resolve() {
+ resolvedHandle = DirectMethodHandle.make(member);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other == null) return false;
+ if (!(other instanceof NamedFunction)) return false;
+ NamedFunction that = (NamedFunction) other;
+ return this.member != null && this.member.equals(that.member);
+ }
+
+ @Override
+ public int hashCode() {
+ if (member != null)
+ return member.hashCode();
+ return super.hashCode();
+ }
+
+ // Put the predefined NamedFunction invokers into the table.
+ static void initializeInvokers() {
+ for (MemberName m : MemberName.getFactory().getMethods(NamedFunction.class, false, null, null, null)) {
+ if (!m.isStatic() || !m.isPackage()) continue;
+ MethodType type = m.getMethodType();
+ if (type.equals(INVOKER_METHOD_TYPE) &&
+ m.getName().startsWith("invoke_")) {
+ String sig = m.getName().substring("invoke_".length());
+ int arity = LambdaForm.signatureArity(sig);
+ MethodType srcType = MethodType.genericMethodType(arity);
+ if (LambdaForm.signatureReturn(sig) == 'V')
+ srcType = srcType.changeReturnType(void.class);
+ MethodTypeForm typeForm = srcType.form();
+ typeForm.namedFunctionInvoker = DirectMethodHandle.make(m);
+ }
+ }
+ }
+
+ // The following are predefined NamedFunction invokers. The system must build
+ // a separate invoker for each distinct signature.
+ /** void return type invokers. */
+ @Hidden
+ static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 0);
+ mh.invokeBasic();
+ return null;
+ }
+ @Hidden
+ static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 1);
+ mh.invokeBasic(a[0]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 2);
+ mh.invokeBasic(a[0], a[1]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 3);
+ mh.invokeBasic(a[0], a[1], a[2]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 4);
+ mh.invokeBasic(a[0], a[1], a[2], a[3]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 5);
+ mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
+ return null;
+ }
+ /** Object return type invokers. */
+ @Hidden
+ static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 0);
+ return mh.invokeBasic();
+ }
+ @Hidden
+ static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 1);
+ return mh.invokeBasic(a[0]);
+ }
+ @Hidden
+ static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 2);
+ return mh.invokeBasic(a[0], a[1]);
+ }
+ @Hidden
+ static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 3);
+ return mh.invokeBasic(a[0], a[1], a[2]);
+ }
+ @Hidden
+ static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 4);
+ return mh.invokeBasic(a[0], a[1], a[2], a[3]);
+ }
+ @Hidden
+ static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 5);
+ return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
+ }
+
+ static final MethodType INVOKER_METHOD_TYPE =
+ MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+
+ private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
+ MethodHandle mh = typeForm.namedFunctionInvoker;
+ if (mh != null) return mh;
+ MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while
+ mh = DirectMethodHandle.make(invoker);
+ MethodHandle mh2 = typeForm.namedFunctionInvoker;
+ if (mh2 != null) return mh2; // benign race
+ if (!mh.type().equals(INVOKER_METHOD_TYPE))
+ throw new InternalError(mh.debugString());
+ return typeForm.namedFunctionInvoker = mh;
+ }
+
+ @Hidden
+ Object invokeWithArguments(Object... arguments) throws Throwable {
+ // If we have a cached invoker, call it right away.
+ // NOTE: The invoker always returns a reference value.
+ if (TRACE_INTERPRETER) return invokeWithArgumentsTracing(arguments);
+ assert(checkArgumentTypes(arguments, methodType()));
+ return invoker().invokeBasic(resolvedHandle(), arguments);
+ }
+
+ @Hidden
+ Object invokeWithArgumentsTracing(Object[] arguments) throws Throwable {
+ Object rval;
+ try {
+ traceInterpreter("[ call", this, arguments);
+ if (invoker == null) {
+ traceInterpreter("| getInvoker", this);
+ invoker();
+ }
+ if (resolvedHandle == null) {
+ traceInterpreter("| resolve", this);
+ resolvedHandle();
+ }
+ assert(checkArgumentTypes(arguments, methodType()));
+ rval = invoker().invokeBasic(resolvedHandle(), arguments);
+ } catch (Throwable ex) {
+ traceInterpreter("] throw =>", ex);
+ throw ex;
+ }
+ traceInterpreter("] return =>", rval);
+ return rval;
+ }
+
+ private MethodHandle invoker() {
+ if (invoker != null) return invoker;
+ // Get an invoker and cache it.
+ return invoker = computeInvoker(methodType().form());
+ }
+
+ private static boolean checkArgumentTypes(Object[] arguments, MethodType methodType) {
+ if (true) return true; // FIXME
+ MethodType dstType = methodType.form().erasedType();
+ MethodType srcType = dstType.basicType().wrap();
+ Class<?>[] ptypes = new Class<?>[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ Object arg = arguments[i];
+ Class<?> ptype = arg == null ? Object.class : arg.getClass();
+ // If the dest. type is a primitive we keep the
+ // argument type.
+ ptypes[i] = dstType.parameterType(i).isPrimitive() ? ptype : Object.class;
+ }
+ MethodType argType = MethodType.methodType(srcType.returnType(), ptypes).wrap();
+ assert(argType.isConvertibleTo(srcType)) : "wrong argument types: cannot convert " + argType + " to " + srcType;
+ return true;
+ }
+
+ String basicTypeSignature() {
+ //return LambdaForm.basicTypeSignature(resolvedHandle.type());
+ return LambdaForm.basicTypeSignature(methodType());
+ }
+
+ MethodType methodType() {
+ if (resolvedHandle != null)
+ return resolvedHandle.type();
+ else
+ // only for certain internal LFs during bootstrapping
+ return member.getInvocationType();
+ }
+
+ MemberName member() {
+ assert(assertMemberIsConsistent());
+ return member;
+ }
+
+ // Called only from assert.
+ private boolean assertMemberIsConsistent() {
+ if (resolvedHandle instanceof DirectMethodHandle) {
+ MemberName m = resolvedHandle.internalMemberName();
+ assert(m.equals(member));
+ }
+ return true;
+ }
+
+ Class<?> memberDeclaringClassOrNull() {
+ return (member == null) ? null : member.getDeclaringClass();
+ }
+
+ char returnType() {
+ return basicType(methodType().returnType());
+ }
+
+ char parameterType(int n) {
+ return basicType(methodType().parameterType(n));
+ }
+
+ int arity() {
+ //int siglen = member.getMethodType().parameterCount();
+ //if (!member.isStatic()) siglen += 1;
+ //return siglen;
+ return methodType().parameterCount();
+ }
+
+ public String toString() {
+ if (member == null) return resolvedHandle.toString();
+ return member.getDeclaringClass().getSimpleName()+"."+member.getName();
+ }
+ }
+
+ void resolve() {
+ for (Name n : names) n.resolve();
+ }
+
+ public static char basicType(Class<?> type) {
+ char c = Wrapper.basicTypeChar(type);
+ if ("ZBSC".indexOf(c) >= 0) c = 'I';
+ assert("LIJFDV".indexOf(c) >= 0);
+ return c;
+ }
+ public static char[] basicTypes(List<Class<?>> types) {
+ char[] btypes = new char[types.size()];
+ for (int i = 0; i < btypes.length; i++) {
+ btypes[i] = basicType(types.get(i));
+ }
+ return btypes;
+ }
+ public static String basicTypeSignature(MethodType type) {
+ char[] sig = new char[type.parameterCount() + 2];
+ int sigp = 0;
+ for (Class<?> pt : type.parameterList()) {
+ sig[sigp++] = basicType(pt);
+ }
+ sig[sigp++] = '_';
+ sig[sigp++] = basicType(type.returnType());
+ assert(sigp == sig.length);
+ return String.valueOf(sig);
+ }
+
+ static final class Name {
+ final char type;
+ private short index;
+ final NamedFunction function;
+ final Object[] arguments;
+
+ private Name(int index, char type, NamedFunction function, Object[] arguments) {
+ this.index = (short)index;
+ this.type = type;
+ this.function = function;
+ this.arguments = arguments;
+ assert(this.index == index);
+ }
+ Name(MethodHandle function, Object... arguments) {
+ this(new NamedFunction(function), arguments);
+ }
+ Name(MemberName function, Object... arguments) {
+ this(new NamedFunction(function), arguments);
+ }
+ Name(NamedFunction function, Object... arguments) {
+ this(-1, function.returnType(), function, arguments = arguments.clone());
+ assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString();
+ for (int i = 0; i < arguments.length; i++)
+ assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString();
+ }
+ Name(int index, char type) {
+ this(index, type, null, null);
+ }
+ Name(char type) {
+ this(-1, type);
+ }
+
+ char type() { return type; }
+ int index() { return index; }
+ boolean initIndex(int i) {
+ if (index != i) {
+ if (index != -1) return false;
+ index = (short)i;
+ }
+ return true;
+ }
+
+
+ void resolve() {
+ if (function != null)
+ function.resolve();
+ }
+
+ Name newIndex(int i) {
+ if (initIndex(i)) return this;
+ return cloneWithIndex(i);
+ }
+ Name cloneWithIndex(int i) {
+ Object[] newArguments = (arguments == null) ? null : arguments.clone();
+ return new Name(i, type, function, newArguments);
+ }
+ Name replaceName(Name oldName, Name newName) { // FIXME: use replaceNames uniformly
+ if (oldName == newName) return this;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ if (arguments == null) return this;
+ boolean replaced = false;
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] == oldName) {
+ if (!replaced) {
+ replaced = true;
+ arguments = arguments.clone();
+ }
+ arguments[j] = newName;
+ }
+ }
+ if (!replaced) return this;
+ return new Name(function, arguments);
+ }
+ Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ boolean replaced = false;
+ eachArg:
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] instanceof Name) {
+ Name n = (Name) arguments[j];
+ int check = n.index;
+ // harmless check to see if the thing is already in newNames:
+ if (check >= 0 && check < newNames.length && n == newNames[check])
+ continue eachArg;
+ // n might not have the correct index: n != oldNames[n.index].
+ for (int i = start; i < end; i++) {
+ if (n == oldNames[i]) {
+ if (n == newNames[i])
+ continue eachArg;
+ if (!replaced) {
+ replaced = true;
+ arguments = arguments.clone();
+ }
+ arguments[j] = newNames[i];
+ continue eachArg;
+ }
+ }
+ }
+ }
+ if (!replaced) return this;
+ return new Name(function, arguments);
+ }
+ void internArguments() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] instanceof Name) {
+ Name n = (Name) arguments[j];
+ if (n.isParam() && n.index < INTERNED_ARGUMENT_LIMIT)
+ arguments[j] = internArgument(n);
+ }
+ }
+ }
+ boolean isParam() {
+ return function == null;
+ }
+ boolean isConstantZero() {
+ return !isParam() && arguments.length == 0 && function.equals(constantZero(0, type).function);
+ }
+
+ public String toString() {
+ return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+type;
+ }
+ public String debugString() {
+ String s = toString();
+ return (function == null) ? s : s + "=" + exprString();
+ }
+ public String exprString() {
+ if (function == null) return "null";
+ StringBuilder buf = new StringBuilder(function.toString());
+ buf.append("(");
+ String cma = "";
+ for (Object a : arguments) {
+ buf.append(cma); cma = ",";
+ if (a instanceof Name || a instanceof Integer)
+ buf.append(a);
+ else
+ buf.append("(").append(a).append(")");
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ private static boolean typesMatch(char parameterType, Object object) {
+ if (object instanceof Name) {
+ return ((Name)object).type == parameterType;
+ }
+ switch (parameterType) {
+ case 'I': return object instanceof Integer;
+ case 'J': return object instanceof Long;
+ case 'F': return object instanceof Float;
+ case 'D': return object instanceof Double;
+ }
+ assert(parameterType == 'L');
+ return true;
+ }
+
+ /**
+ * Does this Name precede the given binding node in some canonical order?
+ * This predicate is used to order data bindings (via insertion sort)
+ * with some stability.
+ * @param binding
+ * @return
+ */
+ boolean isSiblingBindingBefore(Name binding) {
+ assert(!binding.isParam());
+ if (isParam()) return true;
+ if (function.equals(binding.function) &&
+ arguments.length == binding.arguments.length) {
+ boolean sawInt = false;
+ for (int i = 0; i < arguments.length; i++) {
+ Object a1 = arguments[i];
+ Object a2 = binding.arguments[i];
+ if (!a1.equals(a2)) {
+ if (a1 instanceof Integer && a2 instanceof Integer) {
+ if (sawInt) continue;
+ sawInt = true;
+ if ((int)a1 < (int)a2) continue; // still might be true
+ }
+ return false;
+ }
+ }
+ return sawInt;
+ }
+ return false;
+ }
+
+ public boolean equals(Name that) {
+ if (this == that) return true;
+ if (isParam())
+ // each parameter is a unique atom
+ return false; // this != that
+ return
+ //this.index == that.index &&
+ this.type == that.type &&
+ this.function.equals(that.function) &&
+ Arrays.equals(this.arguments, that.arguments);
+ }
+ @Override
+ public boolean equals(Object x) {
+ return x instanceof Name && equals((Name)x);
+ }
+ @Override
+ public int hashCode() {
+ if (isParam())
+ return index | (type << 8);
+ return function.hashCode() ^ Arrays.hashCode(arguments);
+ }
+ }
+
+ static Name argument(int which, char type) {
+ int tn = ALL_TYPES.indexOf(type);
+ if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
+ return new Name(which, type);
+ return INTERNED_ARGUMENTS[tn][which];
+ }
+ static Name internArgument(Name n) {
+ assert(n.isParam()) : "not param: " + n;
+ assert(n.index < INTERNED_ARGUMENT_LIMIT);
+ return argument(n.index, n.type);
+ }
+ static Name[] arguments(int extra, String types) {
+ int length = types.length();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, types.charAt(i));
+ return names;
+ }
+ static Name[] arguments(int extra, char... types) {
+ int length = types.length;
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, types[i]);
+ return names;
+ }
+ static Name[] arguments(int extra, List<Class<?>> types) {
+ int length = types.size();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types.get(i)));
+ return names;
+ }
+ static Name[] arguments(int extra, Class<?>... types) {
+ int length = types.length;
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types[i]));
+ return names;
+ }
+ static Name[] arguments(int extra, MethodType types) {
+ int length = types.parameterCount();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types.parameterType(i)));
+ return names;
+ }
+ static final String ALL_TYPES = "LIJFD"; // omit V, not an argument type
+ static final int INTERNED_ARGUMENT_LIMIT = 10;
+ private static final Name[][] INTERNED_ARGUMENTS
+ = new Name[ALL_TYPES.length()][INTERNED_ARGUMENT_LIMIT];
+ static {
+ for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+ for (int i = 0; i < INTERNED_ARGUMENTS[tn].length; i++) {
+ char type = ALL_TYPES.charAt(tn);
+ INTERNED_ARGUMENTS[tn][i] = new Name(i, type);
+ }
+ }
+ }
+
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+ static Name constantZero(int which, char type) {
+ return CONSTANT_ZERO[ALL_TYPES.indexOf(type)].newIndex(which);
+ }
+ private static final Name[] CONSTANT_ZERO
+ = new Name[ALL_TYPES.length()];
+ static {
+ for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+ char bt = ALL_TYPES.charAt(tn);
+ Wrapper wrap = Wrapper.forBasicType(bt);
+ MemberName zmem = new MemberName(LambdaForm.class, "zero"+bt, MethodType.methodType(wrap.primitiveType()), REF_invokeStatic);
+ try {
+ zmem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zmem, null, NoSuchMethodException.class);
+ } catch (IllegalAccessException|NoSuchMethodException ex) {
+ throw new InternalError(ex);
+ }
+ NamedFunction zcon = new NamedFunction(zmem);
+ Name n = new Name(zcon).newIndex(0);
+ assert(n.type == ALL_TYPES.charAt(tn));
+ CONSTANT_ZERO[tn] = n;
+ assert(n.isConstantZero());
+ }
+ }
+
+ // Avoid appealing to ValueConversions at bootstrap time:
+ private static int zeroI() { return 0; }
+ private static long zeroJ() { return 0; }
+ private static float zeroF() { return 0; }
+ private static double zeroD() { return 0; }
+ private static Object zeroL() { return null; }
+
+ // Put this last, so that previous static inits can run before.
+ static {
+ if (USE_PREDEFINED_INTERPRET_METHODS)
+ PREPARED_FORMS.putAll(computeInitialPreparedForms());
+ }
+
+ /**
+ * Internal marker for byte-compiled LambdaForms.
+ */
+ /*non-public*/
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Compiled {
+ }
+
+ /**
+ * Internal marker for LambdaForm interpreter frames.
+ */
+ /*non-public*/
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Hidden {
+ }
+
+
+/*
+ // Smoke-test for the invokers used in this file.
+ static void testMethodHandleLinkers() throws Throwable {
+ MemberName.Factory lookup = MemberName.getFactory();
+ MemberName asList_MN = new MemberName(Arrays.class, "asList",
+ MethodType.methodType(List.class, Object[].class),
+ REF_invokeStatic);
+ //MethodHandleNatives.resolve(asList_MN, null);
+ asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class);
+ System.out.println("about to call "+asList_MN);
+ Object[] abc = { "a", "bc" };
+ List<?> lst = (List<?>) MethodHandle.linkToStatic(abc, asList_MN);
+ System.out.println("lst="+lst);
+ MemberName toString_MN = new MemberName(Object.class.getMethod("toString"));
+ String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN);
+ toString_MN = new MemberName(Object.class.getMethod("toString"), true);
+ String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN);
+ System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
+ MemberName toArray_MN = new MemberName(List.class.getMethod("toArray"));
+ Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN);
+ System.out.println("toArray="+Arrays.toString(arr));
+ }
+ static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
+ // Requires these definitions in MethodHandle:
+ static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable;
+ static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable;
+ static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable;
+ static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
+ */
+
+ static { NamedFunction.initializeInvokers(); }
+}
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,6 +26,8 @@
package java.lang.invoke;
import sun.invoke.util.BytecodeDescriptor;
+import sun.invoke.util.VerifyAccess;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -38,6 +40,7 @@
import java.util.List;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.Objects;
/**
* A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -71,19 +74,14 @@
private String name; // may be null if not yet materialized
private Object type; // may be null if not yet materialized
private int flags; // modifier bits; see reflect.Modifier
-
- private Object vmtarget; // VM-specific target value
- private int vmindex; // method index within class or interface
-
- { vmindex = VM_INDEX_UNINITIALIZED; }
+ //@Injected JVM_Method* vmtarget;
+ //@Injected int vmindex;
+ private Object resolution; // if null, this guy is resolved
/** Return the declaring class of this member.
* In the case of a bare name and type, the declaring class will be null.
*/
public Class<?> getDeclaringClass() {
- if (clazz == null && isResolved()) {
- expandFromVM();
- }
return clazz;
}
@@ -105,6 +103,16 @@
return name;
}
+ public MethodType getMethodOrFieldType() {
+ if (isInvocable())
+ return getMethodType();
+ if (isGetter())
+ return MethodType.methodType(getFieldType());
+ if (isSetter())
+ return MethodType.methodType(void.class, getFieldType());
+ throw new InternalError("not a method or field: "+this);
+ }
+
/** Return the declared type of this member, which
* must be a method or constructor.
*/
@@ -140,9 +148,11 @@
* a reference to declaring class. For static methods, it is the same as the declared type.
*/
public MethodType getInvocationType() {
- MethodType itype = getMethodType();
+ MethodType itype = getMethodOrFieldType();
+ if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
+ return itype.changeReturnType(clazz);
if (!isStatic())
- itype = itype.insertParameterTypes(0, clazz);
+ return itype.insertParameterTypes(0, clazz);
return itype;
}
@@ -208,9 +218,98 @@
return (flags & RECOGNIZED_MODIFIERS);
}
+ /** Return the reference kind of this member, or zero if none.
+ */
+ public byte getReferenceKind() {
+ return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
+ }
+ private boolean referenceKindIsConsistent() {
+ byte refKind = getReferenceKind();
+ if (refKind == REF_NONE) return isType();
+ if (isField()) {
+ assert(staticIsConsistent());
+ assert(MethodHandleNatives.refKindIsField(refKind));
+ } else if (isConstructor()) {
+ assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
+ } else if (isMethod()) {
+ assert(staticIsConsistent());
+ assert(MethodHandleNatives.refKindIsMethod(refKind));
+ if (clazz.isInterface())
+ assert(refKind == REF_invokeInterface ||
+ refKind == REF_invokeVirtual && isObjectPublicMethod());
+ } else {
+ assert(false);
+ }
+ return true;
+ }
+ private boolean isObjectPublicMethod() {
+ if (clazz == Object.class) return true;
+ MethodType mtype = getMethodType();
+ if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
+ return true;
+ if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
+ return true;
+ if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
+ return true;
+ return false;
+ }
+ /*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
+ int refKind = getReferenceKind();
+ if (refKind == originalRefKind) return true;
+ switch (originalRefKind) {
+ case REF_invokeInterface:
+ // Looking up an interface method, can get (e.g.) Object.hashCode
+ assert(refKind == REF_invokeVirtual ||
+ refKind == REF_invokeSpecial) : this;
+ return true;
+ case REF_invokeVirtual:
+ case REF_newInvokeSpecial:
+ // Looked up a virtual, can get (e.g.) final String.hashCode.
+ assert(refKind == REF_invokeSpecial) : this;
+ return true;
+ }
+ assert(false) : this;
+ return true;
+ }
+ private boolean staticIsConsistent() {
+ byte refKind = getReferenceKind();
+ return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
+ }
+ private boolean vminfoIsConsistent() {
+ byte refKind = getReferenceKind();
+ assert(isResolved()); // else don't call
+ Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
+ assert(vminfo instanceof Object[]);
+ long vmindex = (Long) ((Object[])vminfo)[0];
+ Object vmtarget = ((Object[])vminfo)[1];
+ if (MethodHandleNatives.refKindIsField(refKind)) {
+ assert(vmindex >= 0) : vmindex + ":" + this;
+ assert(vmtarget instanceof Class);
+ } else {
+ if (MethodHandleNatives.refKindDoesDispatch(refKind))
+ assert(vmindex >= 0) : vmindex + ":" + this;
+ else
+ assert(vmindex < 0) : vmindex;
+ assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
+ }
+ return true;
+ }
+
+ private MemberName changeReferenceKind(byte refKind, byte oldKind) {
+ assert(getReferenceKind() == oldKind);
+ assert(MethodHandleNatives.refKindIsValid(refKind));
+ flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
+// if (isConstructor() && refKind != REF_newInvokeSpecial)
+// flags += (IS_METHOD - IS_CONSTRUCTOR);
+// else if (refKind == REF_newInvokeSpecial && isMethod())
+// flags += (IS_CONSTRUCTOR - IS_METHOD);
+ return this;
+ }
+
private void setFlags(int flags) {
this.flags = flags;
assert(testAnyFlags(ALL_KINDS));
+ assert(referenceKindIsConsistent());
}
private boolean testFlags(int mask, int value) {
@@ -223,6 +322,17 @@
return !testFlags(mask, 0);
}
+ /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
+ public boolean isMethodHandleInvoke() {
+ final int bits = Modifier.NATIVE | Modifier.FINAL;
+ final int negs = Modifier.STATIC;
+ if (testFlags(bits | negs, bits) &&
+ clazz == MethodHandle.class) {
+ return name.equals("invoke") || name.equals("invokeExact");
+ }
+ return false;
+ }
+
/** Utility method to query the modifier flags of this member. */
public boolean isStatic() {
return Modifier.isStatic(flags);
@@ -243,10 +353,22 @@
public boolean isFinal() {
return Modifier.isFinal(flags);
}
+ /** Utility method to query whether this member or its defining class is final. */
+ public boolean canBeStaticallyBound() {
+ return Modifier.isFinal(flags | clazz.getModifiers());
+ }
+ /** Utility method to query the modifier flags of this member. */
+ public boolean isVolatile() {
+ return Modifier.isVolatile(flags);
+ }
/** Utility method to query the modifier flags of this member. */
public boolean isAbstract() {
return Modifier.isAbstract(flags);
}
+ /** Utility method to query the modifier flags of this member. */
+ public boolean isNative() {
+ return Modifier.isNative(flags);
+ }
// let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
// unofficial modifier flags, used by HotSpot:
@@ -279,15 +401,12 @@
IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
IS_FIELD = MN_IS_FIELD, // field
IS_TYPE = MN_IS_TYPE; // nested type
- static final int // for MethodHandleNatives.getMembers
- SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
- SEARCH_INTERFACES = MN_SEARCH_INTERFACES;
static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
- static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
+ static final int SEARCH_ALL_SUPERS = MN_SEARCH_SUPERCLASSES | MN_SEARCH_INTERFACES;
/** Utility method to query whether this member is a method or constructor. */
public boolean isInvocable() {
@@ -318,6 +437,12 @@
return !testAnyFlags(ALL_ACCESS);
}
+ /** Utility method to query whether this member is accessible from a given lookup class. */
+ public boolean isAccessibleFrom(Class<?> lookupClass) {
+ return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
+ lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
+ }
+
/** Initialize a query. It is not resolved. */
private void init(Class<?> defClass, String name, Object type, int flags) {
// defining class is allowed to be null (for a naked name/type pair)
@@ -328,7 +453,7 @@
this.name = name;
this.type = type;
setFlags(flags);
- assert(!isResolved());
+ assert(this.resolution == null); // nobody should have touched this yet
}
private void expandFromVM() {
@@ -339,39 +464,94 @@
}
// Capturing information from the Core Reflection API:
- private static int flagsMods(int flags, int mods) {
+ private static int flagsMods(int flags, int mods, byte refKind) {
assert((flags & RECOGNIZED_MODIFIERS) == 0);
assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
- return flags | mods;
+ assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0);
+ return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT);
}
/** Create a name for the given reflected method. The resulting name will be in a resolved state. */
public MemberName(Method m) {
- Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
- init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
+ this(m, false);
+ }
+ @SuppressWarnings("LeakingThisInConstructor")
+ public MemberName(Method m, boolean wantSpecial) {
+ m.getClass(); // NPE check
// fill in vmtarget, vmindex while we have m in hand:
MethodHandleNatives.init(this, m);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = m.getName();
+ if (this.type == null)
+ this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
+ if (wantSpecial) {
+ if (getReferenceKind() == REF_invokeVirtual)
+ changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+ }
+ }
+ public MemberName asSpecial() {
+ switch (getReferenceKind()) {
+ case REF_invokeSpecial: return this;
+ case REF_invokeVirtual: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+ case REF_newInvokeSpecial: return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
+ }
+ throw new IllegalArgumentException(this.toString());
+ }
+ public MemberName asConstructor() {
+ switch (getReferenceKind()) {
+ case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
+ case REF_newInvokeSpecial: return this;
+ }
+ throw new IllegalArgumentException(this.toString());
}
/** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */
+ @SuppressWarnings("LeakingThisInConstructor")
public MemberName(Constructor<?> ctor) {
- Object[] typeInfo = { void.class, ctor.getParameterTypes() };
- init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
+ ctor.getClass(); // NPE check
// fill in vmtarget, vmindex while we have ctor in hand:
MethodHandleNatives.init(this, ctor);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = CONSTRUCTOR_NAME;
+ if (this.type == null)
+ this.type = new Object[] { void.class, ctor.getParameterTypes() };
}
- /** Create a name for the given reflected field. The resulting name will be in a resolved state. */
+ /** Create a name for the given reflected field. The resulting name will be in a resolved state.
+ */
public MemberName(Field fld) {
- init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
+ this(fld, false);
+ }
+ @SuppressWarnings("LeakingThisInConstructor")
+ public MemberName(Field fld, boolean makeSetter) {
+ fld.getClass(); // NPE check
// fill in vmtarget, vmindex while we have fld in hand:
MethodHandleNatives.init(this, fld);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = fld.getName();
+ this.type = fld.getType();
+ assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+ byte refKind = this.getReferenceKind();
+ assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
+ if (makeSetter) {
+ changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
+ }
+ }
+ public boolean isGetter() {
+ return MethodHandleNatives.refKindIsGetter(getReferenceKind());
+ }
+ public boolean isSetter() {
+ return MethodHandleNatives.refKindIsSetter(getReferenceKind());
+ }
+ public MemberName asSetter() {
+ byte refKind = getReferenceKind();
+ assert(MethodHandleNatives.refKindIsGetter(refKind));
+ assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+ byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField));
+ return clone().changeReferenceKind(setterRefKind, refKind);
}
/** Create a name for the given class. The resulting name will be in a resolved state. */
public MemberName(Class<?> type) {
- init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
- vmindex = 0; // isResolved
- assert(isResolved());
+ init(type.getDeclaringClass(), type.getSimpleName(), type,
+ flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
+ initResolved(true);
}
// bare-bones constructor; the JVM will fill it in
@@ -386,41 +566,89 @@
}
}
- // %%% define equals/hashcode?
+ /** Get the definition of this member name.
+ * This may be in a super-class of the declaring class of this member.
+ */
+ public MemberName getDefinition() {
+ if (!isResolved()) throw new IllegalStateException("must be resolved: "+this);
+ if (isType()) return this;
+ MemberName res = this.clone();
+ res.clazz = null;
+ res.type = null;
+ res.name = null;
+ res.resolution = res;
+ res.expandFromVM();
+ assert(res.getName().equals(this.getName()));
+ return res;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(clazz, flags, name, getType());
+ }
+ @Override
+ public boolean equals(Object that) {
+ return (that instanceof MemberName && this.equals((MemberName)that));
+ }
+
+ /** Decide if two member names have exactly the same symbolic content.
+ * Does not take into account any actual class members, so even if
+ * two member names resolve to the same actual member, they may
+ * be distinct references.
+ */
+ public boolean equals(MemberName that) {
+ if (this == that) return true;
+ if (that == null) return false;
+ return this.clazz == that.clazz
+ && this.flags == that.flags
+ && Objects.equals(this.name, that.name)
+ && Objects.equals(this.getType(), that.getType());
+ }
// Construction from symbolic parts, for queries:
- /** Create a field or type name from the given components: Declaring class, name, type, modifiers.
+ /** Create a field or type name from the given components: Declaring class, name, type, reference kind.
* The declaring class may be supplied as null if this is to be a bare name and type.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
- init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
+ public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
+ init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
+ initResolved(false);
}
/** Create a field or type name from the given components: Declaring class, name, type.
* The declaring class may be supplied as null if this is to be a bare name and type.
* The modifier flags default to zero.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, Class<?> type) {
- this(defClass, name, type, 0);
+ public MemberName(Class<?> defClass, String name, Class<?> type, Void unused) {
+ this(defClass, name, type, REF_NONE);
+ initResolved(false);
}
/** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
* It will be a constructor if and only if the name is {@code "<init>"}.
* The declaring class may be supplied as null if this is to be a bare name and type.
+ * The last argument is optional, a boolean which requests REF_invokeSpecial.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
- int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
- init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
+ public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+ init(defClass, name, type, flagsMods(flags, 0, refKind));
+ initResolved(false);
}
- /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
- * It will be a constructor if and only if the name is {@code "<init>"}.
- * The declaring class may be supplied as null if this is to be a bare name and type.
- * The modifier flags default to zero.
- * The resulting name will in an unresolved state.
+// /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
+// * It will be a constructor if and only if the name is {@code "<init>"}.
+// * The declaring class may be supplied as null if this is to be a bare name and type.
+// * The modifier flags default to zero.
+// * The resulting name will in an unresolved state.
+// */
+// public MemberName(Class<?> defClass, String name, MethodType type, Void unused) {
+// this(defClass, name, type, REF_NONE);
+// }
+
+ /** Query whether this member name is resolved to a non-static, non-final method.
*/
- public MemberName(Class<?> defClass, String name, MethodType type) {
- this(defClass, name, type, 0);
+ public boolean hasReceiverTypeDispatch() {
+ return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
}
/** Query whether this member name is resolved.
@@ -429,15 +657,38 @@
* (Document?)
*/
public boolean isResolved() {
- return (vmindex != VM_INDEX_UNINITIALIZED);
+ return resolution == null;
+ }
+
+ private void initResolved(boolean isResolved) {
+ assert(this.resolution == null); // not initialized yet!
+ if (!isResolved)
+ this.resolution = this;
+ assert(isResolved() == isResolved);
}
- /** Query whether this member name is resolved to a non-static, non-final method.
- */
- public boolean hasReceiverTypeDispatch() {
- return (isMethod() && getVMIndex() >= 0);
+ void checkForTypeAlias() {
+ if (isInvocable()) {
+ MethodType type;
+ if (this.type instanceof MethodType)
+ type = (MethodType) this.type;
+ else
+ this.type = type = getMethodType();
+ if (type.erase() == type) return;
+ if (VerifyAccess.isTypeVisible(type, clazz)) return;
+ throw new LinkageError("bad method type alias: "+type+" not visible from "+clazz);
+ } else {
+ Class<?> type;
+ if (this.type instanceof Class<?>)
+ type = (Class<?>) this.type;
+ else
+ this.type = type = getFieldType();
+ if (VerifyAccess.isTypeVisible(type, clazz)) return;
+ throw new LinkageError("bad field type alias: "+type+" not visible from "+clazz);
+ }
}
+
/** Produce a string form of this member name.
* For types, it is simply the type's own string (as reported by {@code toString}).
* For fields, it is {@code "DeclaringClass.name/type"}.
@@ -445,6 +696,7 @@
* If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
* If the member is unresolved, a prefix {@code "*."} is prepended.
*/
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
@Override
public String toString() {
if (isType())
@@ -464,22 +716,12 @@
} else {
buf.append(type == null ? "(*)*" : getName(type));
}
- /*
- buf.append('/');
- // key: Public, private, pRotected, sTatic, Final, sYnchronized,
- // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
- // (annotation), Enum, (unused)
- final String FIELD_MOD_CHARS = "PprTF?vt????Y?E?";
- final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
- String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
- for (int i = 0; i < modChars.length(); i++) {
- if ((flags & (1 << i)) != 0) {
- char mc = modChars.charAt(i);
- if (mc != '?')
- buf.append(mc);
- }
+ byte refKind = getReferenceKind();
+ if (refKind != REF_NONE) {
+ buf.append('/');
+ buf.append(MethodHandleNatives.refKindName(refKind));
}
- */
+ //buf.append("#").append(System.identityHashCode(this));
return buf.toString();
}
private static String getName(Object obj) {
@@ -488,19 +730,6 @@
return String.valueOf(obj);
}
- // Queries to the JVM:
- /** Document? */
- /*non-public*/ int getVMIndex() {
- if (!isResolved())
- throw newIllegalStateException("not resolved", this);
- return vmindex;
- }
-// /*non-public*/ Object getVMTarget() {
-// if (!isResolved())
-// throw newIllegalStateException("not resolved", this);
-// return vmtarget;
-// }
-
public IllegalAccessException makeAccessException(String message, Object from) {
message = message + ": "+ toString();
if (from != null) message += ", from " + from;
@@ -518,14 +747,19 @@
}
public ReflectiveOperationException makeAccessException() {
String message = message() + ": "+ toString();
- if (isResolved())
- return new IllegalAccessException(message);
+ ReflectiveOperationException ex;
+ if (isResolved() || !(resolution instanceof NoSuchMethodError ||
+ resolution instanceof NoSuchFieldError))
+ ex = new IllegalAccessException(message);
else if (isConstructor())
- return new NoSuchMethodException(message);
+ ex = new NoSuchMethodException(message);
else if (isMethod())
- return new NoSuchMethodException(message);
+ ex = new NoSuchMethodException(message);
else
- return new NoSuchFieldException(message);
+ ex = new NoSuchFieldException(message);
+ if (resolution instanceof Throwable)
+ ex.initCause((Throwable) resolution);
+ return ex;
}
/** Actually making a query requires an access check. */
@@ -539,7 +773,7 @@
private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory();
- private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
+ private static int ALLOWED_FLAGS = ALL_KINDS;
/// Queries
List<MemberName> getMembers(Class<?> defc,
@@ -573,14 +807,14 @@
// JVM returned to us with an intentional overflow!
totalCount += buf.length;
int excess = bufCount - buf.length;
- if (bufs == null) bufs = new ArrayList<MemberName[]>(1);
+ if (bufs == null) bufs = new ArrayList<>(1);
bufs.add(buf);
int len2 = buf.length;
len2 = Math.max(len2, excess);
len2 = Math.max(len2, totalCount / 4);
buf = newMemberBuffer(Math.min(BUF_MAX, len2));
}
- ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
+ ArrayList<MemberName> result = new ArrayList<>(totalCount);
if (bufs != null) {
for (MemberName[] buf0 : bufs) {
Collections.addAll(result, buf0);
@@ -599,43 +833,29 @@
}
return result;
}
- boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
- if (m.name == null || m.type == null) { // find unique non-overloaded name
- Class<?> defc = m.getDeclaringClass();
- List<MemberName> choices = null;
- if (m.isMethod())
- choices = getMethods(defc, searchSupers, m.name, (MethodType) m.type, lookupClass);
- else if (m.isConstructor())
- choices = getConstructors(defc, lookupClass);
- else if (m.isField())
- choices = getFields(defc, searchSupers, m.name, (Class<?>) m.type, lookupClass);
- //System.out.println("resolving "+m+" to "+choices);
- if (choices == null || choices.size() != 1)
- return false;
- if (m.name == null) m.name = choices.get(0).name;
- if (m.type == null) m.type = choices.get(0).type;
- }
- MethodHandleNatives.resolve(m, lookupClass);
- if (m.isResolved()) return true;
- int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
- String matchSig = m.getSignature();
- MemberName[] buf = { m };
- int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
- m.getName(), matchSig, matchFlags, lookupClass, 0, buf);
- if (n != 1) return false;
- return m.isResolved();
- }
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
* If lookup fails or access is not permitted, null is returned.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/
- public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) {
- MemberName result = m.clone();
- if (resolveInPlace(result, searchSupers, lookupClass))
- return result;
- return null;
+ private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass) {
+ MemberName m = ref.clone(); // JVM will side-effect the ref
+ assert(refKind == m.getReferenceKind());
+ try {
+ m = MethodHandleNatives.resolve(m, lookupClass);
+ m.checkForTypeAlias();
+ m.resolution = null;
+ } catch (LinkageError ex) {
+ // JVM reports that the "bytecode behavior" would get an error
+ assert(!m.isResolved());
+ m.resolution = ex;
+ return m;
+ }
+ assert(m.referenceKindIsConsistent());
+ m.initResolved(true);
+ assert(m.vminfoIsConsistent());
+ return m;
}
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
@@ -645,16 +865,29 @@
*/
public
<NoSuchMemberException extends ReflectiveOperationException>
- MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+ MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
Class<NoSuchMemberException> nsmClass)
throws IllegalAccessException, NoSuchMemberException {
- MemberName result = resolveOrNull(m, searchSupers, lookupClass);
- if (result != null)
+ MemberName result = resolve(refKind, m, lookupClass);
+ if (result.isResolved())
return result;
- ReflectiveOperationException ex = m.makeAccessException();
+ ReflectiveOperationException ex = result.makeAccessException();
if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
throw nsmClass.cast(ex);
}
+ /** Produce a resolved version of the given member.
+ * Super types are searched (for inherited members) if {@code searchSupers} is true.
+ * Access checking is performed on behalf of the given {@code lookupClass}.
+ * If lookup fails or access is not permitted, return null.
+ * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
+ */
+ public
+ MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass) {
+ MemberName result = resolve(refKind, m, lookupClass);
+ if (result.isResolved())
+ return result;
+ return null;
+ }
/** Return a list of all methods defined by the given class.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,9 +26,13 @@
package java.lang.invoke;
-import java.util.ArrayList;
-import sun.invoke.util.ValueConversions;
+import java.util.*;
+import sun.invoke.util.*;
+import sun.misc.Unsafe;
+
import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* A method handle is a typed, directly executable reference to an underlying method,
@@ -208,8 +212,8 @@
* refers directly to an associated {@code CONSTANT_Methodref},
* {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
* constant pool entry.
- * (For more details on method handle constants,
- * see the <a href="package-summary.html#mhcon">package summary</a>.)
+ * (For full details on method handle constants,
+ * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
* <p>
* Method handles produced by lookups or constant loads from methods or
* constructors with the variable arity modifier bit ({@code 0x0080})
@@ -224,6 +228,19 @@
* (E.g., if a non-static method handle is obtained via {@code ldc},
* the type of the receiver is the class named in the constant pool entry.)
* <p>
+ * Method handle constants are subject to the same link-time access checks
+ * their corresponding bytecode instructions, and the {@code ldc} instruction
+ * will throw corresponding linkage errors if the bytecode behaviors would
+ * throw such errors.
+ * <p>
+ * As a corollary of this, access to protected members is restricted
+ * to receivers only of the accessing class, or one of its subclasses,
+ * and the accessing class must in turn be a subclass (or package sibling)
+ * of the protected member's defining class.
+ * If a method reference refers to a protected non-static method or field
+ * of a class outside the current package, the receiver argument will
+ * be narrowed to the type of the accessing class.
+ * <p>
* When a method handle to a virtual method is invoked, the method is
* always looked up in the receiver (that is, the first argument).
* <p>
@@ -390,39 +407,8 @@
* @author John Rose, JSR 292 EG
*/
public abstract class MethodHandle {
- // { JVM internals:
-
- private byte vmentry; // adapter stub or method entry point
- //private int vmslots; // optionally, hoist type.form.vmslots
- /*non-public*/ Object vmtarget; // VM-specific, class-specific target value
-
- // TO DO: vmtarget should be invisible to Java, since the JVM puts internal
- // managed pointers into it. Making it visible exposes it to debuggers,
- // which can cause errors when they treat the pointer as an Object.
-
- // These two dummy fields are present to force 'I' and 'J' signatures
- // into this class's constant pool, so they can be transferred
- // to vmentry when this class is loaded.
- static final int INT_FIELD = 0;
- static final long LONG_FIELD = 0;
-
- // vmentry (a void* field) is used *only* by the JVM.
- // The JVM adjusts its type to int or long depending on system wordsize.
- // Since it is statically typed as neither int nor long, it is impossible
- // to use this field from Java bytecode. (Please don't try to, either.)
-
- // The vmentry is an assembly-language stub which is jumped to
- // immediately after the method type is verified.
- // For a direct MH, this stub loads the vmtarget's entry point
- // and jumps to it.
-
- // } End of JVM internals.
-
static { MethodHandleImpl.initStatics(); }
- // interface MethodHandle<R throws X extends Exception,A...>
- // { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
-
/**
* Internal marker interface which distinguishes (to the Java compiler)
* those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
@@ -431,7 +417,9 @@
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@interface PolymorphicSignature { }
- private MethodType type;
+ private final MethodType type;
+ /*private*/ final LambdaForm form;
+ // form is not private so that invokers can easily fetch it
/**
* Reports the type of this method handle.
@@ -448,9 +436,13 @@
* the {@code java.lang.invoke} package.
*/
// @param type type (permanently assigned) of the new method handle
- /*non-public*/ MethodHandle(MethodType type) {
- type.getClass(); // elicit NPE
+ /*non-public*/ MethodHandle(MethodType type, LambdaForm form) {
+ type.getClass(); // explicit NPE
+ form.getClass(); // explicit NPE
this.type = type;
+ this.form = form;
+
+ form.prepare(); // TO DO: Try to delay this step until just before invocation.
}
/**
@@ -506,6 +498,46 @@
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
/**
+ * Private method for trusted invocation of a method handle respecting simplified signatures.
+ * Type mismatches will not throw {@code WrongMethodTypeException}, but could crash the JVM.
+ * <p>
+ * The caller signature is restricted to the following basic types:
+ * Object, int, long, float, double, and void return.
+ * <p>
+ * The caller is responsible for maintaining type correctness by ensuring
+ * that the each outgoing argument value is a member of the range of the corresponding
+ * callee argument type.
+ * (The caller should therefore issue appropriate casts and integer narrowing
+ * operations on outgoing argument values.)
+ * The caller can assume that the incoming result value is part of the range
+ * of the callee's return type.
+ */
+ /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
+
+ /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeStatic}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeSpecial}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeInterface}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
+
+ /**
* Performs a variable arity invocation, passing the arguments in the given array
* 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
@@ -557,6 +589,7 @@
*/
public Object invokeWithArguments(Object... arguments) throws Throwable {
int argc = arguments == null ? 0 : arguments.length;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
MethodType type = type();
if (type.parameterCount() != argc || isVarargsCollector()) {
// simulate invoke
@@ -690,7 +723,7 @@
if (!type.isConvertibleTo(newType)) {
throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
}
- return MethodHandleImpl.convertArguments(this, newType, 1);
+ return convertArguments(newType);
}
/**
@@ -772,7 +805,8 @@
*/
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
asSpreaderChecks(arrayType, arrayLength);
- return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
+ int spreadArgPos = type.parameterCount() - arrayLength;
+ return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
}
private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
@@ -790,7 +824,7 @@
}
}
if (sawProblem) {
- ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>(type().parameterList());
+ ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
for (int i = nargs - arrayLength; i < nargs; i++) {
ptypes.set(i, arrayElement);
}
@@ -885,8 +919,12 @@
*/
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
asCollectorChecks(arrayType, arrayLength);
+ int collectArgPos = type().parameterCount()-1;
+ MethodHandle target = this;
+ if (arrayType != type().parameterType(collectArgPos))
+ target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
- return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
+ return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
}
// private API: return true if last param exactly matches arrayType
@@ -1056,7 +1094,7 @@
boolean lastMatch = asCollectorChecks(arrayType, 0);
if (isVarargsCollector() && lastMatch)
return this;
- return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
+ return MethodHandleImpl.makeVarargsCollector(this, arrayType);
}
/**
@@ -1155,14 +1193,13 @@
*/
public MethodHandle bindTo(Object x) {
Class<?> ptype;
- if (type().parameterCount() == 0 ||
- (ptype = type().parameterType(0)).isPrimitive())
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ MethodType type = type();
+ if (type.parameterCount() == 0 ||
+ (ptype = type.parameterType(0)).isPrimitive())
throw newIllegalArgumentException("no leading reference parameter", x);
- x = MethodHandles.checkValue(ptype, x);
- // Cf. MethodHandles.insertArguments for the following logic:
- MethodHandle bmh = MethodHandleImpl.bindReceiver(this, x);
- if (bmh != null) return bmh;
- return MethodHandleImpl.bindArgument(this, 0, x);
+ x = ptype.cast(x); // throw CCE if needed
+ return bindReceiver(x);
}
/**
@@ -1183,11 +1220,178 @@
@Override
public String toString() {
if (DEBUG_METHOD_HANDLE_NAMES) return debugString();
+ return standardString();
+ }
+ String standardString() {
return "MethodHandle"+type;
}
+ String debugString() {
+ return standardString()+"="+internalForm()+internalValues();
+ }
+
+ //// Implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+
+ // Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch
+
+ /*non-public*/
+ MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
+ if (!member.isVarargs()) return this;
+ int argc = type().parameterCount();
+ if (argc != 0) {
+ Class<?> arrayType = type().parameterType(argc-1);
+ if (arrayType.isArray()) {
+ return MethodHandleImpl.makeVarargsCollector(this, arrayType);
+ }
+ }
+ throw member.makeAccessException("cannot make variable arity", null);
+ }
+ /*non-public*/
+ MethodHandle viewAsType(MethodType newType) {
+ // No actual conversions, just a new view of the same method.
+ if (!type.isViewableAs(newType))
+ throw new InternalError();
+ return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
+ }
+
+ // Decoding
+
+ /*non-public*/
+ LambdaForm internalForm() {
+ return form;
+ }
+
+ /*non-public*/
+ MemberName internalMemberName() {
+ return null; // DMH returns DMH.member
+ }
+
+ /*non-public*/
+ Object internalValues() {
+ return "";
+ }
+
+ //// Method handle implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+
+ /*non-public*/ MethodHandle convertArguments(MethodType newType) {
+ // Override this if it can be improved.
+ return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
+ }
+
+ /*non-public*/
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ // Override this if it can be improved.
+ return rebind().bindArgument(pos, basicType, value);
+ }
+
+ /*non-public*/
+ MethodHandle bindReceiver(Object receiver) {
+ // Override this if it can be improved.
+ return bindArgument(0, 'L', receiver);
+ }
+
+ /*non-public*/
+ MethodHandle bindImmediate(int pos, char basicType, Object value) {
+ // Bind an immediate value to a position in the arguments.
+ // This means, elide the respective argument,
+ // and replace all references to it in NamedFunction args with the specified value.
+
+ // CURRENT RESTRICTIONS
+ // * only for pos 0 and UNSAFE (position is adjusted in MHImpl to make API usable for others)
+ assert pos == 0 && basicType == 'L' && value instanceof Unsafe;
+ MethodType type2 = type.dropParameterTypes(pos, pos + 1); // adjustment: ignore receiver!
+ LambdaForm form2 = form.bindImmediate(pos + 1, basicType, value); // adjust pos to form-relative pos
+ return copyWith(type2, form2);
+ }
/*non-public*/
- String debugString() {
- return getNameString(this);
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ throw new InternalError("copyWith: " + this.getClass());
+ }
+
+ /*non-public*/
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ // Override this if it can be improved.
+ return rebind().dropArguments(srcType, pos, drops);
+ }
+
+ /*non-public*/
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ // Override this if it can be improved.
+ return rebind().permuteArguments(newType, reorder);
+ }
+
+ /*non-public*/
+ MethodHandle rebind() {
+ // Bind 'this' into a new invoker, of the known class BMH.
+ MethodType type2 = type();
+ LambdaForm form2 = reinvokerForm(type2.basicType());
+ // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
+ return BoundMethodHandle.bindSingle(type2, form2, this);
+ }
+
+ /*non-public*/
+ MethodHandle reinvokerTarget() {
+ throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
+ }
+
+ /** Create a LF which simply reinvokes a target of the given basic type.
+ * The target MH must override {@link #reinvokerTarget} to provide the target.
+ */
+ static LambdaForm reinvokerForm(MethodType mtype) {
+ mtype = mtype.basicType();
+ LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
+ if (reinvoker != null) return reinvoker;
+ MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
+ final int THIS_BMH = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ int nameCursor = ARG_LIMIT;
+ final int NEXT_MH = nameCursor++;
+ final int REINVOKE = nameCursor++;
+ LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+ Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+ targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
+ names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
+ return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
+ }
+
+ private static final LambdaForm.NamedFunction NF_reinvokerTarget;
+ static {
+ try {
+ NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
+ .getDeclaredMethod("reinvokerTarget"));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ /**
+ * Replace the old lambda form of this method handle with a new one.
+ * The new one must be functionally equivalent to the old one.
+ * Threads may continue running the old form indefinitely,
+ * but it is likely that the new one will be preferred for new executions.
+ * Use with discretion.
+ * @param newForm
+ */
+ /*non-public*/
+ void updateForm(LambdaForm newForm) {
+ if (form == newForm) return;
+ // ISSUE: Should we have a memory fence here?
+ UNSAFE.putObject(this, FORM_OFFSET, newForm);
+ this.form.prepare(); // as in MethodHandle.<init>
+ }
+
+ private static final long FORM_OFFSET;
+ static {
+ try {
+ FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,17 +26,14 @@
package java.lang.invoke;
import sun.invoke.util.VerifyType;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
-import sun.misc.Unsafe;
+import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
@@ -47,670 +44,471 @@
/*non-public*/ abstract class MethodHandleImpl {
/// Factory methods to create method handles:
- private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
+ static void initStatics() {
+ // Trigger selected static initializations.
+ MemberName.Factory.INSTANCE.getClass();
+ }
- static void initStatics() {
- // Trigger preceding sequence.
+ static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
+ if (!arrayClass.isArray())
+ throw newIllegalArgumentException("not an array: "+arrayClass);
+ MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
+ MethodType srcType = accessor.type().erase();
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(1, lambdaType);
+ Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
+ names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
+ LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
+ MethodHandle mh = new SimpleMethodHandle(srcType, form);
+ if (ArrayAccessor.needCast(arrayClass)) {
+ mh = mh.bindTo(arrayClass);
+ }
+ mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
+ return mh;
}
- /** Look up a given method.
- * Callable only from sun.invoke and related packages.
- * <p>
- * The resulting method handle type will be of the given type,
- * with a receiver type {@code rcvc} prepended if the member is not static.
- * <p>
- * Access checks are made as of the given lookup class.
- * In particular, if the method is protected and {@code defc} is in a
- * different package from the lookup class, then {@code rcvc} must be
- * the lookup class or a subclass.
- * @param token Proof that the lookup class has access to this package.
- * @param member Resolved method or constructor to call.
- * @param name Name of the desired method.
- * @param rcvc Receiver type of desired non-static method (else null)
- * @param doDispatch whether the method handle will test the receiver type
- * @param lookupClass access-check relative to this class
- * @return a direct handle to the matching method
- * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
- */
- static
- MethodHandle findMethod(MemberName method,
- boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
- MethodType mtype = method.getMethodType();
- if (!method.isStatic()) {
- // adjust the advertised receiver type to be exactly the one requested
- // (in the case of invokespecial, this will be the calling class)
- Class<?> recvType = method.getDeclaringClass();
- mtype = mtype.insertParameterTypes(0, recvType);
- }
- DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
- if (!mh.isValid())
- throw method.makeAccessException("no direct method handle", lookupClass);
- assert(mh.type() == mtype);
- if (!method.isVarargs())
- return mh;
- int argc = mtype.parameterCount();
- if (argc != 0) {
- Class<?> arrayType = mtype.parameterType(argc-1);
- if (arrayType.isArray())
- return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
+ static final class ArrayAccessor {
+ /// Support for array element access
+ static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it
+ static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it
+
+ static int getElementI(int[] a, int i) { return a[i]; }
+ static long getElementJ(long[] a, int i) { return a[i]; }
+ static float getElementF(float[] a, int i) { return a[i]; }
+ static double getElementD(double[] a, int i) { return a[i]; }
+ static boolean getElementZ(boolean[] a, int i) { return a[i]; }
+ static byte getElementB(byte[] a, int i) { return a[i]; }
+ static short getElementS(short[] a, int i) { return a[i]; }
+ static char getElementC(char[] a, int i) { return a[i]; }
+ static Object getElementL(Object[] a, int i) { return a[i]; }
+
+ static void setElementI(int[] a, int i, int x) { a[i] = x; }
+ static void setElementJ(long[] a, int i, long x) { a[i] = x; }
+ static void setElementF(float[] a, int i, float x) { a[i] = x; }
+ static void setElementD(double[] a, int i, double x) { a[i] = x; }
+ static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
+ static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
+ static void setElementS(short[] a, int i, short x) { a[i] = x; }
+ static void setElementC(char[] a, int i, char x) { a[i] = x; }
+ static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
+
+ static Object getElementL(Class<?> arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; }
+ static void setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
+
+ // Weakly typed wrappers of Object[] accessors:
+ static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); }
+ static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); }
+ static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class<?>) arrayClass, (Object[])a, i); }
+ static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class<?>) arrayClass, (Object[])a, i, x); }
+
+ static boolean needCast(Class<?> arrayClass) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ return !elemClass.isPrimitive() && elemClass != Object.class;
}
- throw method.makeAccessException("cannot make variable arity", null);
- }
-
- static
- MethodHandle makeAllocator(MethodHandle rawConstructor) {
- MethodType rawConType = rawConstructor.type();
- Class<?> allocateClass = rawConType.parameterType(0);
- // Wrap the raw (unsafe) constructor with the allocation of a suitable object.
- assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true));
- // allocator(arg...)
- // [fold]=> cookedConstructor(obj=allocate(C), arg...)
- // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...))
- MethodHandle returner = MethodHandles.identity(allocateClass);
- MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass);
- MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false);
- assert(cookedConstructor.type().equals(ctype));
- ctype = ctype.dropParameterTypes(0, 1);
- cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true);
- AllocateObject allocator = new AllocateObject(allocateClass);
- // allocate() => new C(void)
- assert(allocator.type().equals(MethodType.methodType(allocateClass)));
- ctype = ctype.dropParameterTypes(0, 1);
- MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
- return fold;
- }
-
- static final class AllocateObject /*<C>*/ extends BoundMethodHandle {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
-
- private final Class<?> /*<C>*/ allocateClass;
-
- // for allocation only:
- private AllocateObject(Class<?> /*<C>*/ allocateClass) {
- super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
- this.allocateClass = allocateClass;
+ static String name(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ if (elemClass == null) throw new IllegalArgumentException();
+ return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
}
- @SuppressWarnings("unchecked")
- private Object /*C*/ allocate() throws InstantiationException {
- return unsafe.allocateInstance(allocateClass);
+ static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide
+ static MethodType type(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ Class<?> arrayArgClass = arrayClass;
+ if (!elemClass.isPrimitive()) {
+ arrayArgClass = Object[].class;
+ if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+ arrayArgClass = Object.class;
+ }
+ if (!needCast(arrayClass)) {
+ return !isSetter ?
+ MethodType.methodType(elemClass, arrayArgClass, int.class) :
+ MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
+ } else {
+ Class<?> classArgClass = Class.class;
+ if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+ classArgClass = Object.class;
+ return !isSetter ?
+ MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) :
+ MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class);
+ }
}
- static final MethodHandle ALLOCATE;
- static {
+ static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ return !isSetter ?
+ MethodType.methodType(elemClass, arrayClass, int.class) :
+ MethodType.methodType(void.class, arrayClass, int.class, elemClass);
+ }
+ static MethodHandle getAccessor(Class<?> arrayClass, boolean isSetter) {
+ String name = name(arrayClass, isSetter);
+ MethodType type = type(arrayClass, isSetter);
try {
- ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0));
+ return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type);
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
}
- static
- MethodHandle accessField(MemberName member, boolean isSetter,
- Class<?> lookupClass) {
- // Use sun. misc.Unsafe to dig up the dirt on the field.
- FieldAccessor accessor = new FieldAccessor(member, isSetter);
- return accessor;
- }
+ /**
+ * 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.
+ * The argument conversions allowed are casting, boxing and unboxing,
+ * integral widening or narrowing, and floating point widening or narrowing.
+ * @param srcType 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(MethodHandle target, MethodType srcType, int level) {
+ assert(level >= 0 && level <= 2);
+ MethodType dstType = target.type();
+ assert(dstType.parameterCount() == target.type().parameterCount());
+ if (srcType == dstType)
+ return target;
- static
- MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) {
- if (!arrayClass.isArray())
- throw newIllegalArgumentException("not an array: "+arrayClass);
- Class<?> elemClass = arrayClass.getComponentType();
- MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
- if (mhs == null) {
- if (!FieldAccessor.doCache(elemClass))
- return FieldAccessor.ahandle(arrayClass, isSetter);
- mhs = new MethodHandle[] {
- FieldAccessor.ahandle(arrayClass, false),
- FieldAccessor.ahandle(arrayClass, true)
- };
- if (mhs[0].type().parameterType(0) == Class.class) {
- mhs[0] = mhs[0].bindTo(elemClass);
- mhs[1] = mhs[1].bindTo(elemClass);
+ // Calculate extra arguments (temporaries) required in the names array.
+ // FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step.
+ int extra = 0;
+ for (int i = 0; i < srcType.parameterCount(); i++) {
+ Class<?> src = srcType.parameterType(i);
+ Class<?> dst = dstType.parameterType(i);
+ if (!VerifyType.isNullConversion(src, dst)) {
+ extra++;
}
- synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
- FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
}
- return mhs[isSetter ? 1 : 0];
- }
+
+ Class<?> needReturn = srcType.returnType();
+ Class<?> haveReturn = dstType.returnType();
+ boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(extra + 1, lambdaType);
+ int[] indexes = new int[lambdaType.parameterCount()];
- static final class FieldAccessor /*<C,V>*/ extends BoundMethodHandle {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- final Object base; // for static refs only
- final long offset;
- final String name;
+ MethodType midType = dstType;
+ for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
+ Class<?> src = srcType.parameterType(i);
+ Class<?> dst = midType.parameterType(i);
+
+ if (VerifyType.isNullConversion(src, dst)) {
+ // do nothing: difference is trivial
+ indexes[i] = argIndex;
+ continue;
+ }
+
+ // Work the current type backward toward the desired caller type:
+ midType = midType.changeParameterType(i, src);
- FieldAccessor(MemberName field, boolean isSetter) {
- super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
- this.offset = (long) field.getVMIndex();
- this.name = field.getName();
- this.base = staticBase(field);
+ // Tricky case analysis follows.
+ MethodHandle fn = null;
+ if (src.isPrimitive()) {
+ if (dst.isPrimitive()) {
+ fn = ValueConversions.convertPrimitive(src, dst);
+ } else {
+ Wrapper w = Wrapper.forPrimitiveType(src);
+ MethodHandle boxMethod = ValueConversions.box(w);
+ if (dst == w.wrapperType())
+ fn = boxMethod;
+ else
+ fn = boxMethod.asType(MethodType.methodType(dst, src));
+ }
+ } else {
+ if (dst.isPrimitive()) {
+ // Caller has boxed a primitive. Unbox it for the target.
+ Wrapper w = Wrapper.forPrimitiveType(dst);
+ if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) {
+ fn = ValueConversions.unbox(dst);
+ } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
+ // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int
+ // must include additional conversions
+ // src must be examined at runtime, to detect Byte, Character, etc.
+ MethodHandle unboxMethod = (level == 1
+ ? ValueConversions.unbox(dst)
+ : ValueConversions.unboxCast(dst));
+ fn = unboxMethod;
+ } else {
+ // Example: Byte->int
+ // Do this by reformulating the problem to Byte->byte.
+ Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
+ MethodHandle unbox = ValueConversions.unbox(srcPrim);
+ // Compose the two conversions. FIXME: should make two Names for this job
+ fn = unbox.asType(MethodType.methodType(dst, src));
+ }
+ } 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.
+ fn = ValueConversions.cast(dst);
+ }
+ }
+ names[tmpIndex] = new Name(fn, names[argIndex]);
+ indexes[i] = tmpIndex;
+ tmpIndex++;
}
- @Override
- String debugString() { return addTypeString(name, this); }
-
- int getFieldI(Object /*C*/ obj) { return unsafe.getInt(obj, offset); }
- void setFieldI(Object /*C*/ obj, int x) { unsafe.putInt(obj, offset, x); }
- long getFieldJ(Object /*C*/ obj) { return unsafe.getLong(obj, offset); }
- void setFieldJ(Object /*C*/ obj, long x) { unsafe.putLong(obj, offset, x); }
- float getFieldF(Object /*C*/ obj) { return unsafe.getFloat(obj, offset); }
- void setFieldF(Object /*C*/ obj, float x) { unsafe.putFloat(obj, offset, x); }
- double getFieldD(Object /*C*/ obj) { return unsafe.getDouble(obj, offset); }
- void setFieldD(Object /*C*/ obj, double x) { unsafe.putDouble(obj, offset, x); }
- boolean getFieldZ(Object /*C*/ obj) { return unsafe.getBoolean(obj, offset); }
- void setFieldZ(Object /*C*/ obj, boolean x) { unsafe.putBoolean(obj, offset, x); }
- byte getFieldB(Object /*C*/ obj) { return unsafe.getByte(obj, offset); }
- void setFieldB(Object /*C*/ obj, byte x) { unsafe.putByte(obj, offset, x); }
- short getFieldS(Object /*C*/ obj) { return unsafe.getShort(obj, offset); }
- void setFieldS(Object /*C*/ obj, short x) { unsafe.putShort(obj, offset, x); }
- char getFieldC(Object /*C*/ obj) { return unsafe.getChar(obj, offset); }
- void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(obj, offset, x); }
- Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(obj, offset); }
- void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(obj, offset, x); }
- // cast (V) is OK here, since we wrap convertArguments around the MH.
-
- static Object staticBase(final MemberName field) {
- if (!field.isStatic()) return null;
- return AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- try {
- Class c = field.getDeclaringClass();
- // FIXME: Should not have to create 'f' to get this value.
- java.lang.reflect.Field f = c.getDeclaredField(field.getName());
- return unsafe.staticFieldBase(f);
- } catch (NoSuchFieldException ee) {
- throw uncaughtException(ee);
- }
- }
- });
+ if (retConv) {
+ 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 {
+ MethodHandle identity = MethodHandles.identity(needReturn);
+ MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
+ adjustReturn = makePairwiseConvert(identity, needConversion, level);
+ }
+ target = makeCollectArguments(adjustReturn, target, 0, false);
}
- int getStaticI() { return unsafe.getInt(base, offset); }
- void setStaticI(int x) { unsafe.putInt(base, offset, x); }
- long getStaticJ() { return unsafe.getLong(base, offset); }
- void setStaticJ(long x) { unsafe.putLong(base, offset, x); }
- float getStaticF() { return unsafe.getFloat(base, offset); }
- void setStaticF(float x) { unsafe.putFloat(base, offset, x); }
- double getStaticD() { return unsafe.getDouble(base, offset); }
- void setStaticD(double x) { unsafe.putDouble(base, offset, x); }
- boolean getStaticZ() { return unsafe.getBoolean(base, offset); }
- void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); }
- byte getStaticB() { return unsafe.getByte(base, offset); }
- void setStaticB(byte x) { unsafe.putByte(base, offset, x); }
- short getStaticS() { return unsafe.getShort(base, offset); }
- void setStaticS(short x) { unsafe.putShort(base, offset, x); }
- char getStaticC() { return unsafe.getChar(base, offset); }
- void setStaticC(char x) { unsafe.putChar(base, offset, x); }
- @SuppressWarnings("unchecked") // (V) is for internal clarity but triggers warning
- Object /*V*/ getStaticL() { return unsafe.getObject(base, offset); }
- void setStaticL(Object /*V*/ x) { unsafe.putObject(base, offset, x); }
-
- static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) {
- String stem;
- if (!isStatic)
- stem = (!isSetter ? "getField" : "setField");
- else
- stem = (!isSetter ? "getStatic" : "setStatic");
- return stem + Wrapper.basicTypeChar(vclass);
+ // Build argument array for the call.
+ Name[] targetArgs = new Name[dstType.parameterCount()];
+ for (int i = 0; i < dstType.parameterCount(); i++) {
+ int idx = indexes[i];
+ targetArgs[i] = names[idx];
}
- static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
- MethodType type;
- if (!isStatic) {
- if (!isSetter)
- return MethodType.methodType(vclass, cclass);
- else
- return MethodType.methodType(void.class, cclass, vclass);
- } else {
- if (!isSetter)
- return MethodType.methodType(vclass);
- else
- return MethodType.methodType(void.class, vclass);
- }
+ names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+ LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
+ }
+
+ static MethodHandle makeReferenceIdentity(Class<?> refType) {
+ MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
+ Name[] names = arguments(1, lambdaType);
+ names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
+ LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
+ }
+
+ static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
+ MethodType type = target.type();
+ int last = type.parameterCount() - 1;
+ if (type.parameterType(last) != arrayType)
+ target = target.asType(type.changeParameterType(last, arrayType));
+ target = target.asFixedArity(); // make sure this attribute is turned off
+ return new AsVarargsCollector(target, target.type(), arrayType);
+ }
+
+ static class AsVarargsCollector extends MethodHandle {
+ MethodHandle target;
+ final Class<?> arrayType;
+ MethodHandle cache;
+
+ AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
+ super(type, reinvokerForm(type));
+ this.target = target;
+ this.arrayType = arrayType;
+ this.cache = target.asCollector(arrayType, 0);
}
- static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
- String name = FieldAccessor.fname(vclass, isSetter, isStatic);
- if (cclass.isPrimitive()) throw newIllegalArgumentException("primitive "+cclass);
- Class<?> ecclass = Object.class; //erase this type
- Class<?> evclass = vclass;
- if (!evclass.isPrimitive()) evclass = Object.class;
- MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
- MethodHandle mh;
- try {
- mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
- } catch (ReflectiveOperationException ex) {
- throw uncaughtException(ex);
- }
- if (evclass != vclass || (!isStatic && ecclass != cclass)) {
- MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
- strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
- mh = convertArguments(mh, strongType, 0);
- }
- return mh;
+
+ @Override MethodHandle reinvokerTarget() { return target; }
+
+ @Override
+ public boolean isVarargsCollector() {
+ return true;
+ }
+
+ @Override
+ public MethodHandle asFixedArity() {
+ return target;
}
- /// Support for array element access
- static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
- new HashMap<Class<?>, MethodHandle[]>();
- // FIXME: Cache on the classes themselves, not here.
- static boolean doCache(Class<?> elemClass) {
- if (elemClass.isPrimitive()) return true;
- ClassLoader cl = elemClass.getClassLoader();
- return cl == null || cl == ClassLoader.getSystemClassLoader();
+ @Override
+ public MethodHandle asType(MethodType newType) {
+ MethodType type = this.type();
+ int collectArg = type.parameterCount() - 1;
+ int newArity = newType.parameterCount();
+ if (newArity == collectArg+1 &&
+ type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
+ // if arity and trailing parameter are compatible, do normal thing
+ return asFixedArity().asType(newType);
+ }
+ // check cache
+ if (cache.type().parameterCount() == newArity)
+ return cache.asType(newType);
+ // build and cache a collector
+ int arrayLength = newArity - collectArg;
+ MethodHandle collector;
+ try {
+ collector = asFixedArity().asCollector(arrayType, arrayLength);
+ } catch (IllegalArgumentException ex) {
+ throw new WrongMethodTypeException("cannot build collector");
+ }
+ cache = collector;
+ return collector.asType(newType);
}
- static int getElementI(int[] a, int i) { return a[i]; }
- static void setElementI(int[] a, int i, int x) { a[i] = x; }
- static long getElementJ(long[] a, int i) { return a[i]; }
- static void setElementJ(long[] a, int i, long x) { a[i] = x; }
- static float getElementF(float[] a, int i) { return a[i]; }
- static void setElementF(float[] a, int i, float x) { a[i] = x; }
- static double getElementD(double[] a, int i) { return a[i]; }
- static void setElementD(double[] a, int i, double x) { a[i] = x; }
- static boolean getElementZ(boolean[] a, int i) { return a[i]; }
- static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
- static byte getElementB(byte[] a, int i) { return a[i]; }
- static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
- static short getElementS(short[] a, int i) { return a[i]; }
- static void setElementS(short[] a, int i, short x) { a[i] = x; }
- static char getElementC(char[] a, int i) { return a[i]; }
- static void setElementC(char[] a, int i, char x) { a[i] = x; }
- static Object getElementL(Object[] a, int i) { return a[i]; }
- static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
- static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
- static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }
+
+ @Override
+ MethodHandle setVarargs(MemberName member) {
+ if (member.isVarargs()) return this;
+ return asFixedArity();
+ }
- static String aname(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- if (vclass == null) throw new IllegalArgumentException();
- return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ MethodHandle mh = super.viewAsType(newType);
+ // put back the varargs bit:
+ MethodType type = mh.type();
+ int arity = type.parameterCount();
+ return mh.asVarargsCollector(type.parameterType(arity-1));
}
- static MethodType atype(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- if (!isSetter)
- return MethodType.methodType(vclass, aclass, int.class);
- else
- return MethodType.methodType(void.class, aclass, int.class, vclass);
+
+ @Override
+ MemberName internalMemberName() {
+ return asFixedArity().internalMemberName();
}
- static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- String name = FieldAccessor.aname(aclass, isSetter);
- Class<?> caclass = null;
- if (!vclass.isPrimitive() && vclass != Object.class) {
- caclass = aclass;
- aclass = Object[].class;
- vclass = Object.class;
- }
- MethodType type = FieldAccessor.atype(aclass, isSetter);
- if (caclass != null)
- type = type.insertParameterTypes(0, Class.class);
- MethodHandle mh;
- try {
- mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
- } catch (ReflectiveOperationException ex) {
- throw uncaughtException(ex);
- }
- if (caclass != null) {
- MethodType strongType = FieldAccessor.atype(caclass, isSetter);
- mh = mh.bindTo(caclass);
- mh = convertArguments(mh, strongType, 0);
- }
- return mh;
+
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ return asFixedArity().bindArgument(pos, basicType, value);
+ }
+
+ @Override
+ MethodHandle bindReceiver(Object receiver) {
+ return asFixedArity().bindReceiver(receiver);
+ }
+
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ return asFixedArity().dropArguments(srcType, pos, drops);
+ }
+
+ @Override
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ return asFixedArity().permuteArguments(newType, reorder);
}
}
- /** Bind a predetermined first argument to the given direct method handle.
- * Callable only from MethodHandles.
- * @param token Proof that the caller has access to this package.
- * @param target Any direct method handle.
- * @param receiver Receiver (or first static method argument) to pre-bind.
- * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
- */
- static
- MethodHandle bindReceiver(MethodHandle target, Object receiver) {
- if (receiver == null) return null;
- if (target instanceof AdapterMethodHandle &&
- ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY
- ) {
- Object info = MethodHandleNatives.getTargetInfo(target);
- if (info instanceof DirectMethodHandle) {
- DirectMethodHandle dmh = (DirectMethodHandle) info;
- if (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(), 0);
- }
- }
+ /** Factory method: Spread selected argument. */
+ static MethodHandle makeSpreadArguments(MethodHandle target,
+ Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ MethodType targetType = target.type();
+
+ for (int i = 0; i < spreadArgCount; i++) {
+ Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
+ if (arg == null) arg = Object.class;
+ targetType = targetType.changeParameterType(spreadArgPos + i, arg);
}
- if (target instanceof DirectMethodHandle)
- return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
- return null; // let caller try something else
- }
-
- /** Bind a predetermined argument to the given arbitrary method handle.
- * Callable only from MethodHandles.
- * @param token Proof that the caller has access to this package.
- * @param target Any method handle.
- * @param receiver Argument (which can be a boxed primitive) to pre-bind.
- * @return a suitable BoundMethodHandle
- */
- static
- MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
- return new BoundMethodHandle(target, receiver, argnum);
- }
+ target = target.asType(targetType);
- static MethodHandle permuteArguments(MethodHandle target,
- MethodType newType,
- MethodType oldType,
- int[] permutationOrNull) {
- assert(oldType.parameterCount() == target.type().parameterCount());
- 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);
- }
- }
- // 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);
+ MethodType srcType = targetType
+ .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType);
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(spreadArgCount + 2, lambdaType);
+ int nameCursor = lambdaType.parameterCount();
+ int[] indexes = new int[targetType.parameterCount()];
+
+ for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) {
+ Class<?> src = lambdaType.parameterType(i);
+ if (i == spreadArgPos) {
+ // Spread the array.
+ MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
+ Name array = names[argIndex];
+ names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
+ for (int j = 0; j < spreadArgCount; i++, j++) {
+ indexes[i] = nameCursor;
+ names[nameCursor++] = new Name(aload, array, j);
}
+ } else if (i < indexes.length) {
+ indexes[i] = argIndex;
}
}
- 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;
- }
- }
- // Else de-rotate, and drop through to the swap-fest.
- Collections.rotate(rotSpan, rotBy);
- }
+ assert(nameCursor == names.length-1); // leave room for the final call
+
+ // Build argument array for the call.
+ Name[] targetArgs = new Name[targetType.parameterCount()];
+ for (int i = 0; i < targetType.parameterCount(); i++) {
+ int idx = indexes[i];
+ targetArgs[i] = names[idx];
+ }
+ names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+
+ LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
+ }
- // 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));
+ static void checkSpreadArgument(Object av, int 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;
}
- 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();
+ // fall through to error:
+ throw newIllegalArgumentException("Array is not of length "+n);
+ }
+
+ private static final NamedFunction NF_checkSpreadArgument;
+ static {
+ try {
+ NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
+ .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+ NF_checkSpreadArgument.resolve();
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
}
- 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();
- }
- target = convertArguments(target, newType, oldType, 0);
- assert(target != null);
- return target;
}
- /*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);
- }
- newType = newType.changeReturnType(oldRT);
+ /** Factory method: Collect or filter selected argument(s). */
+ static MethodHandle makeCollectArguments(MethodHandle target,
+ MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
+ MethodType targetType = target.type(); // (a..., c, [b...])=>r
+ MethodType collectorType = collector.type(); // (b...)=>c
+ int collectArgCount = collectorType.parameterCount();
+ Class<?> collectValType = collectorType.returnType();
+ int collectValCount = (collectValType == void.class ? 0 : 1);
+ MethodType srcType = targetType // (a..., [b...])=>r
+ .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
+ if (!retainOriginalArgs) { // (a..., b...)=>r
+ srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList());
}
- 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;
- }
+ // 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... ]
- 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", oldType, newType);
- return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
- }
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(2, lambdaType);
+ final int collectNamePos = names.length - 2;
+ final int targetNamePos = names.length - 1;
+
+ Name[] collectorArgs = Arrays.copyOfRange(names, 1 + collectArgPos, 1 + collectArgPos + collectArgCount);
+ names[collectNamePos] = new Name(collector, (Object[]) collectorArgs);
- 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);
- }
- // called internally only
- static MethodHandle spreadArgumentsFromPos(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 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
- assert(arrayLength == oldType.parameterCount() - spreadArgPos);
- assert(newType.parameterType(spreadArgPos) == arrayType);
- return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
- }
+ // Build argument array for the target.
+ // Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ].
+ // Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ].
+ Name[] targetArgs = new Name[targetType.parameterCount()];
+ int inputArgPos = 1; // incoming LF args to copy to target
+ int targetArgPos = 0; // fill pointer for targetArgs
+ int chunk = collectArgPos; // |headArgs|
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ inputArgPos += chunk;
+ targetArgPos += chunk;
+ if (collectValType != void.class) {
+ targetArgs[targetArgPos++] = names[collectNamePos];
+ }
+ chunk = collectArgCount;
+ if (retainOriginalArgs) {
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ targetArgPos += chunk; // optionally pass on the collected chunk
+ }
+ inputArgPos += chunk;
+ chunk = targetArgs.length - targetArgPos; // all the rest
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ assert(inputArgPos + chunk == collectNamePos); // use of rest of input args also
+ names[targetNamePos] = new Name(target, (Object[]) targetArgs);
- static MethodHandle collectArguments(MethodHandle target,
- int collectArg,
- MethodHandle collector) {
- MethodType type = target.type();
- Class<?> collectType = collector.type().returnType();
- assert(collectType != void.class); // else use foldArguments
- 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
- // 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);
- assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false));
- return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
- }
-
- static MethodHandle filterArgument(MethodHandle target,
- int pos,
- MethodHandle filter) {
- MethodType ttype = target.type();
- MethodType ftype = filter.type();
- assert(ftype.parameterCount() == 1);
- return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
- }
-
- static MethodHandle foldArguments(MethodHandle target,
- MethodType newType,
- int foldPos,
- MethodHandle combiner) {
- MethodType oldType = target.type();
- MethodType ctype = combiner.type();
- assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true));
- return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
- }
-
- static
- MethodHandle dropArguments(MethodHandle target,
- MethodType newType, int argnum) {
- int drops = newType.parameterCount() - target.type().parameterCount();
- return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
+ LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
}
static
@@ -735,47 +533,42 @@
MethodHandle makeGuardWithTest(MethodHandle test,
MethodHandle target,
MethodHandle fallback) {
- // gwt(arg...)
- // [fold]=> continueAfterTest(z=test(arg...), arg...)
- // [filter]=> (tf=select(z))(arg...)
- // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f
- // [tailcall]=> tf(arg...)
- assert(test.type().returnType() == boolean.class);
- MethodType targetType = target.type();
- MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class);
- assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true));
- // working backwards, as usual:
- assert(target.type().equals(fallback.type()));
- MethodHandle tailcall = MethodHandles.exactInvoker(target.type());
- MethodHandle select = selectAlternative();
- select = bindArgument(select, 2, CountingMethodHandle.wrap(fallback));
- select = bindArgument(select, 1, CountingMethodHandle.wrap(target));
- // select(z: boolean) => (z ? target : fallback)
- MethodHandle filter = filterArgument(tailcall, 0, select);
- assert(filter.type().parameterType(0) == boolean.class);
- MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test);
- return fold;
+ MethodType basicType = target.type().basicType();
+ MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType);
+ int arity = basicType.parameterCount();
+ int extraNames = 3;
+ MethodType lambdaType = basicType.invokerType();
+ Name[] names = arguments(extraNames, lambdaType);
+
+ Object[] testArgs = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class);
+ Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class);
+
+ // call test
+ names[arity + 1] = new Name(test, testArgs);
+
+ // call selectAlternative
+ Object[] selectArgs = { names[arity + 1], target, fallback };
+ names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
+ targetArgs[0] = names[arity + 2];
+
+ // call target or fallback
+ names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
+
+ LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(target.type(), form);
}
- private static class GuardWithCatch extends BoundMethodHandle {
+ private static class GuardWithCatch {
private final MethodHandle target;
private final Class<? extends Throwable> exType;
private final MethodHandle catcher;
+ // FIXME: Build the control flow out of foldArguments.
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
- this(INVOKES[target.type().parameterCount()], target, exType, 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;
this.catcher = catcher;
}
- @Override
- String debugString() {
- return addTypeString(target, this);
- }
+ @LambdaForm.Hidden
private Object invoke_V(Object... av) throws Throwable {
try {
return target.invokeExact(av);
@@ -784,6 +577,7 @@
return catcher.invokeExact(t, av);
}
}
+ @LambdaForm.Hidden
private Object invoke_L0() throws Throwable {
try {
return target.invokeExact();
@@ -792,6 +586,7 @@
return catcher.invokeExact(t);
}
}
+ @LambdaForm.Hidden
private Object invoke_L1(Object a0) throws Throwable {
try {
return target.invokeExact(a0);
@@ -800,6 +595,7 @@
return catcher.invokeExact(t, a0);
}
}
+ @LambdaForm.Hidden
private Object invoke_L2(Object a0, Object a1) throws Throwable {
try {
return target.invokeExact(a0, a1);
@@ -808,6 +604,7 @@
return catcher.invokeExact(t, a0, a1);
}
}
+ @LambdaForm.Hidden
private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
try {
return target.invokeExact(a0, a1, a2);
@@ -816,6 +613,7 @@
return catcher.invokeExact(t, a0, a1, a2);
}
}
+ @LambdaForm.Hidden
private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3);
@@ -824,6 +622,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3);
}
}
+ @LambdaForm.Hidden
private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4);
@@ -832,6 +631,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4);
}
}
+ @LambdaForm.Hidden
private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5);
@@ -840,6 +640,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
}
}
+ @LambdaForm.Hidden
private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
@@ -848,6 +649,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
}
}
+ @LambdaForm.Hidden
private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
@@ -857,7 +659,7 @@
}
}
static MethodHandle[] makeInvokes() {
- ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
+ ArrayList<MethodHandle> invokes = new ArrayList<>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = invokes.size();
@@ -898,39 +700,60 @@
MethodType gtype = type.generic();
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
// Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
- MethodHandle gtarget = convertArguments(target, gtype, type, 2);
- MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
- MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
- if (gtarget == null || gcatcher == null || gguard == null) return null;
- return convertArguments(gguard, type, gtype, 2);
+ MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
+ MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
+ GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+ if (gtarget == null || gcatcher == null) throw new InternalError();
+ MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard);
+ return makePairwiseConvert(ginvoker, type, 2);
} else {
- MethodType gtype = MethodType.genericMethodType(0, true);
- MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
- MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
+ MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
- MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
- MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
- if (gtarget == null || gcatcher == null || gguard == null) return null;
- return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
+ MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs);
+ GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+ if (gtarget == null || gcatcher == null) throw new InternalError();
+ MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
+ return makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
}
}
static
MethodHandle throwException(MethodType type) {
- return AdapterMethodHandle.makeRetypeRaw(type, throwException());
+ assert(Throwable.class.isAssignableFrom(type.parameterType(0)));
+ int arity = type.parameterCount();
+ if (arity > 1) {
+ return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
+ }
+ return makePairwiseConvert(throwException(), type, 2);
}
static MethodHandle THROW_EXCEPTION;
static MethodHandle throwException() {
- if (THROW_EXCEPTION != null) return THROW_EXCEPTION;
+ MethodHandle mh = THROW_EXCEPTION;
+ if (mh != null) return mh;
try {
- THROW_EXCEPTION
+ mh
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
- return THROW_EXCEPTION;
+ THROW_EXCEPTION = mh;
+ return mh;
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
+
+ static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
+ static
+ MethodHandle fakeMethodHandleInvoke(MemberName method) {
+ MethodType type = method.getInvocationType();
+ assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
+ MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
+ if (mh != null) return mh;
+ mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
+ mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
+ FAKE_METHOD_HANDLE_INVOKE = mh;
+ return mh;
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package java.lang.invoke;
+import java.lang.invoke.MethodHandleNatives.Constants;
+
+//Not yet public: public
+class MethodHandleInfo {
+ public static final int
+ REF_NONE = Constants.REF_NONE,
+ REF_getField = Constants.REF_getField,
+ REF_getStatic = Constants.REF_getStatic,
+ REF_putField = Constants.REF_putField,
+ REF_putStatic = Constants.REF_putStatic,
+ REF_invokeVirtual = Constants.REF_invokeVirtual,
+ REF_invokeStatic = Constants.REF_invokeStatic,
+ REF_invokeSpecial = Constants.REF_invokeSpecial,
+ REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
+ REF_invokeInterface = Constants.REF_invokeInterface;
+
+ private final Class<?> declaringClass;
+ private final String name;
+ private final MethodType methodType;
+ private final int referenceKind;
+
+ public MethodHandleInfo(MethodHandle mh) throws ReflectiveOperationException {
+ MemberName mn = mh.internalMemberName();
+ this.declaringClass = mn.getDeclaringClass();
+ this.name = mn.getName();
+ this.methodType = mn.getMethodType();
+ this.referenceKind = mn.getReferenceKind();
+ }
+
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public MethodType getMethodType() {
+ return methodType;
+ }
+
+ public int getReferenceKind() {
+ return referenceKind;
+ }
+}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -29,6 +29,7 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
@@ -41,56 +42,21 @@
private MethodHandleNatives() { } // static only
- /// MethodName support
+ /// MemberName support
static native void init(MemberName self, Object ref);
static native void expand(MemberName self);
- static native void resolve(MemberName self, Class<?> caller);
+ static native MemberName resolve(MemberName self, Class<?> caller) throws LinkageError;
static native int getMembers(Class<?> defc, String matchName, String matchSig,
int matchFlags, Class<?> caller, int skip, MemberName[] results);
- /// MethodHandle support
-
- /** Initialize the method handle to adapt the call. */
- static native void init(AdapterMethodHandle self, MethodHandle target, int argnum);
- /** Initialize the method handle to call the correct method, directly. */
- static native void init(BoundMethodHandle self, Object target, int argnum);
- /** Initialize the method handle to call as if by an invoke* instruction. */
- static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class<?> caller);
-
- /** Initialize a method type, once per form. */
- static native void init(MethodType self);
-
- /** Fetch the vmtarget field.
- * It will be sanitized as necessary to avoid exposing non-Java references.
- * This routine is for debugging and reflection.
- */
- static native Object getTarget(MethodHandle self, int format);
+ /// Field layout queries parallel to sun.misc.Unsafe:
+ static native long objectFieldOffset(MemberName self); // e.g., returns vmindex
+ static native long staticFieldOffset(MemberName self); // e.g., returns vmindex
+ static native Object staticFieldBase(MemberName self); // e.g., returns clazz
+ static native Object getMemberVMInfo(MemberName self); // returns {vmindex,vmtarget}
- /** Fetch the name of the handled method, if available.
- * This routine is for debugging and reflection.
- */
- static MemberName getMethodName(MethodHandle self) {
- return (MemberName) getTarget(self, ETF_METHOD_NAME);
- }
-
- /** Fetch the reflective version of the handled method, if available.
- */
- static AccessibleObject getTargetMethod(MethodHandle self) {
- return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD);
- }
-
- /** Fetch the target of this method handle.
- * If it directly targets a method, return a MemberName for the method.
- * If it is chained to another method handle, return that handle.
- */
- static Object getTargetInfo(MethodHandle self) {
- return getTarget(self, ETF_HANDLE_OR_METHOD_NAME);
- }
-
- static Object[] makeTarget(Class<?> defc, String name, String sig, int mods, Class<?> refc) {
- return new Object[] { defc, name, sig, mods, refc };
- }
+ /// MethodHandle support
/** Fetch MH-related JVM parameter.
* which=0 retrieves MethodHandlePushLimit
@@ -98,19 +64,6 @@
*/
static native int getConstant(int which);
- /** Java copy of MethodHandlePushLimit in range 2..255. */
- static final int JVM_PUSH_LIMIT;
- /** JVM stack motion (in words) after one slot is pushed, usually -1.
- */
- static final int JVM_STACK_MOVE_UNIT;
-
- /** 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;
-
- static final int OP_ROT_ARGS_DOWN_LIMIT_BIAS;
-
static final boolean COUNT_GWT;
/// CallSite support
@@ -122,17 +75,11 @@
private static native void registerNatives();
static {
registerNatives();
- int k;
- JVM_PUSH_LIMIT = getConstant(Constants.GC_JVM_PUSH_LIMIT);
- JVM_STACK_MOVE_UNIT = getConstant(Constants.GC_JVM_STACK_MOVE_UNIT);
- k = getConstant(Constants.GC_CONV_OP_IMPLEMENTED_MASK);
- CONV_OP_IMPLEMENTED_MASK = (k != 0) ? k : DEFAULT_CONV_OP_IMPLEMENTED_MASK;
- k = getConstant(Constants.GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS);
- OP_ROT_ARGS_DOWN_LIMIT_BIAS = (k != 0) ? (byte)k : -1;
- HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
COUNT_GWT = getConstant(Constants.GC_COUNT_GWT) != 0;
- //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
- }
+
+ // The JVM calls MethodHandleNatives.<clinit>. Cascade the <clinit> calls as needed:
+ MethodHandleImpl.initStatics();
+}
// All compile-time constants go here.
// There is an opportunity to check them against the JVM's idea of them.
@@ -140,16 +87,8 @@
Constants() { } // static only
// MethodHandleImpl
static final int // for getConstant
- GC_JVM_PUSH_LIMIT = 0,
- GC_JVM_STACK_MOVE_UNIT = 1,
- GC_CONV_OP_IMPLEMENTED_MASK = 2,
- GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS = 3,
- GC_COUNT_GWT = 4;
- static final int
- ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
- ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
- ETF_METHOD_NAME = 2, // ultimate method as MemberName
- ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass)
+ GC_COUNT_GWT = 4,
+ GC_LAMBDA_SUPPORT = 5;
// MemberName
// The JVM uses values of -2 and above for vtable indexes.
@@ -162,65 +101,11 @@
MN_IS_CONSTRUCTOR = 0x00020000, // constructor
MN_IS_FIELD = 0x00040000, // field
MN_IS_TYPE = 0x00080000, // nested type
- MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
- MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
- VM_INDEX_UNINITIALIZED = -99;
-
- // BoundMethodHandle
- /** Constants for decoding the vmargslot field, which contains 2 values. */
- static final int
- ARG_SLOT_PUSH_SHIFT = 16,
- ARG_SLOT_MASK = (1<<ARG_SLOT_PUSH_SHIFT)-1;
-
- // AdapterMethodHandle
- /** Conversions recognized by the JVM.
- * They must align with the constants in java.lang.invoke.AdapterMethodHandle,
- * in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp.
- */
- static final int
- OP_RETYPE_ONLY = 0x0, // 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
- 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 arguments using an auxiliary function
- OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
- 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
- CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
- CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
- CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
- CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1;
-
- /** Which conv-ops are implemented by the JVM? */
- static final int DEFAULT_CONV_OP_IMPLEMENTED_MASK =
- // Value to use if the corresponding JVM query fails.
- ((1<<OP_RETYPE_ONLY)
- |(1<<OP_RETYPE_RAW)
- |(1<<OP_CHECK_CAST)
- |(1<<OP_PRIM_TO_PRIM)
- |(1<<OP_REF_TO_PRIM)
- |(1<<OP_SWAP_ARGS)
- |(1<<OP_ROT_ARGS)
- |(1<<OP_DUP_ARGS)
- |(1<<OP_DROP_ARGS)
- //|(1<<OP_SPREAD_ARGS)
- );
+ MN_REFERENCE_KIND_SHIFT = 24, // refKind
+ MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+ // The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
+ MN_SEARCH_SUPERCLASSES = 0x00100000,
+ MN_SEARCH_INTERFACES = 0x00200000;
/**
* Basic types as encoded in the JVM. These code values are not
@@ -243,9 +128,54 @@
T_ILLEGAL = 99;
/**
+ * Constant pool entry types.
+ */
+ static final byte
+ CONSTANT_Utf8 = 1,
+ CONSTANT_Integer = 3,
+ CONSTANT_Float = 4,
+ CONSTANT_Long = 5,
+ CONSTANT_Double = 6,
+ CONSTANT_Class = 7,
+ CONSTANT_String = 8,
+ CONSTANT_Fieldref = 9,
+ CONSTANT_Methodref = 10,
+ CONSTANT_InterfaceMethodref = 11,
+ CONSTANT_NameAndType = 12,
+ CONSTANT_MethodHandle = 15, // JSR 292
+ CONSTANT_MethodType = 16, // JSR 292
+ CONSTANT_InvokeDynamic = 18,
+ CONSTANT_LIMIT = 19; // Limit to tags found in classfiles
+
+ /**
+ * Access modifier flags.
+ */
+ static final char
+ ACC_PUBLIC = 0x0001,
+ ACC_PRIVATE = 0x0002,
+ ACC_PROTECTED = 0x0004,
+ ACC_STATIC = 0x0008,
+ ACC_FINAL = 0x0010,
+ ACC_SYNCHRONIZED = 0x0020,
+ ACC_VOLATILE = 0x0040,
+ ACC_TRANSIENT = 0x0080,
+ ACC_NATIVE = 0x0100,
+ ACC_INTERFACE = 0x0200,
+ ACC_ABSTRACT = 0x0400,
+ ACC_STRICT = 0x0800,
+ ACC_SYNTHETIC = 0x1000,
+ ACC_ANNOTATION = 0x2000,
+ ACC_ENUM = 0x4000,
+ // aliases:
+ ACC_SUPER = ACC_SYNCHRONIZED,
+ ACC_BRIDGE = ACC_VOLATILE,
+ ACC_VARARGS = ACC_TRANSIENT;
+
+ /**
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
*/
- static final int
+ static final byte
+ REF_NONE = 0, // null value
REF_getField = 1,
REF_getStatic = 2,
REF_putField = 3,
@@ -254,8 +184,66 @@
REF_invokeStatic = 6,
REF_invokeSpecial = 7,
REF_newInvokeSpecial = 8,
- REF_invokeInterface = 9;
+ REF_invokeInterface = 9,
+ REF_LIMIT = 10;
+ }
+
+ static boolean refKindIsValid(int refKind) {
+ return (refKind > REF_NONE && refKind < REF_LIMIT);
+ }
+ static boolean refKindIsField(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind <= REF_putStatic);
+ }
+ static boolean refKindIsGetter(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind <= REF_getStatic);
+ }
+ static boolean refKindIsSetter(byte refKind) {
+ return refKindIsField(refKind) && !refKindIsGetter(refKind);
+ }
+ static boolean refKindIsMethod(byte refKind) {
+ return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
+ }
+ static boolean refKindHasReceiver(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind & 1) != 0;
+ }
+ static boolean refKindIsStatic(byte refKind) {
+ return !refKindHasReceiver(refKind) && (refKind != REF_newInvokeSpecial);
}
+ static boolean refKindDoesDispatch(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind == REF_invokeVirtual ||
+ refKind == REF_invokeInterface);
+ }
+ static {
+ final int HR_MASK = ((1 << REF_getField) |
+ (1 << REF_putField) |
+ (1 << REF_invokeVirtual) |
+ (1 << REF_invokeSpecial) |
+ (1 << REF_invokeInterface)
+ );
+ for (byte refKind = REF_NONE+1; refKind < REF_LIMIT; refKind++) {
+ assert(refKindHasReceiver(refKind) == (((1<<refKind) & HR_MASK) != 0)) : refKind;
+ }
+ }
+ static String refKindName(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return REFERENCE_KIND_NAME[refKind];
+ }
+ private static String[] REFERENCE_KIND_NAME = {
+ null,
+ "getField",
+ "getStatic",
+ "putField",
+ "putStatic",
+ "invokeVirtual",
+ "invokeStatic",
+ "invokeSpecial",
+ "newInvokeSpecial",
+ "invokeInterface"
+ };
private static native int getNamedCon(int which, Object[] name);
static boolean verifyConstants() {
@@ -275,16 +263,11 @@
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);
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ String err = (name+": JVM has "+vmval+" which Java does not define");
+ // ignore exotic ops the JVM cares about; we just wont issue them
+ //System.err.println("warning: "+err);
+ continue;
}
}
return true;
@@ -299,18 +282,21 @@
/**
* The JVM is linking an invokedynamic instruction. Create a reified call site for it.
*/
- static CallSite makeDynamicCallSite(MethodHandle bootstrapMethod,
- String name, MethodType type,
- Object info,
- MemberName callerMethod, int callerBCI) {
- return CallSite.makeSite(bootstrapMethod, name, type, info, callerMethod, callerBCI);
- }
-
- /**
- * Called by the JVM to check the length of a spread array.
- */
- static void checkSpreadArgument(Object av, int n) {
- MethodHandleStatics.checkSpreadArgument(av, n);
+ static MemberName linkCallSite(Object callerObj,
+ Object bootstrapMethodObj,
+ Object nameObj, Object typeObj,
+ Object staticArguments,
+ Object[] appendixResult) {
+ MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
+ Class<?> caller = (Class<?>)callerObj;
+ String name = nameObj.toString().intern();
+ MethodType type = (MethodType)typeObj;
+ appendixResult[0] = CallSite.makeSite(bootstrapMethod,
+ name,
+ type,
+ staticArguments,
+ caller);
+ return Invokers.linkToCallSiteMethod(type);
}
/**
@@ -321,71 +307,64 @@
}
/**
- * The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning.
- */
- static void notifyGenericMethodType(MethodType type) {
- type.form().notifyGenericMethodType();
- }
-
- /**
- * The JVM wants to raise an exception. Here's the path.
+ * The JVM wants to link a call site that requires a dynamic type check.
+ * Name is a type-checking invoker, invokeExact or invoke.
+ * Return a JVM method (MemberName) to handle the invoking.
+ * The method assumes the following arguments on the stack:
+ * 0: the method handle being invoked
+ * 1-N: the arguments to the method handle invocation
+ * N+1: an implicitly added type argument (the given MethodType)
*/
- static void raiseException(int code, Object actual, Object required) {
- 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;
- case 191: // athrow
- // JVM is asking us to wrap an exception which happened during resolving
- if (required == BootstrapMethodError.class) {
- throw new BootstrapMethodError((Throwable) actual);
- }
- break;
+ static MemberName linkMethod(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ if (!TRACE_METHOD_LINKAGE)
+ return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+ return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult);
+ }
+ static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
+ throw new LinkageError("no such method "+defc.getName()+"."+name+type);
+ switch (name) {
+ case "invoke":
+ return Invokers.genericInvokerMethod(callerClass, type, appendixResult);
+ case "invokeExact":
+ return Invokers.exactInvokerMethod(callerClass, type, appendixResult);
}
- // disregard the identity of the actual object, if it is not a class:
- 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:
- throw new InternalError("unexpected code "+code+": "+message);
+ throw new UnsupportedOperationException("linkMethod "+name);
+ }
+ // Tracing logic:
+ static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ System.out.println("linkMethod "+defc.getName()+"."+
+ name+type+"/"+Integer.toHexString(refKind));
+ try {
+ MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+ System.out.println("linkMethod => "+res+" + "+appendixResult[0]);
+ return res;
+ } catch (Throwable ex) {
+ System.out.println("linkMethod => throw "+ex);
+ throw ex;
}
}
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
+ * The type argument is a Class for field requests and a MethodType for non-fields.
+ * <p>
+ * Recent versions of the JVM may also pass a resolved MemberName for the type.
+ * In that case, the name is ignored and may be null.
*/
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type) {
try {
Lookup lookup = IMPL_LOOKUP.in(callerClass);
- return lookup.linkMethodHandleConstant(refKind, defc, name, type);
+ assert(refKindIsValid(refKind));
+ return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
} catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Mon Aug 27 10:58:40 2012 -0700
@@ -27,6 +27,7 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
/**
* This class consists exclusively of static names internal to the
@@ -38,16 +39,30 @@
private MethodHandleStatics() { } // do not instantiate
+ static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
static final boolean DEBUG_METHOD_HANDLE_NAMES;
+ static final boolean DUMP_CLASS_FILES;
+ static final boolean TRACE_INTERPRETER;
+ static final boolean TRACE_METHOD_LINKAGE;
+ static final Integer COMPILE_THRESHOLD;
static {
- final Object[] values = { false };
+ final Object[] values = { false, false, false, false, null };
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+ values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
+ values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
+ values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
+ values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
return null;
}
});
DEBUG_METHOD_HANDLE_NAMES = (Boolean) values[0];
+ DUMP_CLASS_FILES = (Boolean) values[1];
+ TRACE_INTERPRETER = (Boolean) values[2];
+ TRACE_METHOD_LINKAGE = (Boolean) values[3];
+ COMPILE_THRESHOLD = (Integer) values[4];
}
/*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
@@ -55,7 +70,7 @@
type = target.type();
MemberName name = null;
if (target != null)
- name = MethodHandleNatives.getMethodName(target);
+ name = target.internalMemberName();
if (name == null)
return "invoke" + type;
return name.getName() + type;
@@ -77,20 +92,6 @@
return str + target.type();
}
- static void checkSpreadArgument(Object av, int 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)
/*non-public*/ static RuntimeException newIllegalStateException(String message) {
return new IllegalStateException(message);
@@ -110,6 +111,9 @@
/*non-public*/ static Error uncaughtException(Exception ex) {
throw new InternalError("uncaught exception", ex);
}
+ static Error NYI() {
+ throw new AssertionError("NYI");
+ }
private static String message(String message, Object obj) {
if (obj != null) message = message + ": " + obj;
return message;
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,7 +26,6 @@
package java.lang.invoke;
import java.lang.reflect.*;
-import sun.invoke.WrapperInstance;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyAccess;
import sun.invoke.util.Wrapper;
@@ -174,6 +173,8 @@
* Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
* The formal parameter {@code this} stands for the self-reference of type {@code C};
* if it is present, it is always the leading argument to the method handle invocation.
+ * (In the case of some {@code protected} members, {@code this} may be
+ * restricted in type to the lookup class; see below.)
* The name {@code arg} stands for all the other method handle arguments.
* In the code examples for the Core Reflection API, the name {@code thisOrNull}
* stands for a null reference if the accessed method or field is static,
@@ -244,6 +245,18 @@
* is exactly equivalent to executing the compiled and resolved call to {@code M}.
* The same point is true of fields and constructors.
* <p>
+ * If the desired member is {@code protected}, the usual JVM rules apply,
+ * including the requirement that the lookup class must be either be in the
+ * same package as the desired member, or must inherit that member.
+ * (See the Java Virtual Machine Specification, sections 4.9.2, 5.4.3.5, and 6.4.)
+ * In addition, if the desired member is a non-static field or method
+ * in a different package, the resulting method handle may only be applied
+ * to objects of the lookup class or one of its subclasses.
+ * This requirement is enforced by narrowing the type of the leading
+ * {@code this} parameter from {@code C}
+ * (which will necessarily be a superclass of the lookup class)
+ * to the lookup class itself.
+ * <p>
* In some cases, access between nested classes is obtained by the Java compiler by creating
* an wrapper method to access a private method of another class
* in the same top-level declaration.
@@ -582,19 +595,9 @@
*/
public
MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, true);
+ MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessStatic(refc, method);
- }
- private
- MethodHandle accessStatic(Class<?> refc, MemberName method) throws IllegalAccessException {
- checkMethod(refc, method, true);
- return MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
- }
- private
- MethodHandle resolveStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, true);
- return accessStatic(refc, method);
+ return getDirectMethod(REF_invokeStatic, refc, method);
}
/**
@@ -609,6 +612,11 @@
* (The dispatching action is identical with that performed by an
* {@code invokevirtual} or {@code invokeinterface} instruction.)
* <p>
+ * The first argument will be of type {@code refc} if the lookup
+ * class has full privileges to access the member. Otherwise
+ * the member must be {@code protected} and the first argument
+ * will be restricted in type to the lookup class.
+ * <p>
* The returned method handle will have
* {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* the method's variable arity modifier bit ({@code 0x0080}) is set.
@@ -636,18 +644,22 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, false);
+ if (refc == MethodHandle.class) {
+ MethodHandle mh = findVirtualForMH(name, type);
+ if (mh != null) return mh;
+ }
+ byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual);
+ MemberName method = resolveOrFail(refKind, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessVirtual(refc, method);
+ return getDirectMethod(refKind, refc, method);
}
- private MethodHandle resolveVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, false);
- return accessVirtual(refc, method);
- }
- private MethodHandle accessVirtual(Class<?> refc, MemberName method) throws IllegalAccessException {
- checkMethod(refc, method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- return restrictProtectedReceiver(method, mh);
+ private MethodHandle findVirtualForMH(String name, MethodType type) {
+ // these names require special lookups because of the implicit MethodType argument
+ if ("invoke".equals(name))
+ return invoker(type);
+ if ("invokeExact".equals(name))
+ return exactInvoker(type);
+ return null;
}
/**
@@ -678,36 +690,9 @@
*/
public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
String name = "<init>";
- MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
+ MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
checkSecurityManager(refc, ctor); // stack walk magic: do not refactor
- return accessConstructor(refc, ctor);
- }
- private MethodHandle accessConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
- assert(ctor.isConstructor());
- checkAccess(refc, ctor);
- MethodHandle rawMH = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull());
- MethodHandle allocMH = MethodHandleImpl.makeAllocator(rawMH);
- return fixVarargs(allocMH, rawMH);
- }
- private MethodHandle resolveConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- String name = "<init>";
- MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
- return accessConstructor(refc, ctor);
- }
-
- /** Return a version of MH which matches matchMH w.r.t. isVarargsCollector. */
- private static MethodHandle fixVarargs(MethodHandle mh, MethodHandle matchMH) {
- boolean va1 = mh.isVarargsCollector();
- boolean va2 = matchMH.isVarargsCollector();
- if (va1 == va2) {
- return mh;
- } else if (va2) {
- MethodType type = mh.type();
- int arity = type.parameterCount();
- return mh.asVarargsCollector(type.parameterType(arity-1));
- } else {
- return mh.asFixedArity();
- }
+ return getDirectConstructor(refc, ctor);
}
/**
@@ -747,21 +732,10 @@
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
checkSpecialCaller(specialCaller);
- MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
+ Lookup specialLookup = this.in(specialCaller);
+ MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessSpecial(refc, method, specialCaller);
- }
- private MethodHandle accessSpecial(Class<?> refc, MemberName method,
- Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
- checkMethod(refc, method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, false, specialCaller);
- return restrictReceiver(method, mh, specialCaller);
- }
- private MethodHandle resolveSpecial(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- Class<?> specialCaller = lookupClass();
- checkSpecialCaller(specialCaller);
- MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
- return accessSpecial(refc, method, specialCaller);
+ return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method);
}
/**
@@ -782,13 +756,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
+ MemberName field = resolveOrFail(REF_getField, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, false, 0);
- }
- private MethodHandle resolveGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
- return makeAccessor(refc, field, false, false, 0);
+ return getDirectField(REF_getField, refc, field);
}
/**
@@ -809,13 +779,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
+ MemberName field = resolveOrFail(REF_putField, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, true, 0);
- }
- private MethodHandle resolveSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
- return makeAccessor(refc, field, false, true, 0);
+ return getDirectField(REF_putField, refc, field);
}
/**
@@ -835,13 +801,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
+ MemberName field = resolveOrFail(REF_getStatic, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, false, 1);
- }
- private MethodHandle resolveStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
- return makeAccessor(refc, field, false, false, 1);
+ return getDirectField(REF_getStatic, refc, field);
}
/**
@@ -861,13 +823,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
+ MemberName field = resolveOrFail(REF_putStatic, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, true, 1);
- }
- private MethodHandle resolveStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
- return makeAccessor(refc, field, false, true, 1);
+ return getDirectField(REF_putStatic, refc, field);
}
/**
@@ -918,14 +876,10 @@
*/
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
Class<? extends Object> refc = receiver.getClass(); // may get NPE
- MemberName method = resolveOrFail(refc, name, type, false);
+ MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- checkMethod(refc, method, false);
- MethodHandle dmh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- MethodHandle bmh = MethodHandleImpl.bindReceiver(dmh, receiver);
- if (bmh == null)
- throw method.makeAccessException("no access", this);
- return fixVarargs(bmh, dmh);
+ MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method);
+ return mh.bindReceiver(receiver).setVarargs(method);
}
/**
@@ -951,12 +905,12 @@
*/
public MethodHandle unreflect(Method m) throws IllegalAccessException {
MemberName method = new MemberName(m);
+ byte refKind = method.getReferenceKind();
+ if (refKind == REF_invokeSpecial)
+ refKind = REF_invokeVirtual;
assert(method.isMethod());
- if (m.isAccessible())
- return MethodHandleImpl.findMethod(method, true, /*no lookupClass*/ null);
- checkMethod(method.getDeclaringClass(), method, method.isStatic());
- MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- return restrictProtectedReceiver(method, mh);
+ Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method);
}
/**
@@ -982,12 +936,11 @@
*/
public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
checkSpecialCaller(specialCaller);
- MemberName method = new MemberName(m);
+ Lookup specialLookup = this.in(specialCaller);
+ MemberName method = new MemberName(m, true);
assert(method.isMethod());
// ignore m.isAccessible: this is a new kind of access
- checkMethod(m.getDeclaringClass(), method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
- return restrictReceiver(method, mh, specialCaller);
+ return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method);
}
/**
@@ -1015,15 +968,8 @@
public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
MemberName ctor = new MemberName(c);
assert(ctor.isConstructor());
- MethodHandle rawCtor;
- if (c.isAccessible()) {
- rawCtor = MethodHandleImpl.findMethod(ctor, false, /*no lookupClass*/ null);
- } else {
- checkAccess(c.getDeclaringClass(), ctor);
- rawCtor = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull());
- }
- MethodHandle allocator = MethodHandleImpl.makeAllocator(rawCtor);
- return fixVarargs(allocator, rawCtor);
+ Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectConstructor(ctor.getDeclaringClass(), ctor);
}
/**
@@ -1041,7 +987,15 @@
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
- return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false, -1);
+ return unreflectField(f, false);
+ }
+ private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
+ MemberName field = new MemberName(f, isSetter);
+ assert(isSetter
+ ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
+ : MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
+ Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectField(field.getReferenceKind(), f.getDeclaringClass(), field);
}
/**
@@ -1059,33 +1013,22 @@
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
- return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true, -1);
+ return unreflectField(f, true);
}
/// Helper methods, all package-private.
- MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoSuchFieldException, IllegalAccessException {
+ MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+ return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
NoSuchFieldException.class);
}
- MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoSuchMethodException, IllegalAccessException {
+ MemberName resolveOrFail(byte refKind, Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
- NoSuchMethodException.class);
- }
-
- MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
- boolean searchSupers, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
- checkSymbolicClass(refc); // do this before attempting to resolve
- name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller,
+ return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
NoSuchMethodException.class);
}
@@ -1141,7 +1084,8 @@
// SecurityManager.checkMemberAccess [0]
}
- void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws IllegalAccessException {
+ void checkMethod(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ boolean wantStatic = (refKind == REF_invokeStatic);
String message;
if (m.isConstructor())
message = "expected a method, not a constructor";
@@ -1150,26 +1094,43 @@
else if (wantStatic != m.isStatic())
message = wantStatic ? "expected a static method" : "expected a non-static method";
else
- { checkAccess(refc, m); return; }
+ { checkAccess(refKind, refc, m); return; }
throw m.makeAccessException(message, this);
}
- void checkAccess(Class<?> refc, MemberName m) throws IllegalAccessException {
+ void checkField(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ boolean wantStatic = !MethodHandleNatives.refKindHasReceiver(refKind);
+ String message;
+ if (wantStatic != m.isStatic())
+ message = wantStatic ? "expected a static field" : "expected a non-static field";
+ else
+ { checkAccess(refKind, refc, m); return; }
+ throw m.makeAccessException(message, this);
+ }
+
+ void checkAccess(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ assert(m.referenceKindIsConsistentWith(refKind) &&
+ MethodHandleNatives.refKindIsValid(refKind) &&
+ (MethodHandleNatives.refKindIsField(refKind) == m.isField()));
int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
int mods = m.getModifiers();
+ if (Modifier.isFinal(mods) &&
+ MethodHandleNatives.refKindIsSetter(refKind))
+ throw m.makeAccessException("unexpected set of a final field", this);
if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers()) && allowedModes != 0)
return; // common case
int requestedModes = fixmods(mods); // adjust 0 => PACKAGE
- if ((requestedModes & allowedModes) != 0
- && VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
- mods, lookupClass(), allowedModes))
- return;
- if (((requestedModes & ~allowedModes) & PROTECTED) != 0
- && (allowedModes & PACKAGE) != 0
- && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
+ if ((requestedModes & allowedModes) != 0) {
+ if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
+ mods, lookupClass(), allowedModes))
+ return;
+ } else {
// Protected members can also be checked as if they were package-private.
- return;
+ if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0
+ && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
+ return;
+ }
throw m.makeAccessException(accessFailedMessage(refc, m), this);
}
@@ -1198,7 +1159,8 @@
private static final boolean ALLOW_NESTMATE_ACCESS = false;
- void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+ private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+ int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
if ((allowedModes & PRIVATE) == 0
|| (specialCaller != lookupClass()
@@ -1208,7 +1170,7 @@
makeAccessException("no private access for invokespecial", this);
}
- MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws IllegalAccessException {
+ private boolean restrictProtectedReceiver(MemberName method) {
// The accessing class only has the right to use a protected member
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
if (!method.isProtected() || method.isStatic()
@@ -1217,52 +1179,82 @@
|| VerifyAccess.isSamePackage(method.getDeclaringClass(), lookupClass())
|| (ALLOW_NESTMATE_ACCESS &&
VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass())))
- return mh;
- else
- return restrictReceiver(method, mh, lookupClass());
+ return false;
+ return true;
}
- MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
+ private MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
assert(!method.isStatic());
- Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
- if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
+ // receiver type of mh is too wide; narrow to caller
+ if (!method.getDeclaringClass().isAssignableFrom(caller)) {
throw method.makeAccessException("caller class must be a subclass below the method", caller);
}
MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller);
- MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
- return fixVarargs(narrowMH, mh);
+ return mh.viewAsType(narrowType);
}
- MethodHandle makeAccessor(Class<?> refc, MemberName field,
- boolean trusted, boolean isSetter,
- int checkStatic) throws IllegalAccessException {
- assert(field.isField());
- if (checkStatic >= 0 && (checkStatic != 0) != field.isStatic())
- throw field.makeAccessException((checkStatic != 0)
- ? "expected a static field"
- : "expected a non-static field", this);
- if (trusted)
- return MethodHandleImpl.accessField(field, isSetter, /*no lookupClass*/ null);
- checkAccess(refc, field);
- MethodHandle mh = MethodHandleImpl.accessField(field, isSetter, lookupClassOrNull());
- return restrictProtectedReceiver(field, mh);
+ private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException {
+ return getDirectMethodCommon(refKind, refc, method,
+ (refKind == REF_invokeSpecial ||
+ (MethodHandleNatives.refKindHasReceiver(refKind) &&
+ restrictProtectedReceiver(method))));
+ }
+ private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException {
+ return getDirectMethodCommon(refKind, refc, method, false);
+ }
+ private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
+ boolean doRestrict) throws IllegalAccessException {
+ checkMethod(refKind, refc, method);
+ if (method.isMethodHandleInvoke())
+ return fakeMethodHandleInvoke(method);
+ MethodHandle mh = DirectMethodHandle.make(refc, method);
+ mh = mh.setVarargs(method);
+ if (doRestrict)
+ mh = restrictReceiver(method, mh, lookupClass());
+ return mh;
+ }
+ private MethodHandle fakeMethodHandleInvoke(MemberName method) {
+ return throwException(method.getReturnType(), UnsupportedOperationException.class);
+ }
+ private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
+ checkField(refKind, refc, field);
+ MethodHandle mh = DirectMethodHandle.make(refc, field);
+ boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) &&
+ restrictProtectedReceiver(field));
+ if (doRestrict)
+ mh = restrictReceiver(field, mh, lookupClass());
+ return mh;
+ }
+ private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
+ assert(ctor.isConstructor());
+ checkAccess(REF_newInvokeSpecial, refc, ctor);
+ return DirectMethodHandle.make(ctor).setVarargs(ctor);
}
/** Hook called from the JVM (via MethodHandleNatives) to link MH constants:
*/
/*non-public*/
- MethodHandle linkMethodHandleConstant(int refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException {
- switch (refKind) {
- case REF_getField: return resolveGetter( defc, name, (Class<?>) type );
- case REF_getStatic: return resolveStaticGetter( defc, name, (Class<?>) type );
- case REF_putField: return resolveSetter( defc, name, (Class<?>) type );
- case REF_putStatic: return resolveStaticSetter( defc, name, (Class<?>) type );
- case REF_invokeVirtual: return resolveVirtual( defc, name, (MethodType) type );
- case REF_invokeStatic: return resolveStatic( defc, name, (MethodType) type );
- case REF_invokeSpecial: return resolveSpecial( defc, name, (MethodType) type );
- case REF_newInvokeSpecial: return resolveConstructor( defc, (MethodType) type );
- case REF_invokeInterface: return resolveVirtual( defc, name, (MethodType) type );
+ MethodHandle linkMethodHandleConstant(byte refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException {
+ MemberName resolved = null;
+ if (type instanceof MemberName) {
+ resolved = (MemberName) type;
+ if (!resolved.isResolved()) throw new InternalError("unresolved MemberName");
+ assert(name == null || name.equals(resolved.getName()));
+ }
+ if (MethodHandleNatives.refKindIsField(refKind)) {
+ MemberName field = (resolved != null) ? resolved
+ : resolveOrFail(refKind, defc, name, (Class<?>) type);
+ return getDirectField(refKind, defc, field);
+ } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
+ MemberName method = (resolved != null) ? resolved
+ : resolveOrFail(refKind, defc, name, (MethodType) type);
+ return getDirectMethod(refKind, defc, method);
+ } else if (refKind == REF_newInvokeSpecial) {
+ assert(name == null || name.equals("<init>"));
+ MemberName ctor = (resolved != null) ? resolved
+ : resolveOrFail(REF_newInvokeSpecial, defc, name, (MethodType) type);
+ return getDirectConstructor(defc, ctor);
}
// oops
throw new ReflectiveOperationException("bad MethodHandle constant #"+refKind+" "+name+" : "+type);
@@ -1281,7 +1273,7 @@
*/
public static
MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.accessArrayElement(arrayClass, false);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
}
/**
@@ -1295,7 +1287,7 @@
*/
public static
MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.accessArrayElement(arrayClass, true);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
}
/// method handle invocation (reflective style)
@@ -1422,78 +1414,12 @@
return type.invokers().generalInvoker();
}
- /**
- * Perform value checking, exactly as if for an adapted method handle.
- * It is assumed that the given value is either null, of type T0,
- * or (if T0 is primitive) of the wrapper class corresponding to T0.
- * The following checks and conversions are made:
- * <ul>
- * <li>If T0 and T1 are references, then a cast to T1 is applied.
- * (The types do not need to be related in any particular way.)
- * <li>If T0 and T1 are primitives, then a widening or narrowing
- * conversion is applied, if one exists.
- * <li>If T0 is a primitive and T1 a reference, and
- * T0 has a wrapper class TW, a boxing conversion to TW is applied,
- * possibly followed by a reference conversion.
- * T1 must be TW or a supertype.
- * <li>If T0 is a reference and T1 a primitive, and
- * T1 has a wrapper class TW, an unboxing conversion is applied,
- * possibly preceded by a reference conversion.
- * T0 must be TW or a supertype.
- * <li>If T1 is void, the return value is discarded
- * <li>If T0 is void and T1 a reference, a null value is introduced.
- * <li>If T0 is void and T1 a primitive, a zero value is introduced.
- * </ul>
- * If the value is discarded, null will be returned.
- * @param valueType
- * @param value
- * @return the value, converted if necessary
- * @throws java.lang.ClassCastException if a cast fails
- */
- // FIXME: This is used in just one place. Refactor away.
- static
- <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
- throws ClassCastException
- {
- if (t0 == t1) {
- // no conversion needed; just reassert the same type
- if (t0.isPrimitive())
- return Wrapper.asPrimitiveType(t1).cast(value);
- else
- return Wrapper.OBJECT.convert(value, t1);
- }
- boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
- if (!prim0) {
- // check contract with caller
- Wrapper.OBJECT.convert(value, t0);
- if (!prim1) {
- return Wrapper.OBJECT.convert(value, t1);
- }
- // convert reference to primitive by unboxing
- Wrapper w1 = Wrapper.forPrimitiveType(t1);
- return w1.convert(value, t1);
- }
- // check contract with caller:
- Wrapper.asWrapperType(t0).cast(value);
- Wrapper w1 = Wrapper.forPrimitiveType(t1);
- return w1.convert(value, t1);
+ static /*non-public*/
+ MethodHandle basicInvoker(MethodType type) {
+ return type.form().basicInvoker();
}
- // FIXME: Delete this. It is used only for insertArguments & bindTo.
- // Replace by a more standard check.
- static
- Object checkValue(Class<?> T1, Object value)
- throws ClassCastException
- {
- Class<?> T0;
- if (value == null)
- T0 = Object.class;
- else
- T0 = value.getClass();
- return checkValue(T0, T1, value);
- }
-
- /// method handle modification (creation from other method handles)
+ /// method handle modification (creation from other method handles)
/**
* Produces a method handle which adapts the type of the
@@ -1541,7 +1467,10 @@
*/
public static
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
- return MethodHandleImpl.convertArguments(target, newType, 2);
+ if (!target.type().isCastableTo(newType)) {
+ throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
+ }
+ return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
}
/**
@@ -1605,11 +1534,8 @@
*/
public static
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
- MethodType oldType = target.type();
- checkReorder(reorder, newType, oldType);
- return MethodHandleImpl.permuteArguments(target,
- newType, oldType,
- reorder);
+ checkReorder(reorder, newType, target.type());
+ return target.permuteArguments(newType, reorder);
}
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
@@ -1678,8 +1604,7 @@
else if (type.isPrimitive())
return ValueConversions.identity(Wrapper.forPrimitiveType(type));
else
- return AdapterMethodHandle.makeRetypeRaw(
- MethodType.methodType(type, type), ValueConversions.identity());
+ return MethodHandleImpl.makeReferenceIdentity(type);
}
/**
@@ -1725,18 +1650,26 @@
MethodHandle result = target;
for (int i = 0; i < insCount; i++) {
Object value = values[i];
- Class<?> valueType = oldType.parameterType(pos+i);
- value = checkValue(valueType, value);
- if (pos == 0 && !valueType.isPrimitive()) {
- // At least for now, make bound method handles a special case.
- MethodHandle bmh = MethodHandleImpl.bindReceiver(result, value);
- if (bmh != null) {
- result = bmh;
- continue;
+ Class<?> ptype = oldType.parameterType(pos+i);
+ if (ptype.isPrimitive()) {
+ char btype = 'I';
+ Wrapper w = Wrapper.forPrimitiveType(ptype);
+ switch (w) {
+ case LONG: btype = 'J'; break;
+ case FLOAT: btype = 'F'; break;
+ case DOUBLE: btype = 'D'; break;
}
- // else fall through to general adapter machinery
+ // perform unboxing and/or primitive conversion
+ value = w.convert(value, ptype);
+ result = result.bindArgument(pos, btype, value);
+ continue;
}
- result = MethodHandleImpl.bindArgument(result, pos, value);
+ value = ptype.cast(value); // throw CCE if needed
+ if (pos == 0) {
+ result = result.bindReceiver(value);
+ } else {
+ result = result.bindArgument(pos, 'L', value);
+ }
}
return result;
}
@@ -1786,16 +1719,17 @@
public static
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
MethodType oldType = target.type(); // get NPE
- if (valueTypes.size() == 0) return target;
+ int dropped = valueTypes.size();
+ MethodType.checkSlotCount(dropped);
+ if (dropped == 0) return target;
int outargs = oldType.parameterCount();
- int inargs = outargs + valueTypes.size();
+ int inargs = outargs + dropped;
if (pos < 0 || pos >= inargs)
throw newIllegalArgumentException("no argument type to remove");
- ArrayList<Class<?>> ptypes =
- new ArrayList<Class<?>>(oldType.parameterList());
+ ArrayList<Class<?>> ptypes = new ArrayList<>(oldType.parameterList());
ptypes.addAll(pos, valueTypes);
MethodType newType = MethodType.methodType(oldType.returnType(), ptypes);
- return MethodHandleImpl.dropArguments(target, newType, pos);
+ return target.dropArguments(newType, pos, dropped);
}
/**
@@ -1939,7 +1873,7 @@
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);
+ return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
}
/**
@@ -2011,10 +1945,7 @@
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;
- assert(AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false));
- return AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
+ return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
}
/**
@@ -2112,9 +2043,7 @@
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
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;
+ return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
}
/**
@@ -2255,6 +2184,8 @@
*/
public static
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
+ if (!Throwable.class.isAssignableFrom(exType))
+ throw new ClassCastException(exType.getName());
return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -26,12 +26,14 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import sun.invoke.util.BytecodeDescriptor;
import static java.lang.invoke.MethodHandleStatics.*;
+import sun.invoke.util.VerifyType;
/**
* A method type represents the arguments and return type accepted and
@@ -107,6 +109,8 @@
/*trusted*/ Class<?> rtype() { return rtype; }
/*trusted*/ Class<?>[] ptypes() { return ptypes; }
+ void setForm(MethodTypeForm f) { form = f; }
+
private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check
}
@@ -126,7 +130,7 @@
checkSlotCount(ptypes.length + slots);
return slots;
}
- private static void checkSlotCount(int count) {
+ static void checkSlotCount(int count) {
if ((count & 0xFF) != count)
throw newIllegalArgumentException("bad parameter count "+count);
}
@@ -135,8 +139,7 @@
return new IndexOutOfBoundsException(num.toString());
}
- static final HashMap<MethodType,MethodType> internTable
- = new HashMap<MethodType, MethodType>();
+ static final WeakInternSet internTable = new WeakInternSet();
static final Class<?>[] NO_PTYPES = {};
@@ -238,31 +241,17 @@
ptypes = NO_PTYPES; trusted = true;
}
MethodType mt1 = new MethodType(rtype, ptypes);
- MethodType mt0;
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- }
+ MethodType mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
if (!trusted)
// defensively copy the array passed in by the user
mt1 = new MethodType(rtype, ptypes.clone());
// promote the object to the Real Thing, and reprobe
MethodTypeForm form = MethodTypeForm.findForm(mt1);
mt1.form = form;
- if (form.erasedType == mt1) {
- // This is a principal (erased) type; show it to the JVM.
- MethodHandleNatives.init(mt1);
- }
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- internTable.put(mt1, mt1);
- }
- return mt1;
+ return internTable.add(mt1);
}
-
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
@@ -394,6 +383,32 @@
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
+ /**
+ * Finds or creates a method type with modified parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param start the position (zero-based) of the first replaced parameter type(s)
+ * @param end the position (zero-based) after the last replaced parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) replaced
+ * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
+ * or if {@code end} is negative or greater than {@code parameterCount()}
+ * or if {@code start} is greater than {@code end}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ /*non-public*/ MethodType replaceParameterTypes(int start, int end, Class<?>... ptypesToInsert) {
+ if (start == end)
+ return insertParameterTypes(start, ptypesToInsert);
+ int len = ptypes.length;
+ if (!(0 <= start && start <= end && end <= len))
+ throw newIndexOutOfBoundsException("start="+start+" end="+end);
+ int ilen = ptypesToInsert.length;
+ if (ilen == 0)
+ return dropParameterTypes(start, end);
+ return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
+ }
+
/**
* Finds or creates a method type with some parameter types omitted.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
@@ -474,6 +489,23 @@
}
/**
+ * Erases all reference types to {@code Object}, and all subword types to {@code int}.
+ * This is the reduced type polymorphism used by private methods
+ * such as {@link MethodHandle#invokeBasic invokeBasic}.
+ * @return a version of the original type with all reference and subword types replaced
+ */
+ /*non-public*/ MethodType basicType() {
+ return form.basicType();
+ }
+
+ /**
+ * @return a version of the original type with MethodHandle prepended as the first argument
+ */
+ /*non-public*/ MethodType invokerType() {
+ return insertParameterTypes(0, MethodHandle.class);
+ }
+
+ /**
* Converts all types, both reference and primitive, to {@code Object}.
* Convenience method for {@link #genericMethodType(int) genericMethodType}.
* The expression {@code type.wrap().erase()} produces the same value
@@ -567,6 +599,11 @@
return Collections.unmodifiableList(Arrays.asList(ptypes));
}
+ /*non-public*/ Class<?> lastParameterType() {
+ int len = ptypes.length;
+ return len == 0 ? void.class : ptypes[len-1];
+ }
+
/**
* Presents the parameter types as an array (a convenience method).
* Changes to the array will not result in changes to the type.
@@ -636,6 +673,26 @@
/*non-public*/
+ boolean isViewableAs(MethodType newType) {
+ if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
+ return false;
+ int argc = parameterCount();
+ if (argc != newType.parameterCount())
+ return false;
+ for (int i = 0; i < argc; i++) {
+ if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
+ return false;
+ }
+ return true;
+ }
+ /*non-public*/
+ boolean isCastableTo(MethodType newType) {
+ int argc = parameterCount();
+ if (argc != newType.parameterCount())
+ return false;
+ return true;
+ }
+ /*non-public*/
boolean isConvertibleTo(MethodType newType) {
if (!canConvert(returnType(), newType.returnType()))
return false;
@@ -818,6 +875,10 @@
return BytecodeDescriptor.unparse(this);
}
+ /*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
+ return BytecodeDescriptor.unparse(cls);
+ }
+
/// Serialization.
/**
@@ -890,18 +951,17 @@
// store them into the implementation-specific final fields.
checkRtype(rtype);
checkPtypes(ptypes);
- unsafe.putObject(this, rtypeOffset, rtype);
- unsafe.putObject(this, ptypesOffset, ptypes);
+ UNSAFE.putObject(this, rtypeOffset, rtype);
+ UNSAFE.putObject(this, ptypesOffset, ptypes);
}
// Support for resetting final fields while deserializing
- private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
private static final long rtypeOffset, ptypesOffset;
static {
try {
- rtypeOffset = unsafe.objectFieldOffset
+ rtypeOffset = UNSAFE.objectFieldOffset
(MethodType.class.getDeclaredField("rtype"));
- ptypesOffset = unsafe.objectFieldOffset
+ ptypesOffset = UNSAFE.objectFieldOffset
(MethodType.class.getDeclaredField("ptypes"));
} catch (Exception ex) {
throw new Error(ex);
@@ -919,4 +979,269 @@
// Verify all operands, and make sure ptypes is unshared:
return methodType(rtype, ptypes);
}
+
+ /**
+ * Weak intern set based on implementation of the <tt>HashSet</tt> and
+ * <tt>WeakHashMap</tt>, with <em>weak values</em>. Note: <tt>null</tt>
+ * values will yield <tt>NullPointerException</tt>
+ * Refer to implementation of WeakInternSet for details.
+ *
+ * @see java.util.HashMap
+ * @see java.util.HashSet
+ * @see java.util.WeakHashMap
+ * @see java.lang.ref.WeakReference
+ */
+ private static class WeakInternSet {
+ // The default initial capacity -- MUST be a power of two.
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ // The maximum capacity, used if a higher value is implicitly specified
+ // by either of the constructors with arguments.
+ // MUST be a power of two <= 1<<30.
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ // The load factor used when none specified in constructor.
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ // The table, resized as necessary. Length MUST Always be a power of two.
+ private Entry[] table;
+
+ // The number of entries contained in this set.
+ private int size;
+
+ // The next size value at which to resize (capacity * load factor).
+ private int threshold;
+
+ // The load factor for the hash table.
+ private final float loadFactor;
+
+ // Reference queue for cleared WeakEntries
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+
+ private Entry[] newTable(int n) {
+ return new Entry[n];
+ }
+
+ /**
+ * Constructs a new, empty <tt>WeakInternSet</tt> with the default initial
+ * capacity (16) and load factor (0.75).
+ */
+ WeakInternSet() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ threshold = DEFAULT_INITIAL_CAPACITY;
+ table = newTable(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because hashing uses power-of-two length hash tables, that
+ * otherwise encounter collisions for hashCodes that do not differ
+ * in lower bits.
+ * @param h preliminary hash code value
+ * @return supplemental hash code value
+ */
+ private static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /**
+ * Checks for equality of non-null reference x and possibly-null y. By
+ * default uses Object.equals.
+ * @param x first object to compare
+ * @param y second object to compare
+ * @return <tt>true</tt> if objects are equal
+ */
+ private static boolean eq(Object x, Object y) {
+ return x == y || x.equals(y);
+ }
+
+ /**
+ * Returns index for hash code h.
+ * @param h raw hash code
+ * @param length length of table (power of 2)
+ * @return index in table
+ */
+ private static int indexFor(int h, int length) {
+ return h & (length-1);
+ }
+
+ /**
+ * Expunges stale entries from the table.
+ */
+ private void expungeStaleEntries() {
+ for (Object x; (x = queue.poll()) != null; ) {
+ synchronized (queue) {
+ Entry entry = (Entry) x;
+ int i = indexFor(entry.hash, table.length);
+ Entry prev = table[i];
+ Entry p = prev;
+ while (p != null) {
+ Entry next = p.next;
+ if (p == entry) {
+ if (prev == entry)
+ table[i] = next;
+ else
+ prev.next = next;
+ entry.next = null;
+ size--;
+ break;
+ }
+ prev = p;
+ p = next;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the table after first expunging stale entries.
+ * @return an expunged hash table
+ */
+ private Entry[] getTable() {
+ expungeStaleEntries();
+ return table;
+ }
+
+ /**
+ * Returns the entry to which the specified value is mapped,
+ * or {@code null} if this set contains no entry for the value.
+ *
+ * <p>More formally, if this set contains an entry for value
+ * {@code entry} to a value {@code value} such that
+ * {@code entry.equals(value)}, then this method returns {@code entry};
+ * otherwise it returns {@code null}.
+ *
+ * @param value value to search for in set
+ * @return interned value if in set, otherwise <tt>null</tt>
+ */
+ synchronized MethodType get(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int index = indexFor(h, tab.length);
+ Entry e = tab[index];
+ MethodType g;
+ while (e != null) {
+ if (e.hash == h && eq(value, g = e.get()))
+ return g;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to add the specified value to the set and returns same value.
+ * If the set previously contained an entry for this value, the old
+ * value is left untouched and returned as the result.
+ *
+ * @param value value to be added
+ * @return the previous entry associated with <tt>value</tt>, or
+ * <tt>value</tt> if there was no previous entry found
+ */
+ synchronized MethodType add(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int i = indexFor(h, tab.length);
+ MethodType g;
+ for (Entry e = tab[i]; e != null; e = e.next) {
+ if (h == e.hash && eq(value, g = e.get())) {
+ return g;
+ }
+ }
+ Entry e = tab[i];
+ tab[i] = new Entry(value, queue, h, e);
+ if (++size >= threshold)
+ resize(tab.length * 2);
+ return value;
+ }
+
+ /**
+ * Rehashes the contents of this set into a new array with a
+ * larger capacity. This method is called automatically when the
+ * number of keys in this set reaches its threshold.
+ *
+ * If current capacity is MAXIMUM_CAPACITY, this method does not
+ * resize the set, but sets threshold to Integer.MAX_VALUE.
+ * This has the effect of preventing future calls.
+ *
+ * @param newCapacity the new capacity, MUST be a power of two;
+ * must be greater than current capacity unless current
+ * capacity is MAXIMUM_CAPACITY (in which case value
+ * is irrelevant)
+ */
+ private void resize(int newCapacity) {
+ Entry[] oldTable = getTable();
+ int oldCapacity = oldTable.length;
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ Entry[] newTable = newTable(newCapacity);
+ transfer(oldTable, newTable);
+ table = newTable;
+
+ /*
+ * If ignoring null elements and processing ref queue caused massive
+ * shrinkage, then restore old table. This should be rare, but avoids
+ * unbounded expansion of garbage-filled tables.
+ */
+ if (size >= threshold / 2) {
+ threshold = (int)(newCapacity * loadFactor);
+ } else {
+ expungeStaleEntries();
+ transfer(newTable, oldTable);
+ table = oldTable;
+ }
+ }
+
+ /**
+ * Transfers all entries from src to dest tables
+ * @param src original table
+ * @param dest new table
+ */
+ private void transfer(Entry[] src, Entry[] dest) {
+ for (int j = 0; j < src.length; ++j) {
+ Entry e = src[j];
+ src[j] = null;
+ while (e != null) {
+ Entry next = e.next;
+ MethodType key = e.get();
+ if (key == null) {
+ e.next = null; // Help GC
+ size--;
+ } else {
+ int i = indexFor(e.hash, dest.length);
+ e.next = dest[i];
+ dest[i] = e;
+ }
+ e = next;
+ }
+ }
+ }
+
+ /**
+ * The entries in this hash table extend WeakReference, using its main ref
+ * field as the key.
+ */
+ private static class Entry extends WeakReference<MethodType> {
+ final int hash;
+ Entry next;
+
+ /**
+ * Creates new entry.
+ */
+ Entry(MethodType key,
+ ReferenceQueue<Object> queue,
+ int hash, Entry next) {
+ super(key, queue);
+ this.hash = hash;
+ this.next = next;
+ }
+ }
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Mon Aug 27 10:58:40 2012 -0700
@@ -27,6 +27,7 @@
import sun.invoke.util.Wrapper;
import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
/**
* Shared information for a group of method types, which differ
@@ -41,27 +42,70 @@
* No more than half of these are likely to be loaded at once.
* @author John Rose
*/
-class MethodTypeForm {
+final class MethodTypeForm {
final int[] argToSlotTable, slotToArgTable;
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
- /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
- /*lazy*/ MethodType primsAsInts; // replace prims by int/long
- /*lazy*/ MethodType primsAsLongs; // replace prims by long
- /*lazy*/ MethodType primsAtEnd; // reorder primitives to the end
+ final MethodType basicType; // the canonical erasure, with primitives simplified
// Cached adapter information:
- /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
+ /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke
+ /*lazy*/ MethodHandle basicInvoker; // cached instance of MH.invokeBasic
+ /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
+
+ // Cached lambda form information, for basic types only:
+ final LambdaForm[] lambdaForms;
+ // Indexes into lambdaForms:
+ static final int
+ LF_INVVIRTUAL = 0, // DMH invokeVirtual
+ LF_INVSTATIC = 1,
+ LF_INVSPECIAL = 2,
+ LF_NEWINVSPECIAL = 3,
+ LF_INVINTERFACE = 4,
+ LF_INVSTATIC_INIT = 5, // DMH invokeStatic with <clinit> barrier
+ LF_INTERPRET = 6, // LF interpreter
+ LF_COUNTER = 7, // CMH wrapper
+ LF_REINVOKE = 8, // other wrapper
+ LF_EX_LINKER = 9, // invokeExact_MT
+ LF_EX_INVOKER = 10, // invokeExact MH
+ LF_GEN_LINKER = 11,
+ LF_GEN_INVOKER = 12,
+ LF_CS_LINKER = 13, // linkToCallSite_CS
+ LF_LIMIT = 14;
public MethodType erasedType() {
return erasedType;
}
+ public MethodType basicType() {
+ return basicType;
+ }
+
+ public LambdaForm cachedLambdaForm(int which) {
+ return lambdaForms[which];
+ }
+
+ public LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
+ // Should we perform some sort of CAS, to avoid racy duplication?
+ return lambdaForms[which] = form;
+ }
+
+ public MethodHandle basicInvoker() {
+ assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also
+ MethodHandle invoker = basicInvoker;
+ if (invoker != null) return invoker;
+ invoker = basicType.invokers().makeBasicInvoker();
+ basicInvoker = invoker;
+ return invoker;
+ }
+
+ /**
+ * Build an MTF for a given type, which must have all references erased to Object.
+ * This MTF will stand for that type and all un-erased variations.
+ * Eagerly compute some basic properties of the type, common to all variations.
+ */
protected MethodTypeForm(MethodType erasedType) {
this.erasedType = erasedType;
@@ -75,26 +119,41 @@
// Walk the argument types, looking for primitives.
int pac = 0, lac = 0, prc = 0, lrc = 0;
- Class<?> epts[] = ptypes;
+ Class<?>[] epts = ptypes;
+ Class<?>[] bpts = epts;
for (int i = 0; i < epts.length; i++) {
Class<?> pt = epts[i];
if (pt != Object.class) {
- assert(pt.isPrimitive());
++pac;
- if (hasTwoArgSlots(pt)) ++lac;
+ Wrapper w = Wrapper.forPrimitiveType(pt);
+ if (w.isDoubleWord()) ++lac;
+ if (w.isSubwordOrInt() && pt != int.class) {
+ if (bpts == epts)
+ bpts = bpts.clone();
+ bpts[i] = int.class;
+ }
}
}
pslotCount += lac; // #slots = #args + #longs
Class<?> rt = erasedType.returnType();
+ Class<?> bt = rt;
if (rt != Object.class) {
++prc; // even void.class counts as a prim here
- if (hasTwoArgSlots(rt)) ++lrc;
+ Wrapper w = Wrapper.forPrimitiveType(rt);
+ if (w.isDoubleWord()) ++lrc;
+ if (w.isSubwordOrInt() && rt != int.class)
+ bt = int.class;
// adjust #slots, #args
if (rt == void.class)
rtypeCount = rslotCount = 0;
else
rslotCount += lrc;
}
+ if (epts == bpts && bt == rt) {
+ this.basicType = erasedType;
+ } else {
+ this.basicType = MethodType.makeImpl(bt, bpts, true);
+ }
if (lac != 0) {
int slot = ptypeCount + lac;
slotToArgTab = new int[slot+1];
@@ -102,7 +161,8 @@
argToSlotTab[0] = slot; // argument "-1" is past end of slots
for (int i = 0; i < epts.length; i++) {
Class<?> pt = epts[i];
- if (hasTwoArgSlots(pt)) --slot;
+ Wrapper w = Wrapper.forBasicType(pt);
+ if (w.isDoubleWord()) --slot;
--slot;
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
argToSlotTab[1+i] = slot;
@@ -130,164 +190,13 @@
// send a few bits down to the JVM:
this.vmslots = parameterSlotCount();
- // short circuit some no-op canonicalizations:
- if (!hasPrimitives()) {
- primsAsBoxes = erasedType;
- primArgsAsBoxes = erasedType;
- primsAsInts = erasedType;
- primsAsLongs = erasedType;
- primsAtEnd = erasedType;
+ if (basicType == erasedType) {
+ lambdaForms = new LambdaForm[LF_LIMIT];
+ } else {
+ lambdaForms = null; // could be basicType.form().lambdaForms;
}
}
- /** Turn all primitive types to corresponding wrapper types.
- */
- public MethodType primsAsBoxes() {
- MethodType ct = primsAsBoxes;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(erasedType, WRAP, WRAP);
- if (ct == null) ct = t; // no prims to box
- return primsAsBoxes = ct;
- }
-
- /** Turn all primitive argument types to corresponding wrapper types.
- * Subword and void return types are promoted to int.
- */
- public MethodType primArgsAsBoxes() {
- MethodType ct = primArgsAsBoxes;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(erasedType, RAW_RETURN, WRAP);
- if (ct == null) ct = t; // no prims to box
- return primArgsAsBoxes = ct;
- }
-
- /** Turn all primitive types to either int or long.
- * Floating point return types are not changed, because
- * they may require special calling sequences.
- * A void return value is turned to int.
- */
- public MethodType primsAsInts() {
- MethodType ct = primsAsInts;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(t, RAW_RETURN, INTS);
- if (ct == null) ct = t; // no prims to int-ify
- return primsAsInts = ct;
- }
-
- /** Turn all primitive types to either int or long.
- * Floating point return types are not changed, because
- * they may require special calling sequences.
- * A void return value is turned to int.
- */
- public MethodType primsAsLongs() {
- MethodType ct = primsAsLongs;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(t, RAW_RETURN, LONGS);
- if (ct == null) ct = t; // no prims to int-ify
- return primsAsLongs = ct;
- }
-
- /** Stably sort parameters into 3 buckets: ref, int, long. */
- public MethodType primsAtEnd() {
- MethodType ct = primsAtEnd;
- if (ct != null) return ct;
- MethodType t = erasedType;
-
- int pac = primitiveParameterCount();
- if (pac == 0)
- return primsAtEnd = t;
-
- int argc = parameterCount();
- int lac = longPrimitiveParameterCount();
- if (pac == argc && (lac == 0 || lac == argc))
- return primsAtEnd = t;
-
- // known to have a mix of 2 or 3 of ref, int, long
- int[] reorder = primsAtEndOrder(t);
- ct = reorderParameters(t, reorder, null);
- //System.out.println("t="+t+" / reorder="+java.util.Arrays.toString(reorder)+" => "+ct);
- return primsAtEnd = ct;
- }
-
- /** Compute a new ordering of parameters so that all references
- * are before all ints or longs, and all ints are before all longs.
- * For this ordering, doubles count as longs, and all other primitive
- * values count as ints.
- * As a special case, if the parameters are already in the specified
- * order, this method returns a null reference, rather than an array
- * specifying a null permutation.
- * <p>
- * For example, the type {@code (int,boolean,int,Object,String)void}
- * produces the order {@code {3,4,0,1,2}}, the type
- * {@code (long,int,String)void} produces {@code {2,1,2}}, and
- * the type {@code (Object,int)Object} produces {@code null}.
- */
- public static int[] primsAtEndOrder(MethodType mt) {
- MethodTypeForm form = mt.form();
- if (form.primsAtEnd == form.erasedType)
- // quick check shows no reordering is necessary
- return null;
-
- int argc = form.parameterCount();
- int[] paramOrder = new int[argc];
-
- // 3-way bucket sort:
- int pac = form.primitiveParameterCount();
- int lac = form.longPrimitiveParameterCount();
- int rfill = 0, ifill = argc - pac, lfill = argc - lac;
-
- Class<?>[] ptypes = mt.ptypes();
- boolean changed = false;
- for (int i = 0; i < ptypes.length; i++) {
- Class<?> pt = ptypes[i];
- int ord;
- if (!pt.isPrimitive()) ord = rfill++;
- else if (!hasTwoArgSlots(pt)) ord = ifill++;
- else ord = lfill++;
- if (ord != i) changed = true;
- assert(paramOrder[ord] == 0);
- paramOrder[ord] = i;
- }
- assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
- if (!changed) {
- form.primsAtEnd = form.erasedType;
- return null;
- }
- return paramOrder;
- }
-
- /** Put the existing parameters of mt into a new order, given by newParamOrder.
- * The third argument is logically appended to mt.parameterArray,
- * so that elements of newParamOrder can index either pre-existing or
- * new parameter types.
- */
- public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) {
- if (newParamOrder == null) return mt; // no-op reordering
- Class<?>[] ptypes = mt.ptypes();
- Class<?>[] ntypes = new Class<?>[newParamOrder.length];
- int maxParam = ptypes.length + (moreParams == null ? 0 : moreParams.length);
- boolean changed = (ntypes.length != ptypes.length);
- for (int i = 0; i < newParamOrder.length; i++) {
- int param = newParamOrder[i];
- if (param != i) changed = true;
- Class<?> nt;
- if (param < ptypes.length) nt = ptypes[param];
- else if (param == maxParam) nt = mt.returnType();
- else nt = moreParams[param - ptypes.length];
- ntypes[i] = nt;
- }
- if (!changed) return mt;
- return MethodType.makeImpl(mt.returnType(), ntypes, true);
- }
-
- private static boolean hasTwoArgSlots(Class<?> type) {
- return type == long.class || type == double.class;
- }
-
private static long pack(int a, int b, int c, int d) {
assert(((a|b|c|d) & ~0xFFFF) == 0);
long hw = ((a << 16) | b), lw = ((c << 16) | d);
@@ -325,11 +234,11 @@
public boolean hasPrimitives() {
return primCounts != 0;
}
-// public boolean hasNonVoidPrimitives() {
-// if (primCounts == 0) return false;
-// if (primitiveParameterCount() != 0) return true;
-// return (primitiveReturnCount() != 0 && returnCount() != 0);
-// }
+ public boolean hasNonVoidPrimitives() {
+ if (primCounts == 0) return false;
+ if (primitiveParameterCount() != 0) return true;
+ return (primitiveReturnCount() != 0 && returnCount() != 0);
+ }
public boolean hasLongPrimitives() {
return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
}
@@ -455,18 +364,6 @@
return cs;
}
- /*non-public*/ void notifyGenericMethodType() {
- if (genericInvoker != null) return;
- try {
- // Trigger adapter creation.
- genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
- } catch (Exception ex) {
- Error err = new InternalError("Exception while resolving inexact invoke", ex);
- err.initCause(ex);
- throw err;
- }
- }
-
@Override
public String toString() {
return "Form"+erasedType;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,66 @@
+/*
+ * 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
+ * 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.
+ */
+
+package java.lang.invoke;
+
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A method handle whose behavior is determined only by its LambdaForm.
+ * @author jrose
+ */
+final class SimpleMethodHandle extends MethodHandle {
+ SimpleMethodHandle(MethodType type, LambdaForm form) {
+ super(type, form);
+ }
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ MethodType type2 = type().dropParameterTypes(pos, pos+1);
+ LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
+ return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
+ }
+
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+ return new SimpleMethodHandle(srcType, newForm);
+ }
+
+ @Override
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
+ return new SimpleMethodHandle(newType, form2);
+ }
+
+ @Override
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ return new SimpleMethodHandle(mt, lf);
+ }
+
+}
--- a/jdk/src/share/classes/java/lang/invoke/package-info.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/package-info.java Mon Aug 27 10:58:40 2012 -0700
@@ -191,6 +191,13 @@
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in a {@code BootstrapMethodError}.)
* <p>
+ * Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
+ * argument, if it can be represented by a constant pool entry.
+ * However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
+ * cannot be created for bootstrap methods, since such constants cannot be directly
+ * represented in the constant pool, and the invocation of the bootstrap method will
+ * not perform the necessary narrowing primitive conversions.
+ * <p>
* Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata.
* In principle, the name and extra arguments are redundant,
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Mon Aug 27 10:58:40 2012 -0700
@@ -118,42 +118,15 @@
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, boolean cast) {
- return unboxByte(x, cast);
- }
-
- static int unboxShortRaw(Object x, boolean cast) {
- return unboxShort(x, cast);
- }
-
- static int unboxBooleanRaw(Object x, boolean cast) {
- return unboxBoolean(x, cast) ? 1 : 0;
- }
-
- static int unboxCharacterRaw(Object x, boolean cast) {
- return unboxCharacter(x, cast);
- }
-
- static int unboxFloatRaw(Object x, boolean cast) {
- return Float.floatToIntBits(unboxFloat(x, cast));
- }
-
- 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(), Object.class, boolean.class);
+ private static MethodType unboxType(Wrapper wrap) {
+ return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
}
private static final EnumMap<Wrapper, MethodHandle>[]
- UNBOX_CONVERSIONS = newWrapperCaches(4);
+ UNBOX_CONVERSIONS = newWrapperCaches(2);
- private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
- EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
+ private static MethodHandle unbox(Wrapper wrap, boolean cast) {
+ EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -163,19 +136,15 @@
case OBJECT:
mh = IDENTITY; break;
case VOID:
- mh = raw ? ALWAYS_ZERO : IGNORE; break;
- case INT: case LONG:
- // these guys don't need separate raw channels
- if (raw) mh = unbox(wrap, false, cast);
- break;
+ mh = IGNORE; break;
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
}
// look up the method
- String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
- MethodType type = unboxType(wrap, raw);
+ String name = "unbox" + wrap.wrapperSimpleName();
+ MethodType type = unboxType(wrap);
try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
} catch (ReflectiveOperationException ex) {
@@ -187,32 +156,30 @@
return mh;
}
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
- + (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
+ + (cast ? " (cast)" : ""));
}
public static MethodHandle unboxCast(Wrapper type) {
- return unbox(type, false, true);
- }
-
- public static MethodHandle unboxRaw(Wrapper type) {
- return unbox(type, true, false);
+ return unbox(type, true);
}
public static MethodHandle unbox(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), false, false);
+ return unbox(Wrapper.forPrimitiveType(type), false);
}
public static MethodHandle unboxCast(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), false, true);
- }
-
- public static MethodHandle unboxRaw(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), true, false);
+ return unbox(Wrapper.forPrimitiveType(type), true);
}
static private final Integer ZERO_INT = 0, ONE_INT = 1;
/// Primitive conversions
+ /**
+ * Produce a Number which represents the given value {@code x}
+ * according to the primitive type of the given wrapper {@code wrap}.
+ * Caller must invoke intValue, byteValue, longValue (etc.) on the result
+ * to retrieve the desired primitive value.
+ */
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
// Maybe merge this code with Wrapper.convert/cast.
Number res = null;
@@ -237,6 +204,27 @@
return res;
}
+ /**
+ * The JVM verifier allows boolean, byte, short, or char to widen to int.
+ * Support exactly this conversion, from a boxed value type Boolean,
+ * Byte, Short, Character, or Integer.
+ */
+ public static int widenSubword(Object x) {
+ if (x instanceof Integer)
+ return (int) x;
+ else if (x instanceof Boolean)
+ return fromBoolean((boolean) x);
+ else if (x instanceof Character)
+ return (char) x;
+ else if (x instanceof Short)
+ return (short) x;
+ else if (x instanceof Byte)
+ return (byte) x;
+ else
+ // Fail with a ClassCastException.
+ return (int) x;
+ }
+
/// Converting primitives to references
static Integer boxInteger(int x) {
@@ -271,53 +259,17 @@
return x;
}
- /// Converting raw primitives to references
-
- static Byte boxByteRaw(int x) {
- return boxByte((byte)x);
- }
-
- static Short boxShortRaw(int x) {
- return boxShort((short)x);
- }
-
- static Boolean boxBooleanRaw(int x) {
- return boxBoolean(x != 0);
- }
-
- static Character boxCharacterRaw(int x) {
- return boxCharacter((char)x);
- }
-
- static Float boxFloatRaw(int x) {
- return boxFloat(Float.intBitsToFloat(x));
- }
-
- static Double boxDoubleRaw(long x) {
- return boxDouble(Double.longBitsToDouble(x));
- }
-
- // a raw void value is (arbitrarily) a garbage int
- static Void boxVoidRaw(int x) {
- return null;
- }
-
- private static MethodType boxType(Wrapper wrap, boolean raw) {
+ private static MethodType boxType(Wrapper wrap) {
// be exact, since return casts are hard to compose
Class<?> boxType = wrap.wrapperType();
- return MethodType.methodType(boxType, rawWrapper(wrap, raw).primitiveType());
- }
-
- private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
- if (raw) return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
- return wrap;
+ return MethodType.methodType(boxType, wrap.primitiveType());
}
private static final EnumMap<Wrapper, MethodHandle>[]
- BOX_CONVERSIONS = newWrapperCaches(4);
+ BOX_CONVERSIONS = newWrapperCaches(2);
- private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
- EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+ private static MethodHandle box(Wrapper wrap, boolean exact) {
+ EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -327,11 +279,7 @@
case OBJECT:
mh = IDENTITY; break;
case VOID:
- if (!raw) mh = ZERO_OBJECT;
- break;
- case INT: case LONG:
- // these guys don't need separate raw channels
- if (raw) mh = box(wrap, exact, false);
+ mh = ZERO_OBJECT;
break;
}
if (mh != null) {
@@ -339,8 +287,8 @@
return mh;
}
// look up the method
- String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
- MethodType type = boxType(wrap, raw);
+ String name = "box" + wrap.wrapperSimpleName();
+ MethodType type = boxType(wrap);
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
@@ -348,160 +296,26 @@
mh = null;
}
} else {
- mh = box(wrap, !exact, raw).asType(type.erase());
+ mh = box(wrap, !exact).asType(type.erase());
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
}
throw new IllegalArgumentException("cannot find box adapter for "
- + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
+ + wrap + (exact ? " (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 = false;
- // e.g., boxShortRaw(int)Short if exact
- // e.g., boxShortRaw(int)Object if !exact
- return box(Wrapper.forPrimitiveType(type), exact, true);
+ return box(Wrapper.forPrimitiveType(type), exact);
}
public static MethodHandle box(Wrapper type) {
boolean exact = false;
- return box(type, exact, false);
- }
-
- public static MethodHandle boxRaw(Wrapper type) {
- boolean exact = false;
- return box(type, exact, true);
- }
-
- /// Kludges for when raw values get accidentally boxed.
-
- static int unboxRawInteger(Object x) {
- if (x instanceof Integer)
- return (int) x;
- else
- return (int) unboxLong(x, false);
- }
-
- static Integer reboxRawInteger(Object x) {
- if (x instanceof Integer)
- return (Integer) x;
- else
- return (int) unboxLong(x, false);
- }
-
- static Byte reboxRawByte(Object x) {
- if (x instanceof Byte) return (Byte) x;
- return boxByteRaw(unboxRawInteger(x));
- }
-
- static Short reboxRawShort(Object x) {
- if (x instanceof Short) return (Short) x;
- return boxShortRaw(unboxRawInteger(x));
- }
-
- static Boolean reboxRawBoolean(Object x) {
- if (x instanceof Boolean) return (Boolean) x;
- return boxBooleanRaw(unboxRawInteger(x));
- }
-
- static Character reboxRawCharacter(Object x) {
- if (x instanceof Character) return (Character) x;
- return boxCharacterRaw(unboxRawInteger(x));
- }
-
- static Float reboxRawFloat(Object x) {
- if (x instanceof Float) return (Float) x;
- return boxFloatRaw(unboxRawInteger(x));
- }
-
- static Long reboxRawLong(Object x) {
- return (Long) x; //never a rebox
- }
-
- static Double reboxRawDouble(Object x) {
- if (x instanceof Double) return (Double) x;
- return boxDoubleRaw(unboxLong(x, true));
- }
-
- private static MethodType reboxType(Wrapper wrap) {
- Class<?> boxType = wrap.wrapperType();
- return MethodType.methodType(boxType, Object.class);
- }
-
- private static final EnumMap<Wrapper, MethodHandle>[]
- REBOX_CONVERSIONS = newWrapperCaches(1);
-
- /**
- * Because we normalize primitive types to reduce the number of signatures,
- * primitives are sometimes manipulated under an "erased" type,
- * either int (for types other than long/double) or long (for all types).
- * 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, typed (inexactly) as an Object
- */
- public static MethodHandle rebox(Wrapper wrap) {
- EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
- MethodHandle mh = cache.get(wrap);
- if (mh != null) {
- return mh;
- }
- // slow path
- switch (wrap) {
- case OBJECT:
- mh = IDENTITY; break;
- case VOID:
- throw new IllegalArgumentException("cannot rebox a void");
- }
- if (mh != null) {
- cache.put(wrap, mh);
- return mh;
- }
- // look up the method
- String name = "reboxRaw" + wrap.simpleName();
- MethodType type = reboxType(wrap);
- 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);
- return mh;
- }
- throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
- }
-
- public static MethodHandle rebox(Class<?> type) {
- return rebox(Wrapper.forPrimitiveType(type));
- }
-
- /// Width-changing conversions between int and long.
-
- static long widenInt(int x) {
- return (long) x;
- }
-
- static Long widenBoxedInt(Integer x) {
- return (long)(int)x;
- }
-
- static int narrowLong(long x) {
- return (int) x;
- }
-
- static Integer narrowBoxedLong(Long x) {
- return (int)(long) x;
+ return box(type, exact);
}
/// Constant functions
@@ -553,7 +367,7 @@
case OBJECT:
case INT: case LONG: case FLOAT: case DOUBLE:
try {
- mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type);
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);
} catch (ReflectiveOperationException ex) {
mh = null;
}
@@ -564,12 +378,9 @@
return mh;
}
- // use the raw method
- Wrapper rawWrap = wrap.rawPrimitive();
- if (mh == null && rawWrap != wrap) {
- mh = MethodHandles.explicitCastArguments(zeroConstantFunction(rawWrap), type);
- }
- if (mh != null) {
+ // use zeroInt and cast the result
+ if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
+ mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
cache.put(wrap, mh);
return mh;
}
@@ -657,7 +468,7 @@
return t.cast(x);
}
- private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
+ private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
static {
try {
MethodType idType = MethodType.genericMethodType(1);
@@ -666,8 +477,6 @@
MethodType ignoreType = idType.changeReturnType(void.class);
MethodType zeroObjectType = MethodType.genericMethodType(0);
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(THIS_CLASS, "castReference", castType);
ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
@@ -723,7 +532,6 @@
if (exact) {
MethodType xmt = MethodType.methodType(type, Object.class);
mh = MethodHandles.explicitCastArguments(mh, xmt);
- //mh = AdapterMethodHandle.makeRetypeRaw(IMPL_TOKEN, xmt, mh);
}
if (cache != null)
cache.put(wrap, mh);
@@ -735,8 +543,10 @@
}
public static MethodHandle identity(Class<?> type) {
- // This stuff has been moved into MethodHandles:
- return MethodHandles.identity(type);
+ if (!type.isPrimitive())
+ // Reference identity has been moved into MethodHandles:
+ return MethodHandles.identity(type);
+ return identity(Wrapper.findPrimitiveType(type));
}
public static MethodHandle identity(Wrapper wrap) {
@@ -769,95 +579,203 @@
throw new IllegalArgumentException("cannot find identity for " + wrap);
}
- /// Float/non-float conversions.
+ /// Primitive conversions.
+ // These are supported directly by the JVM, usually by a single instruction.
+ // In the case of narrowing to a subword, there may be a pair of instructions.
+ // In the case of booleans, there may be a helper routine to manage a 1-bit value.
+ // This is the full 8x8 matrix (minus the diagonal).
- static float doubleToFloat(double x) {
+ // narrow double to all other types:
+ static float doubleToFloat(double x) { // bytecode: d2f
return (float) x;
}
- static double floatToDouble(float x) {
- return x;
- }
-
- // narrow double to integral type
- static long doubleToLong(double x) {
+ static long doubleToLong(double x) { // bytecode: d2l
return (long) x;
}
- static int doubleToInt(double x) {
+ static int doubleToInt(double x) { // bytecode: d2i
return (int) x;
}
- static short doubleToShort(double x) {
+ static short doubleToShort(double x) { // bytecodes: d2i, i2s
return (short) x;
}
- static char doubleToChar(double x) {
+ static char doubleToChar(double x) { // bytecodes: d2i, i2c
return (char) x;
}
- static byte doubleToByte(double x) {
+ static byte doubleToByte(double x) { // bytecodes: d2i, i2b
return (byte) x;
}
static boolean doubleToBoolean(double x) {
return toBoolean((byte) x);
}
- // narrow float to integral type
- static long floatToLong(float x) {
+ // widen float:
+ static double floatToDouble(float x) { // bytecode: f2d
+ return x;
+ }
+ // narrow float:
+ static long floatToLong(float x) { // bytecode: f2l
return (long) x;
}
- static int floatToInt(float x) {
+ static int floatToInt(float x) { // bytecode: f2i
return (int) x;
}
- static short floatToShort(float x) {
+ static short floatToShort(float x) { // bytecodes: f2i, i2s
return (short) x;
}
- static char floatToChar(float x) {
+ static char floatToChar(float x) { // bytecodes: f2i, i2c
return (char) x;
}
- static byte floatToByte(float x) {
+ static byte floatToByte(float x) { // bytecodes: f2i, i2b
return (byte) x;
}
static boolean floatToBoolean(float x) {
return toBoolean((byte) x);
}
- // widen integral type to double
- static double longToDouble(long x) {
+ // widen long:
+ static double longToDouble(long x) { // bytecode: l2d
+ return x;
+ }
+ static float longToFloat(long x) { // bytecode: l2f
+ return x;
+ }
+ // narrow long:
+ static int longToInt(long x) { // bytecode: l2i
+ return (int) x;
+ }
+ static short longToShort(long x) { // bytecodes: f2i, i2s
+ return (short) x;
+ }
+ static char longToChar(long x) { // bytecodes: f2i, i2c
+ return (char) x;
+ }
+ static byte longToByte(long x) { // bytecodes: f2i, i2b
+ return (byte) x;
+ }
+ static boolean longToBoolean(long x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen int:
+ static double intToDouble(int x) { // bytecode: i2d
return x;
}
- static double intToDouble(int x) {
+ static float intToFloat(int x) { // bytecode: i2f
+ return x;
+ }
+ static long intToLong(int x) { // bytecode: i2l
+ return x;
+ }
+ // narrow int:
+ static short intToShort(int x) { // bytecode: i2s
+ return (short) x;
+ }
+ static char intToChar(int x) { // bytecode: i2c
+ return (char) x;
+ }
+ static byte intToByte(int x) { // bytecode: i2b
+ return (byte) x;
+ }
+ static boolean intToBoolean(int x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen short:
+ static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i')
+ return x;
+ }
+ static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i')
+ return x;
+ }
+ static long shortToLong(short x) { // bytecode: i2l (implicit 's2i')
+ return x;
+ }
+ static int shortToInt(short x) { // (implicit 's2i')
return x;
}
- static double shortToDouble(short x) {
+ // narrow short:
+ static char shortToChar(short x) { // bytecode: i2c (implicit 's2i')
+ return (char)x;
+ }
+ static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i')
+ return (byte)x;
+ }
+ static boolean shortToBoolean(short x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen char:
+ static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i')
+ return x;
+ }
+ static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i')
+ return x;
+ }
+ static long charToLong(char x) { // bytecode: i2l (implicit 'c2i')
+ return x;
+ }
+ static int charToInt(char x) { // (implicit 'c2i')
return x;
}
- static double charToDouble(char x) {
+ // narrow char:
+ static short charToShort(char x) { // bytecode: i2s (implicit 'c2i')
+ return (short)x;
+ }
+ static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i')
+ return (byte)x;
+ }
+ static boolean charToBoolean(char x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen byte:
+ static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i')
+ return x;
+ }
+ static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i')
return x;
}
- static double byteToDouble(byte x) {
+ static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i')
+ return x;
+ }
+ static int byteToInt(byte x) { // (implicit 'b2i')
return x;
}
+ static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i')
+ return (short)x;
+ }
+ static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i')
+ return (char)x;
+ }
+ // narrow byte to boolean:
+ static boolean byteToBoolean(byte x) {
+ return toBoolean(x);
+ }
+
+ // widen boolean to all types:
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 long booleanToLong(boolean x) {
+ return fromBoolean(x);
+ }
+ static int booleanToInt(boolean x) {
+ return fromBoolean(x);
+ }
+ static short booleanToShort(boolean x) {
+ return fromBoolean(x);
+ }
+ static char booleanToChar(boolean x) {
+ return (char)fromBoolean(x);
+ }
+ static byte booleanToByte(boolean x) {
+ return fromBoolean(x);
+ }
+ // helpers to force boolean into the conversion scheme:
static boolean toBoolean(byte x) {
// see javadoc for MethodHandles.explicitCastArguments
return ((x & 1) != 0);
@@ -868,62 +786,48 @@
}
private static final EnumMap<Wrapper, MethodHandle>[]
- CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4);
+ CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
- 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);
+ public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
+ EnumMap<Wrapper, MethodHandle> cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
+ MethodHandle mh = cache.get(wdst);
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;
+ Class<?> src = wsrc.primitiveType();
+ Class<?> dst = wdst.primitiveType();
+ MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src);
+ if (wsrc == wdst) {
+ mh = identity(src);
+ } else if (wsrc == Wrapper.VOID) {
+ mh = zeroConstantFunction(wdst);
+ } else if (wdst == Wrapper.VOID) {
+ mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles.
+ } else if (wsrc == Wrapper.OBJECT) {
+ mh = unboxCast(dst);
+ } else if (wdst == Wrapper.OBJECT) {
+ mh = box(src);
+ } else {
+ assert(src.isPrimitive() && dst.isPrimitive());
+ try {
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
+ }
}
if (mh != null) {
assert(mh.type() == type) : mh;
- cache.put(wrap, mh);
+ cache.put(wdst, mh);
return mh;
}
- throw new IllegalArgumentException("cannot find float conversion constant for " +
+ throw new IllegalArgumentException("cannot find primitive conversion function 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);
+ public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {
+ return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));
}
private static String capitalize(String x) {
--- a/jdk/src/share/classes/sun/invoke/util/VerifyAccess.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyAccess.java Mon Aug 27 10:58:40 2012 -0700
@@ -169,6 +169,46 @@
}
/**
+ * Decide if the given method type, attributed to a member or symbolic
+ * reference of a given reference class, is really visible to that class.
+ * @param type the supposed type of a member or symbolic reference of refc
+ * @param refc
+ */
+ public static boolean isTypeVisible(Class<?> type, Class<?> refc) {
+ if (type == refc) return true; // easy check
+ while (type.isArray()) type = type.getComponentType();
+ if (type.isPrimitive() || type == Object.class) return true;
+ ClassLoader parent = type.getClassLoader();
+ if (parent == null) return true;
+ ClassLoader child = refc.getClassLoader();
+ if (child == null) return false;
+ if (parent == child || loadersAreRelated(parent, child, true))
+ return true;
+ // Do it the hard way: Look up the type name from the refc loader.
+ try {
+ Class<?> res = child.loadClass(type.getName());
+ return (type == res);
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Decide if the given method type, attributed to a member or symbolic
+ * reference of a given reference class, is really visible to that class.
+ * @param type the supposed type of a member or symbolic reference of refc
+ * @param refc
+ */
+ public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {
+ for (int n = -1, max = type.parameterCount(); n < max; n++) {
+ Class<?> ptype = (n < 0 ? type.returnType() : type.parameterType(n));
+ if (!isTypeVisible(ptype, refc))
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Test if two classes have the same class loader and package qualifier.
* @param class1
* @param class2
--- a/jdk/src/share/classes/sun/invoke/util/VerifyType.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyType.java Mon Aug 27 10:58:40 2012 -0700
@@ -122,8 +122,6 @@
return isNullConversion(recv.returnType(), call.returnType());
}
- //TO DO: isRawConversion
-
/**
* Determine if the JVM verifier allows a value of type call to be
* passed to a formal parameter (or return variable) of type recv.
@@ -188,40 +186,6 @@
return -1;
}
- public static int canPassRaw(Class<?> src, Class<?> dst) {
- if (dst.isPrimitive()) {
- if (dst == void.class)
- // As above, return anything to a caller expecting void.
- return 1;
- if (src == void.class)
- // Special permission for raw conversions: allow a void
- // to be captured as a garbage int.
- // Caller promises that the actual value will be disregarded.
- return dst == int.class ? 1 : 0;
- if (isNullType(src))
- // Special permission for raw conversions: allow a null
- // to be reinterpreted as anything. For objects, it is safe,
- // and for primitives you get a garbage value (probably zero).
- return 1;
- if (!src.isPrimitive())
- return 0;
- Wrapper sw = Wrapper.forPrimitiveType(src);
- Wrapper dw = Wrapper.forPrimitiveType(dst);
- if (sw.stackSlots() == dw.stackSlots())
- return 1; // can do a reinterpret-cast on a stacked primitive
- if (sw.isSubwordOrInt() && dw == Wrapper.VOID)
- return 1; // can drop an outgoing int value
- return 0;
- } else if (src.isPrimitive()) {
- return 0;
- }
-
- // Both references.
- if (isNullReferenceConversion(src, dst))
- return 1;
- return -1;
- }
-
public static boolean isSpreadArgType(Class<?> spreadArg) {
return spreadArg.isArray();
}
--- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java Mon Aug 27 10:58:40 2012 -0700
@@ -47,7 +47,8 @@
private final Object zero;
private final Object emptyArray;
private final int format;
- private final String simpleName;
+ private final String wrapperSimpleName;
+ private final String primitiveSimpleName;
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
this.wrapperType = wtype;
@@ -56,12 +57,13 @@
this.zero = zero;
this.emptyArray = emptyArray;
this.format = format;
- this.simpleName = wtype.getSimpleName();
+ this.wrapperSimpleName = wtype.getSimpleName();
+ this.primitiveSimpleName = ptype.getSimpleName();
}
/** For debugging, give the details of this wrapper. */
public String detailString() {
- return simpleName+
+ return wrapperSimpleName+
java.util.Arrays.asList(wrapperType, primitiveType,
basicTypeChar, zero,
"0x"+Integer.toHexString(format));
@@ -418,7 +420,11 @@
/** What is the simple name of the wrapper type?
*/
- public String simpleName() { return simpleName; }
+ public String wrapperSimpleName() { return wrapperSimpleName; }
+
+ /** What is the simple name of the primitive type?
+ */
+ public String primitiveSimpleName() { return primitiveSimpleName; }
// /** Wrap a value in the given type, which may be either a primitive or wrapper type.
// * Performs standard primitive conversions, including truncation and float conversions.
@@ -456,26 +462,31 @@
// If the target type is an interface, perform no runtime check.
// (This loophole is safe, and is allowed by the JVM verifier.)
// If the target type is a primitive, change it to a wrapper.
+ assert(!type.isPrimitive());
+ if (!type.isInterface())
+ type.cast(x);
@SuppressWarnings("unchecked")
T result = (T) x; // unchecked warning is expected here
return result;
}
Class<T> wtype = wrapperType(type);
if (wtype.isInstance(x)) {
- @SuppressWarnings("unchecked")
- T result = (T) x; // unchecked warning is expected here
- return result;
+ return wtype.cast(x);
}
- Class<?> sourceType = x.getClass(); // throw NPE if x is null
if (!isCast) {
+ Class<?> sourceType = x.getClass(); // throw NPE if x is null
Wrapper source = findWrapperType(sourceType);
if (source == null || !this.isConvertibleFrom(source)) {
throw newClassCastException(wtype, sourceType);
}
+ } else if (x == null) {
+ @SuppressWarnings("unchecked")
+ T z = (T) zero;
+ return z;
}
@SuppressWarnings("unchecked")
T result = (T) wrap(x); // unchecked warning is expected here
- assert result.getClass() == wtype;
+ assert (result == null ? Void.class : result.getClass()) == wtype;
return result;
}
@@ -523,7 +534,7 @@
case 'S': return Short.valueOf((short) xn.intValue());
case 'B': return Byte.valueOf((byte) xn.intValue());
case 'C': return Character.valueOf((char) xn.intValue());
- case 'Z': return Boolean.valueOf(boolValue(xn.longValue()));
+ case 'Z': return Boolean.valueOf(boolValue(xn.byteValue()));
}
throw new InternalError("bad wrapper");
}
@@ -546,72 +557,11 @@
case 'S': return Short.valueOf((short) x);
case 'B': return Byte.valueOf((byte) x);
case 'C': return Character.valueOf((char) x);
- case 'Z': return Boolean.valueOf(boolValue(x));
+ case 'Z': return Boolean.valueOf(boolValue((byte) x));
}
throw new InternalError("bad wrapper");
}
- /** Wrap a value (a long or smaller value) in this wrapper's type.
- * Does not perform floating point conversion.
- * Produces a {@code Long} for {@code OBJECT}, although the exact type
- * of the operand is not known.
- * Returns null for {@code VOID}.
- */
- public Object wrapRaw(long x) {
- switch (basicTypeChar) {
- case 'F': return Float.valueOf(Float.intBitsToFloat((int)x));
- case 'D': return Double.valueOf(Double.longBitsToDouble(x));
- case 'L': // same as 'J':
- case 'J': return (Long) x;
- }
- // Other wrapping operations are just the same, given that the
- // operand is already promoted to an int.
- return wrap((int)x);
- }
-
- /** Produce bitwise value which encodes the given wrapped value.
- * Does not perform floating point conversion.
- * Returns zero for {@code VOID}.
- */
- public long unwrapRaw(Object x) {
- switch (basicTypeChar) {
- case 'F': return Float.floatToRawIntBits((Float) x);
- case 'D': return Double.doubleToRawLongBits((Double) x);
-
- case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type");
- case 'V': return 0;
- case 'I': return (int)(Integer) x;
- case 'J': return (long)(Long) x;
- case 'S': return (short)(Short) x;
- case 'B': return (byte)(Byte) x;
- case 'C': return (char)(Character) x;
- case 'Z': return (boolean)(Boolean) x ? 1 : 0;
- }
- throw new InternalError("bad wrapper");
- }
-
- /** Report what primitive type holds this guy's raw value. */
- public Class<?> rawPrimitiveType() {
- return rawPrimitive().primitiveType();
- }
-
- /** Report, as a wrapper, what primitive type holds this guy's raw value.
- * Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE,
- * else returns INT.
- */
- public Wrapper rawPrimitive() {
- switch (basicTypeChar) {
- case 'S': case 'B':
- case 'C': case 'Z':
- case 'V':
- case 'F':
- return INT;
- case 'D':
- return LONG;
- }
- return this;
- }
-
private static Number numberValue(Object x) {
if (x instanceof Number) return (Number)x;
if (x instanceof Character) return (int)(Character)x;
@@ -620,7 +570,10 @@
return (Number)x;
}
- private static boolean boolValue(long bits) {
+ // Parameter type of boolValue must be byte, because
+ // MethodHandles.explicitCastArguments defines boolean
+ // conversion as first converting to byte.
+ private static boolean boolValue(byte bits) {
bits &= 1; // simple 31-bit zero extension
return (bits != 0);
}
--- a/jdk/src/share/classes/sun/misc/Unsafe.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/misc/Unsafe.java Mon Aug 27 10:58:40 2012 -0700
@@ -678,6 +678,14 @@
public native Object staticFieldBase(Field f);
/**
+ * Detect if the given class may need to be initialized. This is often
+ * needed in conjunction with obtaining the static field base of a
+ * class.
+ * @return false only if a call to {@code ensureClassInitialized} would have no effect
+ */
+ public native boolean shouldBeInitialized(Class<?> c);
+
+ /**
* Ensure the given class has been initialized. This is often
* needed in conjunction with obtaining the static field base of a
* class.
--- a/jdk/src/share/classes/sun/util/resources/es/CurrencyNames_es_VE.properties Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/src/share/classes/sun/util/resources/es/CurrencyNames_es_VE.properties Mon Aug 27 10:58:40 2012 -0700
@@ -36,5 +36,5 @@
# Taligent is a registered trademark of Taligent, Inc.
VEB=Bs
-VEF=BsF.
+VEF=Bs.F.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7157574/Test7157574.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+/*
+7157574 method handles returned by reflective lookup API sometimes have wrong receiver type
+
+When an inherited non-static field or method is looked up in a class C using Lookup.findVirtual(C...), etc., the JSR 292 API, the first argument of the resulting method handle must be the receiver ('this'), and must be the requested class (or more specific, in the case of findSpecial or a lookup of a protected method).
+
+But currently, if a supertype T defines the looked-up method or field and C inherits it, the returned method handle might have the more specific initial type T.
+
+The relevant javadoc (and 292 spec.) is as follows:
+ * The formal parameter {@code this} stands for the self-reference of type {@code C};
+ * if it is present, it is always the leading argument to the method handle invocation.
+ * (In the case of some {@code protected} members, {@code this} may be
+ * restricted in type to the lookup class; see below.)
+
+Because of this bug, all of the assertions fail in the following example:
+*/
+
+/* @test
+ * @bug 7157574
+ * @summary method handles returned by reflective lookup API sometimes have wrong receiver type
+ *
+ * @run main Test7157574
+ */
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+public class Test7157574 {
+ interface Intf { void ig1(); void ig2(); void ig3(); void ig4(); void m1(); }
+ static abstract class Super implements Intf { public abstract void m2(); public int f2; }
+ static abstract class Sub extends Super { }
+ public static void main(String... av) throws Throwable {
+ MethodHandle m1 = lookup().findVirtual(Sub.class, "m1", methodType(void.class));
+ System.out.println(m1);
+ MethodHandle m2 = lookup().findVirtual(Sub.class, "m2", methodType(void.class));
+ System.out.println(m2);
+ MethodHandle f2 = lookup().findGetter(Sub.class, "f2", int.class);
+ System.out.println(f2);
+ MethodHandle f2s = lookup().findSetter(Sub.class, "f2", int.class);
+ System.out.println(f2s);
+ MethodHandle chc = lookup().findVirtual(Sub.class, "hashCode", methodType(int.class));
+ System.out.println(chc);
+ MethodHandle ihc = lookup().findVirtual(Intf.class, "hashCode", methodType(int.class));
+ System.out.println(ihc);
+ assertEquals(Sub.class, m1.type().parameterType(0));
+ assertEquals(Sub.class, m2.type().parameterType(0));
+ assertEquals(Sub.class, f2.type().parameterType(0));
+ assertEquals(Sub.class, f2s.type().parameterType(0));
+ assertEquals(Sub.class, chc.type().parameterType(0));
+ assertEquals(Intf.class, ihc.type().parameterType(0));
+ // test the MHs on a concrete version of Sub
+ class C extends Sub {
+ public void m1() { this.f2 = -1; }
+ public void m2() { this.f2 = -2; }
+ // Pack the vtable of Intf with leading junk:
+ private void ig() { throw new RuntimeException(); }
+ public void ig1() { ig(); }
+ public void ig2() { ig(); }
+ public void ig3() { ig(); }
+ public void ig4() { ig(); }
+ }
+ testConcrete(new C(), m1, m2, f2, f2s, chc, ihc);
+ }
+ private static void testConcrete(Sub s,
+ MethodHandle m1, MethodHandle m2,
+ MethodHandle f2, MethodHandle f2s,
+ MethodHandle chc, MethodHandle ihc
+ ) throws Throwable {
+ s.f2 = 0;
+ m1.invokeExact(s);
+ assertEquals(-1, s.f2);
+ m2.invokeExact(s);
+ assertEquals(-2, s.f2);
+ s.f2 = 2;
+ assertEquals(2, (int) f2.invokeExact(s));
+ f2s.invokeExact(s, 0);
+ assertEquals(0, s.f2);
+ assertEquals(s.hashCode(), (int) chc.invokeExact(s));
+ assertEquals(s.hashCode(), (int) ihc.invokeExact((Intf)s));
+ }
+
+ private static void assertEquals(Object expect, Object observe) {
+ if (java.util.Objects.equals(expect, observe)) return;
+ String msg = ("expected "+expect+" but observed "+observe);
+ System.out.println("FAILED: "+msg);
+ throw new AssertionError(msg);
+ }
+}
--- a/jdk/test/java/lang/invoke/InvokeGenericTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -68,24 +68,6 @@
public InvokeGenericTest() {
}
- @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("x86_64") || 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;
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -54,7 +54,6 @@
/**
* @author jrose
*/
-@SuppressWarnings("LocalVariableHidesMemberVariable")
public class JavaDocExamplesTest {
/** Wrapper for running the JUnit tests in this module.
* Put JUnit on the classpath!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/MaxTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012, 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 BoundMethodHandle tests with primitive types
+ * @compile MaxTest.java
+ * @run junit/othervm test.java.lang.invoke.MaxTest
+ */
+
+package test.java.lang.invoke;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import org.junit.Test;
+
+public class MaxTest {
+
+ static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ private MethodHandle getMax(Class<?> t) throws Throwable {
+ return LOOKUP.findStatic(Math.class, "max", MethodType.methodType(t, t, t));
+ }
+
+ static int ITERATION_COUNT = 40000;
+ static {
+ String iterations = System.getProperty(MaxTest.class.getSimpleName() + ".ITERATION_COUNT");
+ if (iterations == null) {
+ iterations = System.getProperty(MaxTest.class.getName() + ".ITERATION_COUNT");
+ }
+ if (iterations != null) {
+ ITERATION_COUNT = Integer.parseInt(iterations);
+ }
+ }
+
+ @Test
+ public void testMaxLong() throws Throwable {
+ final Class<?> C = long.class;
+ final long P = 23L;
+ final long Q = 42L;
+ final long R = Math.max(P, Q);
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((long) h.invokeExact(P, Q), R);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((long) bh.invokeExact(Q), R);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((long) bbh.invokeExact(), R);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((long) b2h.invokeExact(P), R);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((long) bb2h.invokeExact(), R);
+ }
+ }
+
+ @Test
+ public void testMaxInt() throws Throwable {
+ final Class<?> C = int.class;
+ final int P = 23;
+ final int Q = 42;
+ final int R = Math.max(P, Q);
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((int) h.invokeExact(P, Q), R);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((int) bh.invokeExact(Q), R);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((int) bbh.invokeExact(), R);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((int) b2h.invokeExact(P), R);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((int) bb2h.invokeExact(), R);
+ }
+ }
+
+ @Test
+ public void testMaxFloat() throws Throwable {
+ final Class<?> C = float.class;
+ final float P = 23F;
+ final float Q = 42F;
+ final float R = Math.max(P, Q);
+ final float D = 0.1F;
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((float) h.invokeExact(P, Q), R, D);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((float) bh.invokeExact(Q), R, D);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((float) bbh.invokeExact(), R, D);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((float) b2h.invokeExact(P), R, D);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((float) bb2h.invokeExact(), R, D);
+ }
+ }
+
+ @Test
+ public void testMaxDouble() throws Throwable {
+ final Class<?> C = double.class;
+ final double P = 23F;
+ final double Q = 42F;
+ final double R = Math.max(P, Q);
+ final double D = 0.1;
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((double) h.invokeExact(P, Q), R, D);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((double) bh.invokeExact(Q), R, D);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((double) bbh.invokeExact(), R, D);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((double) b2h.invokeExact(P), R, D);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((double) bb2h.invokeExact(), R, D);
+ }
+ }
+
+}
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,12 +25,13 @@
/* @test
* @summary unit tests for java.lang.invoke.MethodHandles
- * @compile -source 7 -target 7 MethodHandlesTest.java
+ * @compile MethodHandlesTest.java remote/RemoteExample.java
* @run junit/othervm test.java.lang.invoke.MethodHandlesTest
*/
package test.java.lang.invoke;
+import test.java.lang.invoke.remote.RemoteExample;
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.*;
@@ -57,8 +58,13 @@
// Set this true during development if you want to fast-forward to
// a particular new, non-working test. Tests which are known to
// work (or have recently worked) test this flag and return on true.
- static boolean CAN_SKIP_WORKING = false;
- //static { CAN_SKIP_WORKING = true; }
+ static final boolean CAN_SKIP_WORKING;
+ static {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".CAN_SKIP_WORKING");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".CAN_SKIP_WORKING");
+ CAN_SKIP_WORKING = Boolean.parseBoolean(vstr);
+ }
// Set 'true' to do about 15x fewer tests, especially those redundant with RicochetTest.
// This might be useful with -Xcomp stress tests that compile all method handles.
@@ -71,62 +77,6 @@
} finally { printCounts(); verbosity -= 9; }
}
- // current failures
- @Test //@Ignore("failure in call to makeRawRetypeOnly in ToGeneric")
- public void testFail_1() throws Throwable {
- // AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters
- testSpreadArguments(int.class, 0, 6);
- }
- @Test //@Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args")
- public void testFail_2() throws Throwable {
- // if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes:
- testSpreadArguments(Object.class, 0, 2);
- }
- @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
- public void testFail_3() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testSpreadArguments(int.class, 1, 2);
- }
- @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
- public void testFail_4() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testCollectArguments(int.class, 1, 2);
- }
- @Test //@Ignore("cannot collect leading primitive types")
- public void testFail_5() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class));
- }
- @Test //@Ignore("should not insert arguments beyond MethodHandlePushLimit")
- public void testFail_6() throws Throwable {
- // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
- testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
- }
- @Test //@Ignore("permuteArguments has trouble with double slots")
- public void testFail_7() throws Throwable {
- testPermuteArguments(new Object[]{10, 200L},
- new Class<?>[]{Integer.class, long.class},
- new int[]{1,0});
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,0,1}); //rot
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{1,2,0}); //rot
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,1,0}); //swap
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{0,1,2,2}); //dup
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,0,1,2});
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,2,0,1});
- //testPermuteArguments(4, Integer.class, 2, long.class, 6);
- }
static final int MAX_ARG_INCREASE = 3;
public MethodHandlesTest() {
@@ -180,7 +130,7 @@
static Object logEntry(String name, Object... args) {
return Arrays.asList(name, Arrays.asList(args));
}
- static Object called(String name, Object... args) {
+ public static Object called(String name, Object... args) {
Object entry = logEntry(name, args);
calledLog.add(entry);
return entry;
@@ -280,6 +230,8 @@
{ param = c; break; }
}
}
+ if (param.isInterface() && param.isAssignableFrom(List.class))
+ return Arrays.asList("#"+nextArg());
if (param.isInterface() || param.isAssignableFrom(String.class))
return "#"+nextArg();
else
@@ -399,6 +351,8 @@
static final Lookup PRIVATE = MethodHandles.lookup();
// This lookup is good for package-private members but not private ones.
static final Lookup PACKAGE = PackageSibling.lookup();
+ // This lookup is good for public members and protected members of PubExample
+ static final Lookup SUBCLASS = RemoteExample.lookup();
// This lookup is good only for public members.
static final Lookup PUBLIC = MethodHandles.publicLookup();
@@ -412,9 +366,11 @@
@Override public String toString() { return name; }
public void v0() { called("v0", this); }
+ protected void pro_v0() { called("pro_v0", this); }
void pkg_v0() { called("pkg_v0", this); }
private void pri_v0() { called("pri_v0", this); }
public static void s0() { called("s0"); }
+ protected static void pro_s0() { called("pro_s0"); }
static void pkg_s0() { called("pkg_s0"); }
private static void pri_s0() { called("pri_s0"); }
@@ -434,12 +390,21 @@
// for testing findConstructor:
public Example(String x, int y) { this.name = x+y; called("Example.<init>", x, y); }
public Example(int x, String y) { this.name = x+y; called("Example.<init>", x, y); }
+ public Example(int x, int y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, long y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, float y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, double y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, int y, int z) { this.name = x+""+y+""+z; called("Example.<init>", x, y, z); }
+ public Example(int x, int y, int z, int a) { this.name = x+""+y+""+z+""+a; called("Example.<init>", x, y, z, a); }
static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial
}
static final Lookup EXAMPLE = Example.EXAMPLE;
public static class PubExample extends Example {
- public PubExample() { super("PubExample#"+nextArg()); }
+ public PubExample() { this("PubExample"); }
+ protected PubExample(String prefix) { super(prefix+"#"+nextArg()); }
+ protected void pro_v0() { called("Pub/pro_v0", this); }
+ protected static void pro_s0() { called("Pub/pro_s0"); }
}
static class SubExample extends Example {
@Override public void v0() { called("Sub/v0", this); }
@@ -457,12 +422,14 @@
@Override public String toString() { return name; }
}
}
+ static interface SubIntExample extends IntExample { }
static final Object[][][] ACCESS_CASES = {
- { { false, PUBLIC }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
- { { false, PUBLIC }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
- { { false, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
- { { true, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: all true
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
+ { { false, PUBLIC }, { false, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
+ { { false, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: subclass OK
+ { { true, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[4]: all true
};
static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) {
@@ -471,11 +438,13 @@
cases = ACCESS_CASES[1]; // PRIVATE only
} else if (name.contains("pkg_") || !Modifier.isPublic(defc.getModifiers())) {
cases = ACCESS_CASES[2]; // not PUBLIC
+ } else if (name.contains("pro_")) {
+ cases = ACCESS_CASES[3]; // PUBLIC class, protected member
} else {
- assertTrue(name.indexOf('_') < 0);
+ assertTrue(name.indexOf('_') < 0 || name.contains("fin_"));
boolean pubc = Modifier.isPublic(defc.getModifiers());
if (pubc)
- cases = ACCESS_CASES[3]; // all access levels
+ cases = ACCESS_CASES[4]; // all access levels
else
cases = ACCESS_CASES[2]; // PACKAGE but not PUBLIC
}
@@ -487,6 +456,13 @@
return accessCases(defc, name, false);
}
+ static Lookup maybeMoveIn(Lookup lookup, Class<?> defc) {
+ if (lookup == PUBLIC || lookup == SUBCLASS || lookup == PACKAGE)
+ // external views stay external
+ return lookup;
+ return lookup.in(defc);
+ }
+
@Test
public void testFindStatic() throws Throwable {
if (CAN_SKIP_WORKING) return;
@@ -495,6 +471,8 @@
testFindStatic(Example.class, void.class, "s0");
testFindStatic(Example.class, void.class, "pkg_s0");
testFindStatic(Example.class, void.class, "pri_s0");
+ testFindStatic(Example.class, void.class, "pro_s0");
+ testFindStatic(PubExample.class, void.class, "Pub/pro_s0");
testFindStatic(Example.class, Object.class, "s1", Object.class);
testFindStatic(Example.class, Object.class, "s2", int.class);
@@ -505,6 +483,7 @@
testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
+ testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
}
void testFindStatic(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -517,14 +496,16 @@
}
void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).findStatic(defc, name, type);
+ target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -537,7 +518,7 @@
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type, target.type());
- assertNameStringContains(target, name);
+ assertNameStringContains(target, methodName);
Object[] args = randomArgs(params);
printCalled(target, name, args);
target.invokeWithArguments(args);
@@ -571,7 +552,12 @@
testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class);
testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class);
testFindVirtual(Example.class, Object.class, "v2", int.class, int.class);
+ testFindVirtual(Example.class, void.class, "pro_v0");
+ testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
+
testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
+ testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
+
// test dispatch
testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0");
testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0");
@@ -602,9 +588,10 @@
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).findVirtual(defc, methodName, type);
+ target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -616,12 +603,20 @@
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
- Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params);
+ Class<?> selfc = defc;
+ // predict receiver type narrowing:
+ if (lookup == SUBCLASS &&
+ name.contains("pro_") &&
+ selfc.isAssignableFrom(lookup.lookupClass())) {
+ selfc = lookup.lookupClass();
+ if (name.startsWith("Pub/")) name = "Rem/"+name.substring(4);
+ }
+ Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)selfc), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertEquals(typeWithSelf, target.type());
assertNameStringContains(target, methodName);
Object[] argsWithSelf = randomArgs(paramsWithSelf);
- if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc);
+ if (selfc.isAssignableFrom(rcvc) && rcvc != selfc) argsWithSelf[0] = randomArg(rcvc);
printCalled(target, name, argsWithSelf);
target.invokeWithArguments(argsWithSelf);
assertCalled(name, argsWithSelf);
@@ -635,6 +630,7 @@
startTest("findSpecial");
testFindSpecial(SubExample.class, Example.class, void.class, "v0");
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
+ testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
// Do some negative testing:
testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
@@ -647,23 +643,34 @@
void testFindSpecial(Class<?> specialCaller,
Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
- testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params);
- testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params);
- testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
- testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
+ if (specialCaller == RemoteExample.class) {
+ testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+ testFindSpecial(true, SUBCLASS, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
+ return;
+ }
+ testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params);
+ testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
}
void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller,
Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller));
- target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
+ if (verbosity >= 5) System.out.println(" lookup => "+maybeMoveIn(lookup, specialCaller));
+ target = maybeMoveIn(lookup, specialCaller).findSpecial(defc, methodName, type, specialCaller);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -680,7 +687,7 @@
assertEquals(type, target.type().dropParameterTypes(0,1));
Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
- assertNameStringContains(target, name);
+ assertNameStringContains(target, methodName);
Object[] args = randomArgs(paramsWithSelf);
printCalled(target, name, args);
target.invokeWithArguments(args);
@@ -693,7 +700,13 @@
startTest("findConstructor");
testFindConstructor(true, EXAMPLE, Example.class);
testFindConstructor(true, EXAMPLE, Example.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, long.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, float.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, double.class);
testFindConstructor(true, EXAMPLE, Example.class, String.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class, int.class);
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
@@ -757,9 +770,10 @@
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).bind(receiver, methodName, type);
+ target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -786,6 +800,7 @@
if (CAN_SKIP_WORKING) return;
startTest("unreflect");
testUnreflect(Example.class, true, void.class, "s0");
+ testUnreflect(Example.class, true, void.class, "pro_s0");
testUnreflect(Example.class, true, void.class, "pkg_s0");
testUnreflect(Example.class, true, void.class, "pri_s0");
@@ -804,6 +819,9 @@
testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class);
testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class);
testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class);
+
+ // Test a public final member in another package:
+ testUnreflect(RemoteExample.class, false, void.class, "Rem/fin_v0");
}
void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -820,8 +838,9 @@
boolean positive, Lookup lookup,
Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
- Method rmethod = defc.getDeclaredMethod(name, params);
+ Method rmethod = defc.getDeclaredMethod(methodName, params);
MethodHandle target = null;
Exception noAccess = null;
boolean isStatic = (rcvc == null);
@@ -829,11 +848,12 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (isSpecial)
- target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
+ target = maybeMoveIn(lookup, specialCaller).unreflectSpecial(rmethod, specialCaller);
else
- target = lookup.in(defc).unreflect(rmethod);
+ target = maybeMoveIn(lookup, defc).unreflect(rmethod);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -960,7 +980,7 @@
}
}
- static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10;
+ static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10, TEST_BOUND = 0x20, TEST_NPE = 0x40;
static boolean testModeMatches(int testMode, boolean isStatic) {
switch (testMode) {
case TEST_FIND_STATIC: return isStatic;
@@ -972,16 +992,20 @@
@Test
public void testUnreflectGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("unreflectGetter");
testGetter(TEST_UNREFLECT);
}
@Test
public void testFindGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findGetter");
testGetter(TEST_FIND_FIELD);
+ testGetter(TEST_FIND_FIELD | TEST_BOUND);
}
@Test
public void testFindStaticGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findStaticGetter");
testGetter(TEST_FIND_STATIC);
}
@@ -990,6 +1014,8 @@
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testGetter(positive, lookup, c[0], c[1], testMode);
+ if (positive)
+ testGetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
testGetter(true, lookup,
new Object[]{ true, System.class, "out", java.io.PrintStream.class },
@@ -1005,12 +1031,15 @@
testAccessor(positive, lookup, fieldRef, value, testMode);
}
- public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
+ public void testAccessor(boolean positive0, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode0) throws Throwable {
if (verbosity >= 4)
- System.out.println("testAccessor"+Arrays.asList(positive, lookup, fieldRef, value, testMode0));
+ System.out.println("testAccessor"+Arrays.deepToString(new Object[]{positive0, lookup, fieldRef, value, testMode0}));
boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
- int testMode = testMode0 & ~TEST_SETTER;
+ boolean doBound = ((testMode0 & TEST_BOUND) != 0);
+ boolean testNPE = ((testMode0 & TEST_NPE) != 0);
+ int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
+ boolean positive = positive0 && !testNPE;
boolean isStatic;
Class<?> fclass;
String fname;
@@ -1035,6 +1064,7 @@
}
if (!testModeMatches(testMode, isStatic)) return;
if (f == null && testMode == TEST_UNREFLECT) return;
+ if (testNPE && isStatic) return;
countTest(positive);
MethodType expType;
if (isGetter)
@@ -1045,7 +1075,7 @@
Exception noAccess = null;
MethodHandle mh;
try {
- switch (testMode0) {
+ switch (testMode0 & ~(TEST_BOUND | TEST_NPE)) {
case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break;
case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break;
case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break;
@@ -1061,6 +1091,7 @@
} catch (ReflectiveOperationException ex) {
mh = null;
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (fname.contains("bogus"))
assertTrue(noAccess instanceof NoSuchFieldException);
else
@@ -1070,15 +1101,19 @@
System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+" => "+mh
+(noAccess == null ? "" : " !! "+noAccess));
- if (positive && noAccess != null) throw new RuntimeException(noAccess);
- assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null);
- if (!positive) return; // negative test failed as expected
+ if (positive && !testNPE && noAccess != null) throw new RuntimeException(noAccess);
+ assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
+ if (!positive && !testNPE) return; // negative access test failed as expected
assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
assertSame(mh.type(), expType);
- assertNameStringContains(mh, fname);
+ //assertNameStringContains(mh, fname); // This does not hold anymore with LFs
HasFields fields = new HasFields();
+ HasFields fieldsForMH = fields;
+ if (testNPE) fieldsForMH = null; // perturb MH argument to elicit expected error
+ if (doBound)
+ mh = mh.bindTo(fieldsForMH);
Object sawValue;
Class<?> vtype = ftype;
if (ftype != int.class) vtype = Object.class;
@@ -1094,19 +1129,28 @@
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), value); // clean to start with
}
+ Throwable caughtEx = null;
if (isGetter) {
Object expValue = value;
for (int i = 0; i <= 1; i++) {
- if (isStatic) {
- if (ftype == int.class)
- sawValue = (int) mh.invokeExact(); // do these exactly
- else
- sawValue = mh.invokeExact();
- } else {
- if (ftype == int.class)
- sawValue = (int) mh.invokeExact((Object) fields);
- else
- sawValue = mh.invokeExact((Object) fields);
+ sawValue = null; // make DA rules happy under try/catch
+ try {
+ if (isStatic || doBound) {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact(); // do these exactly
+ else
+ sawValue = mh.invokeExact();
+ } else {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact((Object) fieldsForMH);
+ else
+ sawValue = mh.invokeExact((Object) fieldsForMH);
+ }
+ } catch (RuntimeException ex) {
+ if (ex instanceof NullPointerException && testNPE) {
+ caughtEx = ex;
+ break;
+ }
}
assertEquals(sawValue, expValue);
if (f != null && f.getDeclaringClass() == HasFields.class
@@ -1121,16 +1165,23 @@
} else {
for (int i = 0; i <= 1; i++) {
Object putValue = randomArg(ftype);
- if (isStatic) {
- if (ftype == int.class)
- mh.invokeExact((int)putValue); // do these exactly
- else
- mh.invokeExact(putValue);
- } else {
- if (ftype == int.class)
- mh.invokeExact((Object) fields, (int)putValue);
- else
- mh.invokeExact((Object) fields, putValue);
+ try {
+ if (isStatic || doBound) {
+ if (ftype == int.class)
+ mh.invokeExact((int)putValue); // do these exactly
+ else
+ mh.invokeExact(putValue);
+ } else {
+ if (ftype == int.class)
+ mh.invokeExact((Object) fieldsForMH, (int)putValue);
+ else
+ mh.invokeExact((Object) fieldsForMH, putValue);
+ }
+ } catch (RuntimeException ex) {
+ if (ex instanceof NullPointerException && testNPE) {
+ caughtEx = ex;
+ break;
+ }
}
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), putValue);
@@ -1140,21 +1191,33 @@
if (f != null && f.getDeclaringClass() == HasFields.class) {
f.set(fields, value); // put it back
}
+ if (testNPE) {
+ if (caughtEx == null || !(caughtEx instanceof NullPointerException))
+ throw new RuntimeException("failed to catch NPE exception"+(caughtEx == null ? " (caughtEx=null)" : ""), caughtEx);
+ caughtEx = null; // nullify expected exception
+ }
+ if (caughtEx != null) {
+ throw new RuntimeException("unexpected exception", caughtEx);
+ }
}
@Test
public void testUnreflectSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("unreflectSetter");
testSetter(TEST_UNREFLECT);
}
@Test
public void testFindSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findSetter");
testSetter(TEST_FIND_FIELD);
+ testSetter(TEST_FIND_FIELD | TEST_BOUND);
}
@Test
public void testFindStaticSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findStaticSetter");
testSetter(TEST_FIND_STATIC);
}
@@ -1164,6 +1227,8 @@
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testSetter(positive, lookup, c[0], c[1], testMode);
+ if (positive)
+ testSetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testSetter(false, lookup,
@@ -1178,34 +1243,84 @@
@Test
public void testArrayElementGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("arrayElementGetter");
testArrayElementGetterSetter(false);
}
@Test
public void testArrayElementSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("arrayElementSetter");
testArrayElementGetterSetter(true);
}
+ private static final int TEST_ARRAY_NONE = 0, TEST_ARRAY_NPE = 1, TEST_ARRAY_OOB = 2, TEST_ARRAY_ASE = 3;
+
public void testArrayElementGetterSetter(boolean testSetter) throws Throwable {
- testArrayElementGetterSetter(new Object[10], testSetter);
- testArrayElementGetterSetter(new String[10], testSetter);
- testArrayElementGetterSetter(new boolean[10], testSetter);
- testArrayElementGetterSetter(new byte[10], testSetter);
- testArrayElementGetterSetter(new char[10], testSetter);
- testArrayElementGetterSetter(new short[10], testSetter);
- testArrayElementGetterSetter(new int[10], testSetter);
- testArrayElementGetterSetter(new float[10], testSetter);
- testArrayElementGetterSetter(new long[10], testSetter);
- testArrayElementGetterSetter(new double[10], testSetter);
+ testArrayElementGetterSetter(testSetter, TEST_ARRAY_NONE);
+ }
+
+ @Test
+ public void testArrayElementErrors() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ startTest("arrayElementErrors");
+ testArrayElementGetterSetter(false, TEST_ARRAY_NPE);
+ testArrayElementGetterSetter(true, TEST_ARRAY_NPE);
+ testArrayElementGetterSetter(false, TEST_ARRAY_OOB);
+ testArrayElementGetterSetter(true, TEST_ARRAY_OOB);
+ testArrayElementGetterSetter(new Object[10], true, TEST_ARRAY_ASE);
+ testArrayElementGetterSetter(new Example[10], true, TEST_ARRAY_ASE);
+ testArrayElementGetterSetter(new IntExample[10], true, TEST_ARRAY_ASE);
}
- public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable {
- countTest(true);
- if (verbosity > 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]");
+ public void testArrayElementGetterSetter(boolean testSetter, int negTest) throws Throwable {
+ testArrayElementGetterSetter(new String[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Iterable<?>[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Example[10], testSetter, negTest);
+ testArrayElementGetterSetter(new IntExample[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Object[10], testSetter, negTest);
+ testArrayElementGetterSetter(new boolean[10], testSetter, negTest);
+ testArrayElementGetterSetter(new byte[10], testSetter, negTest);
+ testArrayElementGetterSetter(new char[10], testSetter, negTest);
+ testArrayElementGetterSetter(new short[10], testSetter, negTest);
+ testArrayElementGetterSetter(new int[10], testSetter, negTest);
+ testArrayElementGetterSetter(new float[10], testSetter, negTest);
+ testArrayElementGetterSetter(new long[10], testSetter, negTest);
+ testArrayElementGetterSetter(new double[10], testSetter, negTest);
+ }
+
+ public void testArrayElementGetterSetter(Object array, boolean testSetter, int negTest) throws Throwable {
+ boolean positive = (negTest == TEST_ARRAY_NONE);
+ int length = java.lang.reflect.Array.getLength(array);
Class<?> arrayType = array.getClass();
Class<?> elemType = arrayType.getComponentType();
+ Object arrayToMH = array;
+ // this stanza allows negative tests to make argument perturbations:
+ switch (negTest) {
+ case TEST_ARRAY_NPE:
+ arrayToMH = null;
+ break;
+ case TEST_ARRAY_OOB:
+ assert(length > 0);
+ arrayToMH = java.lang.reflect.Array.newInstance(elemType, 0);
+ break;
+ case TEST_ARRAY_ASE:
+ assert(testSetter && !elemType.isPrimitive());
+ if (elemType == Object.class)
+ arrayToMH = new StringBuffer[length]; // very random subclass of Object!
+ else if (elemType == Example.class)
+ arrayToMH = new SubExample[length];
+ else if (elemType == IntExample.class)
+ arrayToMH = new SubIntExample[length];
+ else
+ return; // can't make an ArrayStoreException test
+ assert(arrayType.isInstance(arrayToMH))
+ : Arrays.asList(arrayType, arrayToMH.getClass(), testSetter, negTest);
+ break;
+ }
+ countTest(positive);
+ if (verbosity > 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+length+"]"+(positive ? "" : " negative test #"+negTest+" using "+Arrays.deepToString(new Object[]{arrayToMH})));
MethodType expType = !testSetter
? MethodType.methodType(elemType, arrayType, int.class)
: MethodType.methodType(void.class, arrayType, int.class, elemType);
@@ -1214,25 +1329,29 @@
: MethodHandles.arrayElementSetter(arrayType);
assertSame(mh.type(), expType);
if (elemType != int.class && elemType != boolean.class) {
- // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
- MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
+ MethodType gtype = mh.type().generic().changeParameterType(1, int.class);
if (testSetter) gtype = gtype.changeReturnType(void.class);
mh = mh.asType(gtype);
}
Object sawValue, expValue;
List<Object> model = array2list(array);
- int length = Array.getLength(array);
+ Throwable caughtEx = null;
for (int i = 0; i < length; i++) {
// update array element
Object random = randomArg(elemType);
model.set(i, random);
if (testSetter) {
- if (elemType == int.class)
- mh.invokeExact((int[]) array, i, (int)random);
- else if (elemType == boolean.class)
- mh.invokeExact((boolean[]) array, i, (boolean)random);
- else
- mh.invokeExact(array, (Integer)i, random);
+ try {
+ if (elemType == int.class)
+ mh.invokeExact((int[]) arrayToMH, i, (int)random);
+ else if (elemType == boolean.class)
+ mh.invokeExact((boolean[]) arrayToMH, i, (boolean)random);
+ else
+ mh.invokeExact(arrayToMH, i, random);
+ } catch (RuntimeException ex) {
+ caughtEx = ex;
+ break;
+ }
assertEquals(model, array2list(array));
} else {
Array.set(array, i, random);
@@ -1247,16 +1366,39 @@
sawValue = Array.get(array, i);
if (!testSetter) {
expValue = sawValue;
- if (elemType == int.class)
- sawValue = (int) mh.invokeExact((int[]) array, i);
- else if (elemType == boolean.class)
- sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
- else
- sawValue = mh.invokeExact(array, (Integer)i);
+ try {
+ if (elemType == int.class)
+ sawValue = (int) mh.invokeExact((int[]) arrayToMH, i);
+ else if (elemType == boolean.class)
+ sawValue = (boolean) mh.invokeExact((boolean[]) arrayToMH, i);
+ else
+ sawValue = mh.invokeExact(arrayToMH, i);
+ } catch (RuntimeException ex) {
+ caughtEx = ex;
+ break;
+ }
assertEquals(sawValue, expValue);
assertEquals(model, array2list(array));
}
}
+ if (!positive) {
+ if (caughtEx == null)
+ throw new RuntimeException("failed to catch exception for negTest="+negTest);
+ // test the kind of exception
+ Class<?> reqType = null;
+ switch (negTest) {
+ case TEST_ARRAY_ASE: reqType = ArrayStoreException.class; break;
+ case TEST_ARRAY_OOB: reqType = ArrayIndexOutOfBoundsException.class; break;
+ case TEST_ARRAY_NPE: reqType = NullPointerException.class; break;
+ default: assert(false);
+ }
+ if (reqType.isInstance(caughtEx)) {
+ caughtEx = null; // nullify expected exception
+ }
+ }
+ if (caughtEx != null) {
+ throw new RuntimeException("unexpected exception", caughtEx);
+ }
}
List<Object> array2list(Object array) {
@@ -1363,6 +1505,8 @@
@Test
public void testVarargsCollector() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ startTest("varargsCollector");
MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
MethodType.methodType(Object.class, String.class, Object[].class));
vac0 = vac0.bindTo("vac");
@@ -1375,7 +1519,7 @@
}
}
- @Test
+ @Test // SLOW
public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("permuteArguments");
@@ -1514,7 +1658,7 @@
}
- @Test
+ @Test // SLOW
public void testSpreadArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("spreadArguments");
@@ -1522,7 +1666,7 @@
if (verbosity >= 3)
System.out.println("spreadArguments "+argType);
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int pos = 0; pos <= nargs; pos++) {
if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
@@ -1608,7 +1752,7 @@
}
}
- @Test
+ @Test // SLOW
public void testCollectArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("collectArguments");
@@ -1616,7 +1760,7 @@
if (verbosity >= 3)
System.out.println("collectArguments "+argType);
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int pos = 0; pos <= nargs; pos++) {
if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
@@ -1650,12 +1794,12 @@
assertArrayEquals(collectedArgs, returnValue);
}
- @Test
+ @Test // SLOW
public void testInsertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("insertArguments");
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int ins = 0; ins <= nargs; ins++) {
if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
continue;
@@ -1677,7 +1821,7 @@
List<Object> argsToPass = new ArrayList<>(resList);
List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
if (verbosity >= 3)
- System.out.println("insert: "+argsToInsert+" into "+target);
+ System.out.println("insert: "+argsToInsert+" @"+pos+" into "+target);
@SuppressWarnings("cast") // cast to spread Object... is helpful
MethodHandle target2 = MethodHandles.insertArguments(target, pos,
(Object[]/*...*/) argsToInsert.toArray());
@@ -1840,7 +1984,7 @@
assertEquals(resList, res2List);
}
- @Test
+ @Test // SLOW
public void testInvokers() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker");
@@ -2109,7 +2253,7 @@
if (CAN_SKIP_WORKING) return;
startTest("catchException");
for (int nargs = 0; nargs < 40; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int throwMode = 0; throwMode < THROW_MODE_LIMIT; throwMode++) {
testCatchException(int.class, new ClassCastException("testing"), throwMode, nargs);
if (CAN_TEST_LIGHTLY && nargs > 3) continue;
@@ -2237,8 +2381,10 @@
assertSame(thrown, caught);
}
- @Test
+ //@Test
public void testInterfaceCast() throws Throwable {
+ //if (CAN_SKIP_WORKING) return;
+ startTest("interfaceCast");
for (Class<?> ctype : new Class<?>[]{ Object.class, String.class, CharSequence.class, Number.class, Iterable.class}) {
testInterfaceCast(ctype, false, false);
testInterfaceCast(ctype, true, false);
@@ -2279,11 +2425,12 @@
}
}
- @Test
+ @Test // SLOW
public void testCastFailure() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testCastFailure");
testCastFailure("cast/argument", 11000);
+ if (CAN_TEST_LIGHTLY) return;
testCastFailure("unbox/argument", 11000);
testCastFailure("cast/return", 11000);
testCastFailure("unbox/return", 11000);
@@ -2380,7 +2527,7 @@
if (verbosity > 2)
System.out.println("caught "+ex);
if (verbosity > 3)
- ex.printStackTrace();
+ ex.printStackTrace(System.out);
assertTrue(true); // all is well
}
}
@@ -2444,7 +2591,7 @@
@Test
public void testAsInterfaceInstance() throws Throwable {
if (CAN_SKIP_WORKING) return;
- startTest("testAsInterfaceInstance");
+ startTest("asInterfaceInstance");
Lookup lookup = MethodHandles.lookup();
// test typical case: Runnable.run
{
@@ -2550,7 +2697,7 @@
} else {
assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
if (!(ex1 instanceof UndeclaredThrowableException) || ex1.getCause() != ex) {
- ex1.printStackTrace();
+ ex1.printStackTrace(System.out);
}
assertSame(ex, ex1.getCause());
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
@@ -2630,6 +2777,8 @@
}
}
// Local abbreviated copy of sun.invoke.util.ValueConversions
+// This guy tests access from outside the same package member, but inside
+// the package itself.
class ValueConversions {
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
private static final Object[] NO_ARGS_ARRAY = {};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/PrivateInvokeTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,376 @@
+/*
+ * 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. 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 white-box testing of method handle sub-primitives
+ * @run junit test.java.lang.invoke.PrivateInvokeTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.junit.*;
+import static org.junit.Assert.*;
+
+public class PrivateInvokeTest {
+ // Utility functions
+ private static final Lookup LOOKUP = lookup();
+ private static final Class<?> THIS_CLASS = PrivateInvokeTest.class;
+ private static final int
+ REF_NONE = 0, // null value
+ REF_getField = 1,
+ REF_getStatic = 2,
+ REF_putField = 3,
+ REF_putStatic = 4,
+ REF_invokeVirtual = 5,
+ REF_invokeStatic = 6,
+ REF_invokeSpecial = 7,
+ REF_newInvokeSpecial = 8,
+ REF_invokeInterface = 9,
+ REF_LIMIT = 10,
+ REF_MH_invokeBasic = REF_NONE;;
+ private static final String[] REF_KIND_NAMES = {
+ "MH::invokeBasic",
+ "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
+ "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
+ "REF_newInvokeSpecial", "REF_invokeInterface"
+ };
+ private int verbose;
+ //{ verbose = 99; } // for debugging
+ {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".verbose");
+ if (vstr == null)
+ vstr = System.getProperty("test.verbose");
+ if (vstr != null) verbose = Integer.parseInt(vstr);
+ }
+ private static int referenceKind(Method m) {
+ if (Modifier.isStatic(m.getModifiers()))
+ return REF_invokeStatic;
+ else if (m.getDeclaringClass().isInterface())
+ return REF_invokeInterface;
+ else if (Modifier.isFinal(m.getModifiers()) ||
+ Modifier.isFinal(m.getDeclaringClass().getModifiers()))
+ return REF_invokeSpecial;
+ else
+ return REF_invokeVirtual;
+ }
+ private static MethodType basicType(MethodType mtype) {
+ MethodType btype = mtype.erase();
+ if (btype.hasPrimitives()) {
+ for (int i = -1; i < mtype.parameterCount(); i++) {
+ Class<?> type = (i < 0 ? mtype.returnType() : mtype.parameterType(i));
+ if (type == boolean.class ||
+ type == byte.class ||
+ type == char.class ||
+ type == short.class) {
+ type = int.class;
+ if (i < 0)
+ btype = btype.changeReturnType(type);
+ else
+ btype = btype.changeParameterType(i, type);
+ }
+ }
+ }
+ return btype;
+ }
+ private static Method getMethod(Class<?> defc, String name, Class<?>... ptypes) {
+ try {
+ return defc.getDeclaredMethod(name, ptypes);
+ } catch (NoSuchMethodException ex) {
+ }
+ try {
+ return defc.getMethod(name, ptypes);
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private static MethodHandle unreflect(Method m) {
+ try {
+ MethodHandle mh = LOOKUP.unreflect(m);
+ if (Modifier.isTransient(m.getModifiers()))
+ mh = mh.asFixedArity(); // remove varargs wrapper
+ return mh;
+ } catch (IllegalAccessException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private static final Lookup DIRECT_INVOKER_LOOKUP;
+ private static final Class<?> MEMBER_NAME_CLASS;
+ private static final MethodHandle MH_INTERNAL_MEMBER_NAME;
+ private static final MethodHandle MH_DEBUG_STRING;
+ static {
+ try {
+ // This is white box testing. Use reflection to grab private implementation bits.
+ String magicName = "IMPL_LOOKUP";
+ Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
+ // This unit test will fail if a security manager is installed.
+ magicLookup.setAccessible(true);
+ // Forbidden fruit...
+ DIRECT_INVOKER_LOOKUP = (Lookup) magicLookup.get(null);
+ MEMBER_NAME_CLASS = Class.forName("java.lang.invoke.MemberName", false, MethodHandle.class.getClassLoader());
+ MH_INTERNAL_MEMBER_NAME = DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, "internalMemberName", methodType(MEMBER_NAME_CLASS))
+ .asType(methodType(Object.class, MethodHandle.class));
+ MH_DEBUG_STRING = DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, "debugString", methodType(String.class));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private Object internalMemberName(MethodHandle mh) {
+ try {
+ return MH_INTERNAL_MEMBER_NAME.invokeExact(mh);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private String debugString(MethodHandle mh) {
+ try {
+ return (String) MH_DEBUG_STRING.invokeExact(mh);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private static MethodHandle directInvoker(int refKind, MethodType mtype) {
+ return directInvoker(REF_KIND_NAMES[refKind], mtype);
+ }
+ private static MethodHandle directInvoker(String name, MethodType mtype) {
+ boolean isStatic;
+ mtype = mtype.erase();
+ if (name.startsWith("MH::")) {
+ isStatic = false;
+ name = strip("MH::", name);
+ } else if (name.startsWith("REF_")) {
+ isStatic = true;
+ name = strip("REF_", name);
+ if (name.startsWith("invoke"))
+ name = "linkTo"+strip("invoke", name);
+ mtype = mtype.appendParameterTypes(MEMBER_NAME_CLASS);
+ } else {
+ throw new AssertionError("name="+name);
+ }
+ //System.out.println("directInvoker = "+name+mtype);
+ try {
+ if (isStatic)
+ return DIRECT_INVOKER_LOOKUP
+ .findStatic(MethodHandle.class, name, mtype);
+ else
+ return DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, name, mtype);
+ } catch (ReflectiveOperationException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private Object invokeWithArguments(Method m, Object... args) {
+ Object recv = null;
+ if (!Modifier.isStatic(m.getModifiers())) {
+ recv = args[0];
+ args = pop(1, args);
+ }
+ try {
+ return m.invoke(recv, args);
+ } catch (IllegalAccessException|IllegalArgumentException|InvocationTargetException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private Object invokeWithArguments(MethodHandle mh, Object... args) {
+ try {
+ return mh.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private int counter;
+ private Object makeArgument(Class<?> type) {
+ final String cname = type.getSimpleName();
+ final int n = ++counter;
+ final int nn = (n << 10) + 13;
+ if (type.isAssignableFrom(String.class)) {
+ return "<"+cname+"#"+nn+">";
+ }
+ if (type == THIS_CLASS) return this.withCounter(nn);
+ if (type == Integer.class || type == int.class) return nn;
+ if (type == Character.class || type == char.class) return (char)(n % 100+' ');
+ if (type == Byte.class || type == byte.class) return (byte)-(n % 100);
+ if (type == Long.class || type == long.class) return (long)nn;
+ throw new IllegalArgumentException("don't know how to make argument of type: "+type);
+ }
+ private Object[] makeArguments(Class<?>... ptypes) {
+ Object[] args = new Object[ptypes.length];
+ for (int i = 0; i < args.length; i++)
+ args[i] = makeArgument(ptypes[i]);
+ return args;
+ }
+ private Object[] makeArguments(MethodType mtype) {
+ return makeArguments(mtype.parameterArray());
+ }
+ private Object[] pop(int n, Object[] args) {
+ if (n >= 0)
+ return Arrays.copyOfRange(args, n, args.length);
+ else
+ return Arrays.copyOfRange(args, 0, args.length+n);
+ }
+ private Object[] pushAtFront(Object arg1, Object[] args) {
+ Object[] res = new Object[1+args.length];
+ res[0] = arg1;
+ System.arraycopy(args, 0, res, 1, args.length);
+ return res;
+ }
+ private Object[] pushAtBack(Object[] args, Object argN) {
+ Object[] res = new Object[1+args.length];
+ System.arraycopy(args, 0, res, 0, args.length);
+ res[args.length] = argN;
+ return res;
+ }
+ private static String strip(String prefix, String s) {
+ assert(s.startsWith(prefix));
+ return s.substring(prefix.length());
+ }
+
+ private final int[] refKindTestCounts = new int[REF_KIND_NAMES.length];
+ @After
+ public void printCounts() {
+ ArrayList<String> zeroes = new ArrayList<>();
+ for (int i = 0; i < refKindTestCounts.length; i++) {
+ final int count = refKindTestCounts[i];
+ final String name = REF_KIND_NAMES[i];
+ if (count == 0) {
+ if (name != null) zeroes.add(name);
+ continue;
+ }
+ if (verbose >= 0)
+ System.out.println("test count for "+name+" : "+count);
+ else if (name != null)
+ zeroes.add(name);
+ }
+ if (verbose >= 0)
+ System.out.println("test counts zero for "+zeroes);
+ }
+
+ // Test subjects
+ public static String makeString(Object x) { return "makeString("+x+")"; }
+ public static String dupString(String x) { return "("+x+"+"+x+")"; }
+ public static String intString(int x) { return "intString("+x+")"; }
+ public static String byteString(byte x) { return "byteString("+x+")"; }
+ public static String longString(String x, long y, String z) { return "longString("+x+y+z+")"; }
+
+ public final String toString() {
+ return "<"+getClass().getSimpleName()+"#"+counter+">";
+ }
+ public final String hello() { return "hello from "+this; }
+ private PrivateInvokeTest withCounter(int counter) {
+ PrivateInvokeTest res = new PrivateInvokeTest();
+ res.counter = counter;
+ return res;
+ }
+
+ public static void main(String... av) throws Throwable {
+ new PrivateInvokeTest().run();
+ }
+ public void run() throws Throwable {
+ testFirst();
+ testInvokeDirect();
+ }
+
+ @Test
+ public void testFirst() throws Throwable {
+ if (true) return; // nothing here
+ try {
+ System.out.println("start of testFirst");
+ } finally {
+ System.out.println("end of testFirst");
+ }
+ }
+
+ @Test
+ public void testInvokeDirect() {
+ testInvokeDirect(getMethod(THIS_CLASS, "hello"));
+ testInvokeDirect(getMethod(Object.class, "toString"));
+ testInvokeDirect(getMethod(Comparable.class, "compareTo", Object.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "makeString", Object.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "dupString", String.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "intString", int.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "byteString", byte.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "longString", String.class, long.class, String.class));
+ }
+
+ void testInvokeDirect(Method m) {
+ final int refKind = referenceKind(m);
+ testInvokeDirect(m, refKind);
+ testInvokeDirect(m, REF_MH_invokeBasic);
+ }
+ void testInvokeDirect(Method m, int refKind) {
+ if (verbose >= 1)
+ System.out.println("testInvoke m="+m+" : "+REF_KIND_NAMES[refKind]);
+ final MethodHandle mh = unreflect(m);
+ Object[] args = makeArguments(mh.type());
+ Object res1 = invokeWithArguments(m, args);
+ // res1 comes from java.lang.reflect.Method::invoke
+ if (verbose >= 1)
+ System.out.println("m"+Arrays.asList(args)+" => "+res1);
+ // res2 comes from java.lang.invoke.MethodHandle::invoke
+ Object res2 = invokeWithArguments(mh, args);
+ assertEquals(res1, res2);
+ MethodType mtype = mh.type();
+ testInvokeVia("DMH invoker", refKind, directInvoker(refKind, mtype), mh, res1, args);
+ MethodType etype = mtype.erase();
+ if (etype != mtype) {
+ // Try a detuned invoker.
+ testInvokeVia("erased DMH invoker", refKind, directInvoker(refKind, etype), mh, res1, args);
+ }
+ MethodType btype = basicType(mtype);
+ if (btype != mtype && btype != etype) {
+ // Try a detuned invoker.
+ testInvokeVia("basic DMH invoker", refKind, directInvoker(refKind, btype), mh, res1, args);
+ }
+ if (false) {
+ // this can crash the JVM
+ testInvokeVia("generic DMH invoker", refKind, directInvoker(refKind, mtype.generic()), mh, res1, args);
+ }
+ refKindTestCounts[refKind] += 1;
+ }
+
+ void testInvokeVia(String kind, int refKind, MethodHandle invoker, MethodHandle mh, Object res1, Object... args) {
+ Object[] args1;
+ if (refKind == REF_MH_invokeBasic)
+ args1 = pushAtFront(mh, args);
+ else
+ args1 = pushAtBack(args, internalMemberName(mh));
+ if (verbose >= 2) {
+ System.out.println(kind+" invoker="+invoker+" mh="+debugString(mh)+" args="+Arrays.asList(args1));
+ }
+ Object res3 = invokeWithArguments(invoker, args1);
+ assertEquals(res1, res3);
+ }
+}
--- a/jdk/test/java/lang/invoke/ThrowExceptionsTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/java/lang/invoke/ThrowExceptionsTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -222,7 +222,7 @@
return savedEx;
}
- private static void assertEquals(Object x, Object y) {
+ private static void assertEquals(Object x, Object y) {
if (x == y || x != null && x.equals(y)) return;
throw new RuntimeException(x+" != "+y);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/remote/RemoteExample.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009-2010 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package test.java.lang.invoke.remote;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import test.java.lang.invoke.MethodHandlesTest;
+
+/**
+ * Out-of-package access into protected members of test.java.lang.invoke.remote.MethodHandle.PubExample.
+ */
+public class RemoteExample extends MethodHandlesTest.PubExample {
+ public RemoteExample() { super("RemoteExample"); }
+ public static Lookup lookup() { return MethodHandles.lookup(); }
+ public final void fin_v0() { MethodHandlesTest.called("Rem/fin_v0", this); }
+ protected void pro_v0() { MethodHandlesTest.called("Rem/pro_v0", this); }
+ protected static void pro_s0() { MethodHandlesTest.called("Rem/pro_s0"); }
+}
--- a/jdk/test/sun/invoke/util/ValueConversionsTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -122,36 +122,6 @@
}
@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()) {
@@ -180,65 +150,6 @@
}
@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 };
@@ -281,6 +192,91 @@
}
@Test
+ public void testConvert() throws Throwable {
+ //System.out.println("convert");
+ for (long tval = 0, ctr = 0;;) {
+ if (++ctr > 99999) throw new AssertionError("too many test values");
+ // next test value:
+ //System.out.println(Long.toHexString(tval)); // prints 3776 test patterns
+ tval = nextTestValue(tval);
+ if (tval == 0) {
+ //System.out.println("test value count = "+ctr); // 3776 = 8*59*8
+ break; // repeat
+ }
+ }
+ for (Wrapper src : Wrapper.values()) {
+ for (Wrapper dst : Wrapper.values()) {
+ testConvert(src, dst, 0);
+ }
+ }
+ }
+ static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable {
+ //System.out.println(src+" => "+dst);
+ boolean testSingleCase = (tval != 0);
+ final long tvalInit = tval;
+ MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
+ MethodType convType;
+ if (src == Wrapper.VOID)
+ convType = MethodType.methodType(dst.primitiveType() /* , void */);
+ else
+ convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
+ assertEquals(convType, conv.type());
+ MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
+ for (;;) {
+ long n = tval;
+ Object testValue = src.wrap(n);
+ Object expResult = dst.cast(testValue, dst.primitiveType());
+ Object result;
+ switch (src) {
+ case INT: result = converter.invokeExact((int)n); break;
+ case LONG: result = converter.invokeExact(/*long*/n); break;
+ case FLOAT: result = converter.invokeExact((float)n); break;
+ case DOUBLE: result = converter.invokeExact((double)n); break;
+ case CHAR: result = converter.invokeExact((char)n); break;
+ case BYTE: result = converter.invokeExact((byte)n); break;
+ case SHORT: result = converter.invokeExact((short)n); break;
+ case OBJECT: result = converter.invokeExact((Object)n); break;
+ case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
+ case VOID: result = converter.invokeExact(); break;
+ default: throw new AssertionError();
+ }
+ assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),
+ expResult, result);
+ if (testSingleCase) break;
+ // next test value:
+ tval = nextTestValue(tval);
+ if (tval == tvalInit) break; // repeat
+ }
+ }
+ static long tweakSign(long x) {
+ // Assuming that x is mostly zeroes, make those zeroes follow bit #62 (just below the sign).
+ // This function is self-inverse.
+ final long MID_SIGN_BIT = 62;
+ long sign = -((x >>> MID_SIGN_BIT) & 1); // all ones or all zeroes
+ long flip = (sign >>> -MID_SIGN_BIT); // apply the sign below the mid-bit
+ return x ^ flip;
+ }
+ static long nextTestValue(long x) {
+ // Produce 64 bits with three component bitfields: [ high:3 | mid:58 | low:3 ].
+ // The high and low fields vary through all possible bit patterns.
+ // The middle field is either all zero or has a single bit set.
+ // For better coverage of the neighborhood of zero, an internal sign bit is xored downward also.
+ long ux = tweakSign(x); // unsign the middle field
+ final long LOW_BITS = 3, LOW_BITS_MASK = (1L << LOW_BITS)-1;
+ final long HIGH_BITS = 3, HIGH_BITS_MASK = ~(-1L >>> HIGH_BITS);
+ if ((ux & LOW_BITS_MASK) != LOW_BITS_MASK) {
+ ++ux;
+ } else {
+ ux &= ~LOW_BITS_MASK;
+ long midBit = (ux & ~HIGH_BITS_MASK);
+ if (midBit == 0)
+ midBit = (1L<<LOW_BITS); // introduce a low bit
+ ux += midBit;
+ }
+ return tweakSign(ux);
+ }
+
+ @Test
public void testVarargsArray() throws Throwable {
//System.out.println("varargsArray");
final int MIN = START_ARITY;
@@ -332,7 +328,7 @@
}
private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
- System.out.println(arrayType.getSimpleName());
+ //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
--- a/jdk/test/sun/text/resources/LocaleData Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/sun/text/resources/LocaleData Mon Aug 27 10:58:40 2012 -0700
@@ -694,7 +694,6 @@
FormatData/es_UY/NumberElements/2=;
CurrencyNames/es_VE/VEB=Bs
# bug 6570259
-CurrencyNames/es_VE/VEF=BsF.
FormatData/es_VE/NumberPatterns/0=#,##0.###;-#,##0.###
# FormatData/es_VE/NumberPatterns/1=Bs#,##0.00;Bs -#,##0.00 # Changed; see bug 4122840
FormatData/es_VE/NumberPatterns/2=#,##0%
@@ -7073,3 +7072,5 @@
FormatData/sl/DatePatterns/0=EEEE, dd. MMMM y
FormatData/sl/DatePatterns/1=dd. MMMM y
+# bug 7189611
+CurrencyNames/es_VE/VEF=Bs.F.
--- a/jdk/test/sun/text/resources/LocaleDataTest.java Mon Aug 27 10:23:43 2012 +0800
+++ b/jdk/test/sun/text/resources/LocaleDataTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -34,7 +34,7 @@
* 6509039 6609737 6610748 6645271 6507067 6873931 6450945 6645268 6646611
* 6645405 6650730 6910489 6573250 6870908 6585666 6716626 6914413 6916787
* 6919624 6998391 7019267 7020960 7025837 7020583 7036905 7066203 7101495
- * 7003124 7085757 7028073 7171028
+ * 7003124 7085757 7028073 7171028 7189611
* @summary Verify locale data
*
*/
--- a/langtools/.hgtags Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/.hgtags Mon Aug 27 10:58:40 2012 -0700
@@ -172,3 +172,5 @@
afb0a523155727d42b1c773f783ff3a7cfab8e86 jdk8-b48
c72c164ced676d3c360d99b1c52cc80940fc3122 jdk8-b49
b2d8a270f5f2144e14a1fe97fbda9e4391a5332e jdk8-b50
+c4cd4cab2220817c88c8c139c9bfc91c36b48826 jdk8-b51
+1d2db0e5eabc2eaf865986f7b7ffbf7b14b00232 jdk8-b52
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Source.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Source.java Mon Aug 27 10:58:40 2012 -0700
@@ -200,6 +200,9 @@
public boolean allowMethodReferences() {
return compareTo(JDK1_8) >= 0;
}
+ public boolean allowEffectivelyFinalInInnerClasses() {
+ return compareTo(JDK1_8) >= 0;
+ }
public static SourceVersion toSourceVersion(Source source) {
switch(source) {
case JDK1_2:
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Aug 27 10:58:40 2012 -0700
@@ -228,7 +228,7 @@
* @param env The current environment.
*/
boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
- Symbol owner = env.info.scope.owner;
+ Symbol owner = owner(env);
// owner refers to the innermost variable, method or
// initializer block declaration at this point.
return
@@ -243,6 +243,41 @@
((v.flags() & STATIC) != 0) == Resolve.isStatic(env));
}
+ /**
+ * Return the innermost enclosing owner symbol in a given attribution context
+ */
+ Symbol owner(Env<AttrContext> env) {
+ while (true) {
+ switch (env.tree.getTag()) {
+ case VARDEF:
+ //a field can be owner
+ VarSymbol vsym = ((JCVariableDecl)env.tree).sym;
+ if (vsym.owner.kind == TYP) {
+ return vsym;
+ }
+ break;
+ case METHODDEF:
+ //method def is always an owner
+ return ((JCMethodDecl)env.tree).sym;
+ case CLASSDEF:
+ //class def is always an owner
+ return ((JCClassDecl)env.tree).sym;
+ case BLOCK:
+ //static/instance init blocks are owner
+ Symbol blockSym = env.info.scope.owner;
+ if ((blockSym.flags() & BLOCK) != 0) {
+ return blockSym;
+ }
+ break;
+ case TOPLEVEL:
+ //toplevel is always an owner (for pkge decls)
+ return env.info.scope.owner;
+ }
+ Assert.checkNonNull(env.next);
+ env = env.next;
+ }
+ }
+
/** Check that variable can be assigned to.
* @param pos The current source code position.
* @param v The assigned varaible
@@ -883,7 +918,6 @@
memberEnter.memberEnter(tree, env);
annotate.flush();
}
- tree.sym.flags_field |= EFFECTIVELY_FINAL;
}
VarSymbol v = tree.sym;
@@ -1131,8 +1165,8 @@
for (JCTree resource : tree.resources) {
CheckContext twrContext = new Check.NestedCheckContext(resultInfo.checkContext) {
@Override
- public void report(DiagnosticPosition pos, Type found, Type req, JCDiagnostic details) {
- chk.basicHandler.report(pos, found, req, diags.fragment("try.not.applicable.to.type", found));
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ chk.basicHandler.report(pos, diags.fragment("try.not.applicable.to.type", details));
}
};
ResultInfo twrResult = new ResultInfo(VAL, syms.autoCloseableType, twrContext);
@@ -1865,7 +1899,7 @@
Type attribDiamond(Env<AttrContext> env,
final JCNewClass tree,
- Type clazztype,
+ final Type clazztype,
List<Type> argtypes,
List<Type> typeargtypes) {
if (clazztype.isErroneous() ||
@@ -1892,27 +1926,26 @@
argtypes,
typeargtypes);
+ Type owntype = types.createErrorType(clazztype);
if (constructor.kind == MTH) {
- try {
- clazztype = rawCheckMethod(site,
- constructor,
- resultInfo,
- localEnv,
- tree.args,
- argtypes,
- typeargtypes,
- localEnv.info.varArgs).getReturnType();
- } catch (Resolve.InapplicableMethodException ex) {
- //an error occurred while inferring uninstantiated type-variables
- resultInfo.checkContext.report(tree.clazz.pos(), clazztype, resultInfo.pt,
- diags.fragment("cant.apply.diamond.1", diags.fragment("diamond", clazztype.tsym), ex.diagnostic));
- clazztype = syms.errType;
- }
- } else {
- clazztype = syms.errType;
+ ResultInfo diamondResult = new ResultInfo(VAL, resultInfo.pt, new Check.NestedCheckContext(resultInfo.checkContext) {
+ @Override
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ enclosingContext.report(tree.clazz.pos(),
+ diags.fragment("cant.apply.diamond.1", diags.fragment("diamond", clazztype.tsym), details));
+ }
+ });
+ owntype = checkMethod(site,
+ constructor,
+ diamondResult,
+ localEnv,
+ tree.args,
+ argtypes,
+ typeargtypes,
+ localEnv.info.varArgs).getReturnType();
}
- return chk.checkClassType(tree.clazz.pos(), clazztype, true);
+ return chk.checkClassType(tree.clazz.pos(), owntype, true);
}
/** Make an attributed null check tree.
@@ -2188,16 +2221,6 @@
// illegal forward reference.
checkInit(tree, env, v, false);
- // If symbol is a local variable accessed from an embedded
- // inner class check that it is final.
- if (v.owner.kind == MTH &&
- v.owner != env.info.scope.owner &&
- (v.flags_field & FINAL) == 0) {
- log.error(tree.pos(),
- "local.var.accessed.from.icls.needs.final",
- v);
- }
-
// If we are expecting a variable (as opposed to a value), check
// that the variable is assignable in the current environment.
if (pkind() == VAR)
@@ -2591,7 +2614,7 @@
// and are subject to definite assignment checking.
if ((env.info.enclVar == v || v.pos > tree.pos) &&
v.owner.kind == TYP &&
- canOwnInitializer(env.info.scope.owner) &&
+ canOwnInitializer(owner(env)) &&
v.owner == env.info.scope.owner.enclClass() &&
((v.flags() & STATIC) != 0) == Resolve.isStatic(env) &&
(!env.tree.hasTag(ASSIGN) ||
@@ -2687,32 +2710,6 @@
List<Type> argtypes,
List<Type> typeargtypes,
boolean useVarargs) {
- try {
- return rawCheckMethod(site, sym, resultInfo, env, argtrees, argtypes, typeargtypes, useVarargs);
- } catch (Resolve.InapplicableMethodException ex) {
- String key = ex.getDiagnostic() == null ?
- "cant.apply.symbol" :
- "cant.apply.symbol.1";
- log.error(env.tree.pos, key,
- Kinds.kindName(sym),
- sym.name == names.init ? sym.owner.name : sym.name,
- rs.methodArguments(sym.type.getParameterTypes()),
- rs.methodArguments(argtypes),
- Kinds.kindName(sym.owner),
- sym.owner.type,
- ex.getDiagnostic());
- return types.createErrorType(site);
- }
- }
-
- private Type rawCheckMethod(Type site,
- Symbol sym,
- ResultInfo resultInfo,
- Env<AttrContext> env,
- final List<JCExpression> argtrees,
- List<Type> argtypes,
- List<Type> typeargtypes,
- boolean useVarargs) {
// Test (5): if symbol is an instance method of a raw type, issue
// an unchecked warning if its argument types change under erasure.
if (allowGenerics &&
@@ -2733,19 +2730,29 @@
// Resolve.instantiate from the symbol's type as well as
// any type arguments and value arguments.
noteWarner.clear();
- Type owntype = rs.rawInstantiate(env,
- site,
- sym,
- resultInfo,
- argtypes,
- typeargtypes,
- true,
- useVarargs,
- noteWarner);
-
- boolean unchecked = noteWarner.hasNonSilentLint(LintCategory.UNCHECKED);
-
- return chk.checkMethod(owntype, sym, env, argtrees, argtypes, useVarargs, unchecked);
+ try {
+ Type owntype = rs.rawInstantiate(
+ env,
+ site,
+ sym,
+ resultInfo,
+ argtypes,
+ typeargtypes,
+ allowBoxing,
+ useVarargs,
+ noteWarner);
+
+ return chk.checkMethod(owntype, sym, env, argtrees, argtypes, useVarargs,
+ noteWarner.hasNonSilentLint(LintCategory.UNCHECKED));
+ } catch (Infer.InferenceException ex) {
+ //invalid target type - propagate exception outwards or report error
+ //depending on the current check context
+ resultInfo.checkContext.report(env.tree.pos(), ex.getDiagnostic());
+ return types.createErrorType(site);
+ } catch (Resolve.InapplicableMethodException ex) {
+ Assert.error();
+ return null;
+ }
}
/**
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java Mon Aug 27 10:58:40 2012 -0700
@@ -426,7 +426,7 @@
/**
* Report a check error
*/
- void report(DiagnosticPosition pos, Type found, Type req, JCDiagnostic details);
+ void report(DiagnosticPosition pos, JCDiagnostic details);
/**
* Obtain a warner for this check context
*/
@@ -450,8 +450,8 @@
return enclosingContext.compatible(found, req, warn);
}
- public void report(DiagnosticPosition pos, Type found, Type req, JCDiagnostic details) {
- enclosingContext.report(pos, found, req, details);
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ enclosingContext.report(pos, details);
}
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
@@ -463,12 +463,8 @@
* Check context to be used when evaluating assignment/return statements
*/
CheckContext basicHandler = new CheckContext() {
- public void report(DiagnosticPosition pos, Type found, Type req, JCDiagnostic details) {
- if (details == null) {
- log.error(pos, "prob.found.req", found, req);
- } else {
- log.error(pos, "prob.found.req.1", details);
- }
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ log.error(pos, "prob.found.req", details);
}
public boolean compatible(Type found, Type req, Warner warn) {
return types.isAssignable(found, req, warn);
@@ -498,10 +494,10 @@
return found;
} else {
if (found.tag <= DOUBLE && req.tag <= DOUBLE) {
- checkContext.report(pos, found, req, diags.fragment("possible.loss.of.precision"));
+ checkContext.report(pos, diags.fragment("possible.loss.of.precision", found, req));
return types.createErrorType(found);
}
- checkContext.report(pos, found, req, null);
+ checkContext.report(pos, diags.fragment("inconvertible.types", found, req));
return types.createErrorType(found);
}
}
@@ -519,7 +515,7 @@
if (types.isCastable(found, req, castWarner(pos, found, req))) {
return req;
} else {
- checkContext.report(pos, found, req, diags.fragment("inconvertible.types", found, req));
+ checkContext.report(pos, diags.fragment("inconvertible.types", found, req));
return types.createErrorType(found);
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Mon Aug 27 10:58:40 2012 -0700
@@ -43,13 +43,15 @@
import static com.sun.tools.javac.code.TypeTags.*;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
-/** This pass implements dataflow analysis for Java programs.
- * Liveness analysis checks that every statement is reachable.
- * Exception analysis ensures that every checked exception that is
- * thrown is declared or caught. Definite assignment analysis
- * ensures that each variable is assigned when used. Definite
- * unassignment analysis ensures that no final variable is assigned
- * more than once.
+/** This pass implements dataflow analysis for Java programs though
+ * different AST visitor steps. Liveness analysis (see AliveAlanyzer) checks that
+ * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that
+ * every checked exception that is thrown is declared or caught. Definite assignment analysis
+ * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite
+ * unassignment analysis (see AssignAnalyzer) in ensures that no final variable
+ * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer)
+ * determines that local variables accessed within the scope of an inner class are
+ * either final or effectively-final.
*
* <p>The JLS has a number of problems in the
* specification of these flow analysis problems. This implementation
@@ -188,10 +190,12 @@
private final Check chk;
private TreeMaker make;
private final Resolve rs;
+ private final JCDiagnostic.Factory diags;
private Env<AttrContext> attrEnv;
private Lint lint;
private final boolean allowImprovedRethrowAnalysis;
private final boolean allowImprovedCatchAnalysis;
+ private final boolean allowEffectivelyFinalInInnerClasses;
public static Flow instance(Context context) {
Flow instance = context.get(flowKey);
@@ -201,8 +205,37 @@
}
public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ new AliveAnalyzer().analyzeTree(env, make);
+ new AssignAnalyzer().analyzeTree(env, make);
new FlowAnalyzer().analyzeTree(env, make);
- new AssignAnalyzer().analyzeTree(env, make);
+ new CaptureAnalyzer().analyzeTree(env, make);
+ }
+
+ /**
+ * Definite assignment scan mode
+ */
+ enum FlowKind {
+ /**
+ * This is the normal DA/DU analysis mode
+ */
+ NORMAL("var.might.already.be.assigned", false),
+ /**
+ * This is the speculative DA/DU analysis mode used to speculatively
+ * derive assertions within loop bodies
+ */
+ SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true);
+
+ String errKey;
+ boolean isFinal;
+
+ FlowKind(String errKey, boolean isFinal) {
+ this.errKey = errKey;
+ this.isFinal = isFinal;
+ }
+
+ boolean isFinal() {
+ return isFinal;
+ }
}
protected Flow(Context context) {
@@ -214,9 +247,13 @@
chk = Check.instance(context);
lint = Lint.instance(context);
rs = Resolve.instance(context);
+ diags = JCDiagnostic.Factory.instance(context);
Source source = Source.instance(context);
allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis();
allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis();
+ Options options = Options.instance(context);
+ allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses() &&
+ options.isSet("allowEffectivelyFinalInInnerClasses"); //pre-lambda guard
}
/**
@@ -259,14 +296,16 @@
* will typically result in an error unless it is within a
* try-finally whose finally block cannot complete normally.
*/
- abstract static class PendingExit {
+ static class PendingExit {
JCTree tree;
PendingExit(JCTree tree) {
this.tree = tree;
}
- abstract void resolveJump();
+ void resolveJump() {
+ //do nothing
+ }
}
abstract void markDead();
@@ -309,18 +348,352 @@
}
/**
- * This pass implements the first two steps of the dataflow analysis:
- * (i) liveness analysis checks that every statement is reachable and (ii)
- * exception analysis to ensure that every checked exception that is
- * thrown is declared or caught.
+ * This pass implements the first step of the dataflow analysis, namely
+ * the liveness analysis check. This checks that every statement is reachable.
+ * The output of this analysis pass are used by other analyzers. This analyzer
+ * sets the 'finallyCanCompleteNormally' field in the JCTry class.
+ */
+ class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
+
+ /** A flag that indicates whether the last statement could
+ * complete normally.
+ */
+ private boolean alive;
+
+ @Override
+ void markDead() {
+ alive = false;
+ }
+
+ /*************************************************************************
+ * Visitor methods for statements and definitions
+ *************************************************************************/
+
+ /** Analyze a definition.
+ */
+ void scanDef(JCTree tree) {
+ scanStat(tree);
+ if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
+ log.error(tree.pos(),
+ "initializer.must.be.able.to.complete.normally");
+ }
+ }
+
+ /** Analyze a statement. Check that statement is reachable.
+ */
+ void scanStat(JCTree tree) {
+ if (!alive && tree != null) {
+ log.error(tree.pos(), "unreachable.stmt");
+ if (!tree.hasTag(SKIP)) alive = true;
+ }
+ scan(tree);
+ }
+
+ /** Analyze list of statements.
+ */
+ void scanStats(List<? extends JCStatement> trees) {
+ if (trees != null)
+ for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
+ scanStat(l.head);
+ }
+
+ /* ------------ Visitor methods for various sorts of trees -------------*/
+
+ public void visitClassDef(JCClassDecl tree) {
+ if (tree.sym == null) return;
+ boolean alivePrev = alive;
+ ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
+ Lint lintPrev = lint;
+
+ pendingExits = new ListBuffer<PendingExit>();
+ lint = lint.augment(tree.sym.attributes_field);
+
+ try {
+ // process all the static initializers
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) != 0) {
+ scanDef(l.head);
+ }
+ }
+
+ // process all the instance initializers
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) == 0) {
+ scanDef(l.head);
+ }
+ }
+
+ // process all the methods
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (l.head.hasTag(METHODDEF)) {
+ scan(l.head);
+ }
+ }
+ } finally {
+ pendingExits = pendingExitsPrev;
+ alive = alivePrev;
+ lint = lintPrev;
+ }
+ }
+
+ public void visitMethodDef(JCMethodDecl tree) {
+ if (tree.body == null) return;
+ Lint lintPrev = lint;
+
+ lint = lint.augment(tree.sym.attributes_field);
+
+ Assert.check(pendingExits.isEmpty());
+
+ try {
+ alive = true;
+ scanStat(tree.body);
+
+ if (alive && tree.sym.type.getReturnType().tag != VOID)
+ log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
+
+ List<PendingExit> exits = pendingExits.toList();
+ pendingExits = new ListBuffer<PendingExit>();
+ while (exits.nonEmpty()) {
+ PendingExit exit = exits.head;
+ exits = exits.tail;
+ Assert.check(exit.tree.hasTag(RETURN));
+ }
+ } finally {
+ lint = lintPrev;
+ }
+ }
+
+ public void visitVarDef(JCVariableDecl tree) {
+ if (tree.init != null) {
+ Lint lintPrev = lint;
+ lint = lint.augment(tree.sym.attributes_field);
+ try{
+ scan(tree.init);
+ } finally {
+ lint = lintPrev;
+ }
+ }
+ }
+
+ public void visitBlock(JCBlock tree) {
+ scanStats(tree.stats);
+ }
+
+ public void visitDoLoop(JCDoWhileLoop tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<PendingExit>();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ scan(tree.cond);
+ alive = alive && !tree.cond.type.isTrue();
+ alive |= resolveBreaks(tree, prevPendingExits);
+ }
+
+ public void visitWhileLoop(JCWhileLoop tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<PendingExit>();
+ scan(tree.cond);
+ alive = !tree.cond.type.isFalse();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ alive = resolveBreaks(tree, prevPendingExits) ||
+ !tree.cond.type.isTrue();
+ }
+
+ public void visitForLoop(JCForLoop tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ scanStats(tree.init);
+ pendingExits = new ListBuffer<PendingExit>();
+ if (tree.cond != null) {
+ scan(tree.cond);
+ alive = !tree.cond.type.isFalse();
+ } else {
+ alive = true;
+ }
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ scan(tree.step);
+ alive = resolveBreaks(tree, prevPendingExits) ||
+ tree.cond != null && !tree.cond.type.isTrue();
+ }
+
+ public void visitForeachLoop(JCEnhancedForLoop tree) {
+ visitVarDef(tree.var);
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ scan(tree.expr);
+ pendingExits = new ListBuffer<PendingExit>();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ resolveBreaks(tree, prevPendingExits);
+ alive = true;
+ }
+
+ public void visitLabelled(JCLabeledStatement tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<PendingExit>();
+ scanStat(tree.body);
+ alive |= resolveBreaks(tree, prevPendingExits);
+ }
+
+ public void visitSwitch(JCSwitch tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<PendingExit>();
+ scan(tree.selector);
+ boolean hasDefault = false;
+ for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ alive = true;
+ JCCase c = l.head;
+ if (c.pat == null)
+ hasDefault = true;
+ else
+ scan(c.pat);
+ scanStats(c.stats);
+ // Warn about fall-through if lint switch fallthrough enabled.
+ if (alive &&
+ lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
+ c.stats.nonEmpty() && l.tail.nonEmpty())
+ log.warning(Lint.LintCategory.FALLTHROUGH,
+ l.tail.head.pos(),
+ "possible.fall-through.into.case");
+ }
+ if (!hasDefault) {
+ alive = true;
+ }
+ alive |= resolveBreaks(tree, prevPendingExits);
+ }
+
+ public void visitTry(JCTry tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<PendingExit>();
+ for (JCTree resource : tree.resources) {
+ if (resource instanceof JCVariableDecl) {
+ JCVariableDecl vdecl = (JCVariableDecl) resource;
+ visitVarDef(vdecl);
+ } else if (resource instanceof JCExpression) {
+ scan((JCExpression) resource);
+ } else {
+ throw new AssertionError(tree); // parser error
+ }
+ }
+
+ scanStat(tree.body);
+ boolean aliveEnd = alive;
+
+ for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
+ alive = true;
+ JCVariableDecl param = l.head.param;
+ scan(param);
+ scanStat(l.head.body);
+ aliveEnd |= alive;
+ }
+ if (tree.finalizer != null) {
+ ListBuffer<PendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ alive = true;
+ scanStat(tree.finalizer);
+ tree.finallyCanCompleteNormally = alive;
+ if (!alive) {
+ if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
+ log.warning(Lint.LintCategory.FINALLY,
+ TreeInfo.diagEndPos(tree.finalizer),
+ "finally.cannot.complete");
+ }
+ } else {
+ while (exits.nonEmpty()) {
+ pendingExits.append(exits.next());
+ }
+ alive = aliveEnd;
+ }
+ } else {
+ alive = aliveEnd;
+ ListBuffer<PendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ while (exits.nonEmpty()) pendingExits.append(exits.next());
+ }
+ }
+
+ @Override
+ public void visitIf(JCIf tree) {
+ scan(tree.cond);
+ scanStat(tree.thenpart);
+ if (tree.elsepart != null) {
+ boolean aliveAfterThen = alive;
+ alive = true;
+ scanStat(tree.elsepart);
+ alive = alive | aliveAfterThen;
+ } else {
+ alive = true;
+ }
+ }
+
+ public void visitBreak(JCBreak tree) {
+ recordExit(tree, new PendingExit(tree));
+ }
+
+ public void visitContinue(JCContinue tree) {
+ recordExit(tree, new PendingExit(tree));
+ }
+
+ public void visitReturn(JCReturn tree) {
+ scan(tree.expr);
+ recordExit(tree, new PendingExit(tree));
+ }
+
+ public void visitThrow(JCThrow tree) {
+ scan(tree.expr);
+ markDead();
+ }
+
+ public void visitApply(JCMethodInvocation tree) {
+ scan(tree.meth);
+ scan(tree.args);
+ }
+
+ public void visitNewClass(JCNewClass tree) {
+ scan(tree.encl);
+ scan(tree.args);
+ if (tree.def != null) {
+ scan(tree.def);
+ }
+ }
+
+ public void visitTopLevel(JCCompilationUnit tree) {
+ // Do nothing for TopLevel since each class is visited individually
+ }
+
+ /**************************************************************************
+ * main method
+ *************************************************************************/
+
+ /** Perform definite assignment/unassignment analysis on a tree.
+ */
+ public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ try {
+ attrEnv = env;
+ Flow.this.make = make;
+ pendingExits = new ListBuffer<PendingExit>();
+ alive = true;
+ scan(env.tree);
+ } finally {
+ pendingExits = null;
+ Flow.this.make = null;
+ }
+ }
+ }
+
+ /**
+ * This pass implements the second step of the dataflow analysis, namely
+ * the exception analysis. This is to ensure that every checked exception that is
+ * thrown is declared or caught. The analyzer uses some info that has been set by
+ * the liveliness analyzer.
*/
class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> {
/** A flag that indicates whether the last statement could
* complete normally.
*/
- private boolean alive;
-
HashMap<Symbol, List<Type>> preciseRethrowTypes;
/** The current class being defined.
@@ -344,13 +717,11 @@
super(tree);
this.thrown = thrown;
}
-
- void resolveJump() { /*do nothing*/ }
}
@Override
void markDead() {
- alive = false;
+ //do nothing
}
/*-------------------- Exceptions ----------------------*/
@@ -395,34 +766,6 @@
* Visitor methods for statements and definitions
*************************************************************************/
- /** Analyze a definition.
- */
- void scanDef(JCTree tree) {
- scanStat(tree);
- if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
- log.error(tree.pos(),
- "initializer.must.be.able.to.complete.normally");
- }
- }
-
- /** Analyze a statement. Check that statement is reachable.
- */
- void scanStat(JCTree tree) {
- if (!alive && tree != null) {
- log.error(tree.pos(), "unreachable.stmt");
- if (!tree.hasTag(SKIP)) alive = true;
- }
- scan(tree);
- }
-
- /** Analyze list of statements.
- */
- void scanStats(List<? extends JCStatement> trees) {
- if (trees != null)
- for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
- scanStat(l.head);
- }
-
/* ------------ Visitor methods for various sorts of trees -------------*/
public void visitClassDef(JCClassDecl tree) {
@@ -431,7 +774,6 @@
JCClassDecl classDefPrev = classDef;
List<Type> thrownPrev = thrown;
List<Type> caughtPrev = caught;
- boolean alivePrev = alive;
ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits;
Lint lintPrev = lint;
@@ -448,7 +790,7 @@
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) != 0) {
- scanDef(l.head);
+ scan(l.head);
errorUncaught();
}
}
@@ -475,7 +817,7 @@
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) == 0) {
- scanDef(l.head);
+ scan(l.head);
errorUncaught();
}
}
@@ -508,7 +850,6 @@
thrown = thrownPrev;
} finally {
pendingExits = pendingExitsPrev;
- alive = alivePrev;
caught = caughtPrev;
classDef = classDefPrev;
lint = lintPrev;
@@ -538,11 +879,7 @@
// else we are in an instance initializer block;
// leave caught unchanged.
- alive = true;
- scanStat(tree.body);
-
- if (alive && tree.sym.type.getReturnType().tag != VOID)
- log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
+ scan(tree.body);
List<FlowPendingExit> exits = pendingExits.toList();
pendingExits = new ListBuffer<FlowPendingExit>();
@@ -575,45 +912,38 @@
}
public void visitBlock(JCBlock tree) {
- scanStats(tree.stats);
+ scan(tree.stats);
}
public void visitDoLoop(JCDoWhileLoop tree) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<FlowPendingExit>();
- scanStat(tree.body);
- alive |= resolveContinues(tree);
+ scan(tree.body);
+ resolveContinues(tree);
scan(tree.cond);
- alive = alive && !tree.cond.type.isTrue();
- alive |= resolveBreaks(tree, prevPendingExits);
+ resolveBreaks(tree, prevPendingExits);
}
public void visitWhileLoop(JCWhileLoop tree) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<FlowPendingExit>();
scan(tree.cond);
- alive = !tree.cond.type.isFalse();
- scanStat(tree.body);
- alive |= resolveContinues(tree);
- alive = resolveBreaks(tree, prevPendingExits) ||
- !tree.cond.type.isTrue();
+ scan(tree.body);
+ resolveContinues(tree);
+ resolveBreaks(tree, prevPendingExits);
}
public void visitForLoop(JCForLoop tree) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
- scanStats(tree.init);
+ scan(tree.init);
pendingExits = new ListBuffer<FlowPendingExit>();
if (tree.cond != null) {
scan(tree.cond);
- alive = !tree.cond.type.isFalse();
- } else {
- alive = true;
}
- scanStat(tree.body);
- alive |= resolveContinues(tree);
+ scan(tree.body);
+ resolveContinues(tree);
scan(tree.step);
- alive = resolveBreaks(tree, prevPendingExits) ||
- tree.cond != null && !tree.cond.type.isTrue();
+ resolveBreaks(tree, prevPendingExits);
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
@@ -621,44 +951,30 @@
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<FlowPendingExit>();
- scanStat(tree.body);
- alive |= resolveContinues(tree);
+ scan(tree.body);
+ resolveContinues(tree);
resolveBreaks(tree, prevPendingExits);
- alive = true;
}
public void visitLabelled(JCLabeledStatement tree) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<FlowPendingExit>();
- scanStat(tree.body);
- alive |= resolveBreaks(tree, prevPendingExits);
+ scan(tree.body);
+ resolveBreaks(tree, prevPendingExits);
}
public void visitSwitch(JCSwitch tree) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<FlowPendingExit>();
scan(tree.selector);
- boolean hasDefault = false;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
- alive = true;
JCCase c = l.head;
- if (c.pat == null)
- hasDefault = true;
- else
+ if (c.pat != null) {
scan(c.pat);
- scanStats(c.stats);
- // Warn about fall-through if lint switch fallthrough enabled.
- if (alive &&
- lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
- c.stats.nonEmpty() && l.tail.nonEmpty())
- log.warning(Lint.LintCategory.FALLTHROUGH,
- l.tail.head.pos(),
- "possible.fall-through.into.case");
+ }
+ scan(c.stats);
}
- if (!hasDefault) {
- alive = true;
- }
- alive |= resolveBreaks(tree, prevPendingExits);
+ resolveBreaks(tree, prevPendingExits);
}
public void visitTry(JCTry tree) {
@@ -706,17 +1022,15 @@
}
}
}
- scanStat(tree.body);
+ scan(tree.body);
List<Type> thrownInTry = allowImprovedCatchAnalysis ?
chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
thrown;
thrown = thrownPrev;
caught = caughtPrev;
- boolean aliveEnd = alive;
List<Type> caughtInTry = List.nil();
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
- alive = true;
JCVariableDecl param = l.head.param;
List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
((JCTypeUnion)l.head.param.vartype).alternatives :
@@ -735,26 +1049,18 @@
}
scan(param);
preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
- scanStat(l.head.body);
+ scan(l.head.body);
preciseRethrowTypes.remove(param.sym);
- aliveEnd |= alive;
}
if (tree.finalizer != null) {
List<Type> savedThrown = thrown;
thrown = List.nil();
ListBuffer<FlowPendingExit> exits = pendingExits;
pendingExits = prevPendingExits;
- alive = true;
- scanStat(tree.finalizer);
- tree.finallyCanCompleteNormally = alive;
- if (!alive) {
+ scan(tree.finalizer);
+ if (!tree.finallyCanCompleteNormally) {
// discard exits and exceptions from try and finally
thrown = chk.union(thrown, thrownPrev);
- if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
- log.warning(Lint.LintCategory.FINALLY,
- TreeInfo.diagEndPos(tree.finalizer),
- "finally.cannot.complete");
- }
} else {
thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
thrown = chk.union(thrown, savedThrown);
@@ -763,11 +1069,9 @@
while (exits.nonEmpty()) {
pendingExits.append(exits.next());
}
- alive = aliveEnd;
}
} else {
thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
- alive = aliveEnd;
ListBuffer<FlowPendingExit> exits = pendingExits;
pendingExits = prevPendingExits;
while (exits.nonEmpty()) pendingExits.append(exits.next());
@@ -777,14 +1081,9 @@
@Override
public void visitIf(JCIf tree) {
scan(tree.cond);
- scanStat(tree.thenpart);
+ scan(tree.thenpart);
if (tree.elsepart != null) {
- boolean aliveAfterThen = alive;
- alive = true;
- scanStat(tree.elsepart);
- alive = alive | aliveAfterThen;
- } else {
- alive = true;
+ scan(tree.elsepart);
}
}
@@ -826,7 +1125,6 @@
public void visitReturn(JCReturn tree) {
scan(tree.expr);
- // if not initial constructor, should markDead instead of recordExit
recordExit(tree, new FlowPendingExit(tree, null));
}
@@ -898,13 +1196,14 @@
/** Perform definite assignment/unassignment analysis on a tree.
*/
public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ analyzeTree(env, env.tree, make);
+ }
+ public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
try {
attrEnv = env;
- JCTree tree = env.tree;
Flow.this.make = make;
pendingExits = new ListBuffer<FlowPendingExit>();
preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
- alive = true;
this.thrown = this.caught = null;
this.classDef = null;
scan(tree);
@@ -921,7 +1220,8 @@
* This pass implements (i) definite assignment analysis, which ensures that
* each variable is assigned when used and (ii) definite unassignment analysis,
* which ensures that no final variable is assigned more than once. This visitor
- * depends on the results of the liveliness analyzer.
+ * depends on the results of the liveliness analyzer. This pass is also used to mark
+ * effectively-final local variables/parameters.
*/
class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> {
@@ -972,7 +1272,10 @@
Scope unrefdResources;
/** Set when processing a loop body the second time for DU analysis. */
- boolean loopPassTwo = false;
+ FlowKind flowKind = FlowKind.NORMAL;
+
+ /** The starting position of the analysed tree */
+ int startPos;
class AssignPendingExit extends BaseAnalyzer.PendingExit {
@@ -1004,9 +1307,10 @@
*/
boolean trackable(VarSymbol sym) {
return
- (sym.owner.kind == MTH ||
+ sym.pos >= startPos &&
+ ((sym.owner.kind == MTH ||
((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
- classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
+ classDef.sym.isEnclosedBy((ClassSymbol)sym.owner))));
}
/** Initialize new trackable variable by setting its address field
@@ -1019,6 +1323,9 @@
System.arraycopy(vars, 0, newvars, 0, nextadr);
vars = newvars;
}
+ if ((sym.flags() & FINAL) == 0) {
+ sym.flags_field |= EFFECTIVELY_FINAL;
+ }
sym.adr = nextadr;
vars[nextadr] = sym;
inits.excl(nextadr);
@@ -1030,7 +1337,17 @@
*/
void letInit(DiagnosticPosition pos, VarSymbol sym) {
if (sym.adr >= firstadr && trackable(sym)) {
- if ((sym.flags() & FINAL) != 0) {
+ if ((sym.flags() & EFFECTIVELY_FINAL) != 0) {
+ if (!uninits.isMember(sym.adr)) {
+ //assignment targeting an effectively final variable
+ //makes the variable lose its status of effectively final
+ //if the variable is _not_ definitively unassigned
+ sym.flags_field &= ~EFFECTIVELY_FINAL;
+ } else {
+ uninit(sym);
+ }
+ }
+ else if ((sym.flags() & FINAL) != 0) {
if ((sym.flags() & PARAMETER) != 0) {
if ((sym.flags() & UNION) != 0) { //multi-catch parameter
log.error(pos, "multicatch.parameter.may.not.be.assigned",
@@ -1041,18 +1358,9 @@
sym);
}
} else if (!uninits.isMember(sym.adr)) {
- log.error(pos,
- loopPassTwo
- ? "var.might.be.assigned.in.loop"
- : "var.might.already.be.assigned",
- sym);
- } else if (!inits.isMember(sym.adr)) {
- // reachable assignment
- uninits.excl(sym.adr);
- uninitsTry.excl(sym.adr);
+ log.error(pos, flowKind.errKey, sym);
} else {
- //log.rawWarning(pos, "unreachable assignment");//DEBUG
- uninits.excl(sym.adr);
+ uninit(sym);
}
}
inits.incl(sym.adr);
@@ -1060,6 +1368,17 @@
log.error(pos, "var.might.already.be.assigned", sym);
}
}
+ //where
+ void uninit(VarSymbol sym) {
+ if (!inits.isMember(sym.adr)) {
+ // reachable assignment
+ uninits.excl(sym.adr);
+ uninitsTry.excl(sym.adr);
+ } else {
+ //log.rawWarning(pos, "unreachable assignment");//DEBUG
+ uninits.excl(sym.adr);
+ }
+ }
/** If tree is either a simple name or of the form this.name or
* C.this.name, and tree represents a trackable variable,
@@ -1308,66 +1627,79 @@
public void visitDoLoop(JCDoWhileLoop tree) {
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
+ FlowKind prevFlowKind = flowKind;
+ flowKind = FlowKind.NORMAL;
+ Bits initsSkip = null;
+ Bits uninitsSkip = null;
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
do {
Bits uninitsEntry = uninits.dup();
uninitsEntry.excludeFrom(nextadr);
- scan(tree.body);
- resolveContinues(tree);
+ scan(tree.body);
+ resolveContinues(tree);
scanCond(tree.cond);
+ if (!flowKind.isFinal()) {
+ initsSkip = initsWhenFalse;
+ uninitsSkip = uninitsWhenFalse;
+ }
if (log.nerrors != prevErrors ||
- loopPassTwo ||
+ flowKind.isFinal() ||
uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
break;
inits = initsWhenTrue;
uninits = uninitsEntry.andSet(uninitsWhenTrue);
- loopPassTwo = true;
+ flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
+ flowKind = prevFlowKind;
+ inits = initsSkip;
+ uninits = uninitsSkip;
resolveBreaks(tree, prevPendingExits);
}
public void visitWhileLoop(JCWhileLoop tree) {
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
- Bits initsCond;
- Bits uninitsCond;
+ FlowKind prevFlowKind = flowKind;
+ flowKind = FlowKind.NORMAL;
+ Bits initsSkip = null;
+ Bits uninitsSkip = null;
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
+ Bits uninitsEntry = uninits.dup();
+ uninitsEntry.excludeFrom(nextadr);
do {
- Bits uninitsEntry = uninits.dup();
- uninitsEntry.excludeFrom(nextadr);
scanCond(tree.cond);
- initsCond = initsWhenFalse;
- uninitsCond = uninitsWhenFalse;
+ if (!flowKind.isFinal()) {
+ initsSkip = initsWhenFalse;
+ uninitsSkip = uninitsWhenFalse;
+ }
inits = initsWhenTrue;
uninits = uninitsWhenTrue;
scan(tree.body);
resolveContinues(tree);
if (log.nerrors != prevErrors ||
- loopPassTwo ||
+ flowKind.isFinal() ||
uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
break;
uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
+ flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsCond;
- uninits = uninitsCond;
+ flowKind = prevFlowKind;
+ //a variable is DA/DU after the while statement, if it's DA/DU assuming the
+ //branch is not taken AND if it's DA/DU before any break statement
+ inits = initsSkip;
+ uninits = uninitsSkip;
resolveBreaks(tree, prevPendingExits);
}
public void visitForLoop(JCForLoop tree) {
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
+ FlowKind prevFlowKind = flowKind;
+ flowKind = FlowKind.NORMAL;
int nextadrPrev = nextadr;
scan(tree.init);
- Bits initsCond;
- Bits uninitsCond;
+ Bits initsSkip = null;
+ Bits uninitsSkip = null;
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
do {
@@ -1375,29 +1707,33 @@
uninitsEntry.excludeFrom(nextadr);
if (tree.cond != null) {
scanCond(tree.cond);
- initsCond = initsWhenFalse;
- uninitsCond = uninitsWhenFalse;
+ if (!flowKind.isFinal()) {
+ initsSkip = initsWhenFalse;
+ uninitsSkip = uninitsWhenFalse;
+ }
inits = initsWhenTrue;
uninits = uninitsWhenTrue;
- } else {
- initsCond = inits.dup();
- initsCond.inclRange(firstadr, nextadr);
- uninitsCond = uninits.dup();
- uninitsCond.inclRange(firstadr, nextadr);
+ } else if (!flowKind.isFinal()) {
+ initsSkip = inits.dup();
+ initsSkip.inclRange(firstadr, nextadr);
+ uninitsSkip = uninits.dup();
+ uninitsSkip.inclRange(firstadr, nextadr);
}
scan(tree.body);
resolveContinues(tree);
scan(tree.step);
if (log.nerrors != prevErrors ||
- loopPassTwo ||
+ flowKind.isFinal() ||
uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
break;
uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
+ flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsCond;
- uninits = uninitsCond;
+ flowKind = prevFlowKind;
+ //a variable is DA/DU after a for loop, if it's DA/DU assuming the
+ //branch is not taken AND if it's DA/DU before any break statement
+ inits = initsSkip;
+ uninits = uninitsSkip;
resolveBreaks(tree, prevPendingExits);
nextadr = nextadrPrev;
}
@@ -1406,7 +1742,8 @@
visitVarDef(tree.var);
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
+ FlowKind prevFlowKind = flowKind;
+ flowKind = FlowKind.NORMAL;
int nextadrPrev = nextadr;
scan(tree.expr);
Bits initsStart = inits.dup();
@@ -1421,13 +1758,13 @@
scan(tree.body);
resolveContinues(tree);
if (log.nerrors != prevErrors ||
- loopPassTwo ||
+ flowKind.isFinal() ||
uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
break;
uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
+ flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
- loopPassTwo = prevLoopPassTwo;
+ flowKind = prevFlowKind;
inits = initsStart;
uninits = uninitsStart.andSet(uninits);
resolveBreaks(tree, prevPendingExits);
@@ -1628,7 +1965,6 @@
public void visitReturn(JCReturn tree) {
scanExpr(tree.expr);
- // if not initial constructor, should markDead instead of recordExit
recordExit(tree, new AssignPendingExit(tree, inits, uninits));
}
@@ -1669,7 +2005,9 @@
public void visitAssign(JCAssign tree) {
JCTree lhs = TreeInfo.skipParens(tree.lhs);
- if (!(lhs instanceof JCIdent)) scanExpr(lhs);
+ if (!(lhs instanceof JCIdent)) {
+ scanExpr(lhs);
+ }
scanExpr(tree.rhs);
letInit(lhs);
}
@@ -1751,10 +2089,14 @@
/** Perform definite assignment/unassignment analysis on a tree.
*/
public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ analyzeTree(env, env.tree, make);
+ }
+
+ public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
try {
attrEnv = env;
- JCTree tree = env.tree;
Flow.this.make = make;
+ startPos = tree.pos().getStartPosition();
inits = new Bits();
uninits = new Bits();
uninitsTry = new Bits();
@@ -1773,6 +2115,7 @@
scan(tree);
} finally {
// note that recursive invocations of this method fail hard
+ startPos = -1;
inits = uninits = uninitsTry = null;
initsWhenTrue = initsWhenFalse =
uninitsWhenTrue = uninitsWhenFalse = null;
@@ -1787,4 +2130,162 @@
}
}
}
+
+ /**
+ * This pass implements the last step of the dataflow analysis, namely
+ * the effectively-final analysis check. This checks that every local variable
+ * reference from a lambda body/local inner class is either final or effectively final.
+ * As effectively final variables are marked as such during DA/DU, this pass must run after
+ * AssignAnalyzer.
+ */
+ class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
+
+ JCTree currentTree; //local class or lambda
+
+ @Override
+ void markDead() {
+ //do nothing
+ }
+
+ @SuppressWarnings("fallthrough")
+ void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) {
+ if (currentTree != null &&
+ sym.owner.kind == MTH &&
+ sym.pos < currentTree.getStartPosition()) {
+ switch (currentTree.getTag()) {
+ case CLASSDEF:
+ if (!allowEffectivelyFinalInInnerClasses) {
+ if ((sym.flags() & FINAL) == 0) {
+ reportInnerClsNeedsFinalError(pos, sym);
+ }
+ break;
+ }
+ case LAMBDA:
+ if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
+ reportEffectivelyFinalError(pos, sym);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ void letInit(JCTree tree) {
+ tree = TreeInfo.skipParens(tree);
+ if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
+ Symbol sym = TreeInfo.symbol(tree);
+ if (currentTree != null &&
+ sym.kind == VAR &&
+ sym.owner.kind == MTH &&
+ ((VarSymbol)sym).pos < currentTree.getStartPosition()) {
+ switch (currentTree.getTag()) {
+ case CLASSDEF:
+ if (!allowEffectivelyFinalInInnerClasses) {
+ reportInnerClsNeedsFinalError(tree, sym);
+ break;
+ }
+ case LAMBDA:
+ reportEffectivelyFinalError(tree, sym);
+ }
+ }
+ }
+ }
+
+ void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
+ String subKey = currentTree.hasTag(LAMBDA) ?
+ "lambda" : "inner.cls";
+ log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey));
+ }
+
+ void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) {
+ log.error(pos,
+ "local.var.accessed.from.icls.needs.final",
+ sym);
+ }
+
+ /*************************************************************************
+ * Visitor methods for statements and definitions
+ *************************************************************************/
+
+ /* ------------ Visitor methods for various sorts of trees -------------*/
+
+ public void visitClassDef(JCClassDecl tree) {
+ JCTree prevTree = currentTree;
+ try {
+ currentTree = tree.sym.isLocal() ? tree : null;
+ super.visitClassDef(tree);
+ } finally {
+ currentTree = prevTree;
+ }
+ }
+
+ @Override
+ public void visitLambda(JCLambda tree) {
+ JCTree prevTree = currentTree;
+ try {
+ currentTree = tree;
+ super.visitLambda(tree);
+ } finally {
+ currentTree = prevTree;
+ }
+ }
+
+ @Override
+ public void visitIdent(JCIdent tree) {
+ if (tree.sym.kind == VAR) {
+ checkEffectivelyFinal(tree, (VarSymbol)tree.sym);
+ }
+ }
+
+ public void visitAssign(JCAssign tree) {
+ JCTree lhs = TreeInfo.skipParens(tree.lhs);
+ if (!(lhs instanceof JCIdent)) {
+ scan(lhs);
+ }
+ scan(tree.rhs);
+ letInit(lhs);
+ }
+
+ public void visitAssignop(JCAssignOp tree) {
+ scan(tree.lhs);
+ scan(tree.rhs);
+ letInit(tree.lhs);
+ }
+
+ public void visitUnary(JCUnary tree) {
+ switch (tree.getTag()) {
+ case PREINC: case POSTINC:
+ case PREDEC: case POSTDEC:
+ scan(tree.arg);
+ letInit(tree.arg);
+ break;
+ default:
+ scan(tree.arg);
+ }
+ }
+
+ public void visitTopLevel(JCCompilationUnit tree) {
+ // Do nothing for TopLevel since each class is visited individually
+ }
+
+ /**************************************************************************
+ * main method
+ *************************************************************************/
+
+ /** Perform definite assignment/unassignment analysis on a tree.
+ */
+ public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ analyzeTree(env, env.tree, make);
+ }
+ public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
+ try {
+ attrEnv = env;
+ Flow.this.make = make;
+ pendingExits = new ListBuffer<PendingExit>();
+ scan(tree);
+ } finally {
+ pendingExits = null;
+ Flow.this.make = null;
+ }
+ }
+ }
}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java Mon Aug 27 10:58:40 2012 -0700
@@ -75,12 +75,7 @@
log = Log.instance(context);
chk = Check.instance(context);
diags = JCDiagnostic.Factory.instance(context);
- ambiguousNoInstanceException =
- new NoInstanceException(true, diags);
- unambiguousNoInstanceException =
- new NoInstanceException(false, diags);
- invalidInstanceException =
- new InvalidInstanceException(diags);
+ inferenceException = new InferenceException(diags);
}
@@ -92,28 +87,7 @@
}
}
- public static class NoInstanceException extends InferenceException {
- private static final long serialVersionUID = 1;
-
- boolean isAmbiguous; // exist several incomparable best instances?
-
- NoInstanceException(boolean isAmbiguous, JCDiagnostic.Factory diags) {
- super(diags);
- this.isAmbiguous = isAmbiguous;
- }
- }
-
- public static class InvalidInstanceException extends InferenceException {
- private static final long serialVersionUID = 2;
-
- InvalidInstanceException(JCDiagnostic.Factory diags) {
- super(diags);
- }
- }
-
- private final NoInstanceException ambiguousNoInstanceException;
- private final NoInstanceException unambiguousNoInstanceException;
- private final InvalidInstanceException invalidInstanceException;
+ private final InferenceException inferenceException;
/***************************************************************************
* Auxiliary type values and classes
@@ -144,7 +118,7 @@
/** Instantiate undetermined type variable to its minimal upper bound.
* Throw a NoInstanceException if this not possible.
*/
- void maximizeInst(UndetVar that, Warner warn) throws NoInstanceException {
+ void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
List<Type> hibounds = Type.filter(that.hibounds, errorFilter);
if (that.eq.isEmpty()) {
if (hibounds.isEmpty())
@@ -158,7 +132,7 @@
}
if (that.inst == null ||
that.inst.isErroneous())
- throw ambiguousNoInstanceException
+ throw inferenceException
.setMessage("no.unique.maximal.instance.exists",
that.qtype, hibounds);
}
@@ -173,7 +147,7 @@
/** Instantiate undetermined type variable to the lub of all its lower bounds.
* Throw a NoInstanceException if this not possible.
*/
- void minimizeInst(UndetVar that, Warner warn) throws NoInstanceException {
+ void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
List<Type> lobounds = Type.filter(that.lobounds, errorFilter);
if (that.eq.isEmpty()) {
if (lobounds.isEmpty())
@@ -184,7 +158,7 @@
that.inst = types.lub(lobounds);
}
if (that.inst == null || that.inst.tag == ERROR)
- throw ambiguousNoInstanceException
+ throw inferenceException
.setMessage("no.unique.minimal.instance.exists",
that.qtype, lobounds);
} else {
@@ -228,7 +202,7 @@
Type qtype1 = types.subst(mtype.getReturnType(), tvars, undetvars);
if (!types.isSubtype(qtype1,
qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
- throw unambiguousNoInstanceException
+ throw inferenceException
.setMessage("infer.no.conforming.instance.exists",
tvars, mtype.getReturnType(), to);
}
@@ -382,17 +356,17 @@
}
public InapplicableMethodException arityMismatch() {
- return unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch");
+ return inferenceException.setMessage("infer.arg.length.mismatch", inferenceVars(undetvars));
}
- public InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected) {
+ public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
String key = varargs ?
- "infer.varargs.argument.mismatch" :
- "infer.no.conforming.assignment.exists";
- return unambiguousNoInstanceException.setMessage(key,
- inferenceVars(undetvars), found, expected);
+ "infer.varargs.argument.mismatch" :
+ "infer.no.conforming.assignment.exists";
+ return inferenceException.setMessage(key,
+ inferenceVars(undetvars), details);
}
public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
- return unambiguousNoInstanceException.setMessage("inaccessible.varargs.type",
+ return inferenceException.setMessage("inaccessible.varargs.type",
expected, Kinds.kindName(location), location);
}
}
@@ -405,7 +379,7 @@
}
catch (InapplicableMethodException ex) {
// inferred method is not applicable
- throw invalidInstanceException.setMessage(ex.getDiagnostic());
+ throw inferenceException.setMessage(ex.getDiagnostic());
}
}
@@ -415,7 +389,7 @@
List<Type> undetvars,
List<Type> arguments,
Warner warn)
- throws InvalidInstanceException {
+ throws InferenceException {
List<Type> args = arguments;
for (Type t : undetvars) {
UndetVar uv = (UndetVar)t;
@@ -496,7 +470,7 @@
}
//where
void reportBoundError(UndetVar uv, BoundErrorKind bk) {
- throw bk.setMessage(uv.inst == null ? ambiguousNoInstanceException : invalidInstanceException, uv);
+ throw bk.setMessage(inferenceException, uv);
}
/**
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Aug 27 10:58:40 2012 -0700
@@ -550,7 +550,7 @@
/* The number of actuals and formals differ */
InapplicableMethodException arityMismatch();
/* An actual argument type does not conform to the corresponding formal type */
- InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected);
+ InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details);
/* The element type of a varargs is not accessible in the current context */
InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected);
}
@@ -565,12 +565,12 @@
public InapplicableMethodException arityMismatch() {
return inapplicableMethodException.setMessage("arg.length.mismatch");
}
- public InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected) {
+ public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
String key = varargs ?
"varargs.argument.mismatch" :
"no.conforming.assignment.exists";
return inapplicableMethodException.setMessage(key,
- found, expected);
+ details);
}
public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
return inapplicableMethodException.setMessage("inaccessible.varargs.type",
@@ -667,12 +667,8 @@
this.rsWarner = rsWarner;
}
- public void report(DiagnosticPosition pos, Type found, Type req, JCDiagnostic details) {
- throw handler.argumentMismatch(useVarargs, found, req);
- }
-
- public Type rawInstantiatePoly(ForAll found, Type req, Warner warn) {
- throw new AssertionError("ForAll in argument position");
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ throw handler.argumentMismatch(useVarargs, details);
}
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Aug 27 10:58:40 2012 -0700
@@ -113,9 +113,9 @@
this.allowMulticatch = source.allowMulticatch();
this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true);
this.allowLambda = source.allowLambda() &&
- fac.options.isSet("allowLambda");
+ fac.options.isSet("allowLambda"); //pre-lambda guard
this.allowMethodReferences = source.allowMethodReferences() &&
- fac.options.isSet("allowMethodReferences");
+ fac.options.isSet("allowMethodReferences"); //pre-lambda guard
this.keepDocComments = keepDocComments;
docComments = newDocCommentTable(keepDocComments);
this.keepLineMap = keepLineMap;
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Aug 27 10:58:40 2012 -0700
@@ -168,6 +168,14 @@
compiler.err.cant.assign.val.to.final.var=\
cannot assign a value to final variable {0}
+# 0: symbol, 1: message segment
+compiler.err.cant.ref.non.effectively.final.var=\
+ local variables referenced from {1} must be final or effectively final
+
+
+compiler.misc.inner.cls=\
+ an inner class
+
# 0: type
compiler.err.cant.deref=\
{0} cannot be dereferenced
@@ -1497,14 +1505,8 @@
#####
-# 0: type, 1: type
+# 0: message segment
compiler.err.prob.found.req=\
- incompatible types\n\
- required: {1}\n\
- found: {0}
-
-# 0: message segment
-compiler.err.prob.found.req.1=\
incompatible types: {0}
# 0: message segment, 1: type, 2: type
@@ -1517,8 +1519,9 @@
compiler.misc.inconvertible.types=\
{0} cannot be converted to {1}
+# 0: type, 1: type
compiler.misc.possible.loss.of.precision=\
- possible loss of precision
+ possible lossy conversion from {0} to {1}
compiler.misc.unchecked.assign=\
unchecked conversion
@@ -1537,8 +1540,8 @@
# 0: type
compiler.misc.try.not.applicable.to.type=\
- try-with-resources not applicable to variable type {0}\n\
- (expected a variable of type java.lang.AutoCloseable)
+ try-with-resources not applicable to variable type\n\
+ ({0})
#####
@@ -1589,16 +1592,20 @@
compiler.misc.infer.no.conforming.instance.exists=\
no instance(s) of type variable(s) {0} exist so that {1} conforms to {2}
-# 0: list of type, 1: type, 2: type
+# 0: list of type, 1: message segment
compiler.misc.infer.no.conforming.assignment.exists=\
- no instance(s) of type variable(s) {0} exist so that argument type {1} conforms to formal parameter type {2}
-
+ cannot infer type-variable(s) {0}\n\
+ (argument mismatch; {1})
+
+# 0: list of type
compiler.misc.infer.arg.length.mismatch=\
- cannot instantiate from arguments because actual and formal argument lists differ in length
-
-# 0: list of type, 1: type, 2: type
+ cannot infer type-variable(s) {0}\n\
+ (actual and formal argument lists differ in length)
+
+# 0: list of type, 1: message segment
compiler.misc.infer.varargs.argument.mismatch=\
- no instance(s) of type variable(s) {0} exist so that argument type {1} conforms to vararg element type {2}
+ cannot infer type-variable(s) {0}\n\
+ (varargs mismatch; {1})
# 0: type, 1: list of type
compiler.misc.inferred.do.not.conform.to.upper.bounds=\
@@ -1637,13 +1644,13 @@
compiler.misc.arg.length.mismatch=\
actual and formal argument lists differ in length
-# 0: type, 1: type
+# 0: message segment
compiler.misc.no.conforming.assignment.exists=\
- actual argument {0} cannot be converted to {1} by method invocation conversion
-
-# 0: type, 1: type
+ argument mismatch; {0}
+
+# 0: message segment
compiler.misc.varargs.argument.mismatch=\
- argument type {0} does not conform to vararg element type {1}
+ varargs mismatch; {0}
#####
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java Mon Aug 27 10:58:40 2012 -0700
@@ -308,9 +308,8 @@
return endPos(((JCSynchronized) tree).body);
else if (tree.hasTag(TRY)) {
JCTry t = (JCTry) tree;
- return endPos((t.finalizer != null)
- ? t.finalizer
- : t.catchers.last().body);
+ return endPos((t.finalizer != null) ? t.finalizer
+ : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body));
} else
return tree.pos;
}
--- a/langtools/test/tools/javac/6840059/T6840059.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6840059/T6840059.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6840059.java:15:9: compiler.err.cant.apply.symbol.1: kindname.constructor, T6840059, java.lang.Integer, java.lang.String, kindname.class, T6840059, (compiler.misc.no.conforming.assignment.exists: java.lang.String, java.lang.Integer)
+T6840059.java:15:9: compiler.err.cant.apply.symbol.1: kindname.constructor, T6840059, java.lang.Integer, java.lang.String, kindname.class, T6840059, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer))
T6840059.java:15:25: compiler.err.cant.apply.symbol.1: kindname.constructor, T6840059, java.lang.Integer, compiler.misc.no.args, kindname.class, T6840059, (compiler.misc.arg.length.mismatch)
2 errors
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD34.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD34.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD34.java:34:49: compiler.err.prob.found.req: java.lang.Number, boolean
+TestCast6979683_BAD34.java:34:49: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Number, boolean)
1 error
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD35.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD35.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD35.java:35:45: compiler.err.prob.found.req: java.lang.Number, int
+TestCast6979683_BAD35.java:35:45: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Number, int)
1 error
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD36.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD36.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD36.java:36:58: compiler.err.prob.found.req: java.lang.Comparable<java.lang.Integer>, int
+TestCast6979683_BAD36.java:36:58: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Comparable<java.lang.Integer>, int)
1 error
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD37.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD37.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD37.java:37:61: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: java.lang.Comparable<java.lang.Short>, int)
+TestCast6979683_BAD37.java:37:61: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Comparable<java.lang.Short>, int)
1 error
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD38.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD38.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD38.java:38:62: compiler.err.prob.found.req: java.lang.Comparable<java.lang.Character>, float
+TestCast6979683_BAD38.java:38:62: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Comparable<java.lang.Character>, float)
1 error
--- a/langtools/test/tools/javac/6979683/TestCast6979683_BAD39.java.errlog Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/6979683/TestCast6979683_BAD39.java.errlog Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-TestCast6979683_BAD39.java:39:53: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: java.lang.Number, char)
+TestCast6979683_BAD39.java:39:53: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Number, char)
1 error
--- a/langtools/test/tools/javac/7132880/T7132880.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/7132880/T7132880.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
-T7132880.java:23:12: compiler.err.cant.apply.symbol.1: kindname.method, m1, java.lang.Integer, java.lang.String, kindname.class, Outer.Inner1, (compiler.misc.no.conforming.assignment.exists: java.lang.String, java.lang.Integer)
-T7132880.java:33:12: compiler.err.cant.apply.symbols: kindname.method, m1, java.lang.String,{(compiler.misc.inapplicable.method: kindname.method, Outer.Inner2, m1(java.lang.Double), (compiler.misc.no.conforming.assignment.exists: java.lang.String, java.lang.Double)),(compiler.misc.inapplicable.method: kindname.method, Outer.Inner2, m1(java.lang.Integer), (compiler.misc.no.conforming.assignment.exists: java.lang.String, java.lang.Integer))}
+T7132880.java:23:12: compiler.err.cant.apply.symbol.1: kindname.method, m1, java.lang.Integer, java.lang.String, kindname.class, Outer.Inner1, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer))
+T7132880.java:33:12: compiler.err.cant.apply.symbols: kindname.method, m1, java.lang.String,{(compiler.misc.inapplicable.method: kindname.method, Outer.Inner2, m1(java.lang.Double), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Double))),(compiler.misc.inapplicable.method: kindname.method, Outer.Inner2, m1(java.lang.Integer), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)))}
T7132880.java:43:12: compiler.err.ref.ambiguous: m2, kindname.method, m2(java.lang.Object,int), Outer.Inner3, kindname.method, m2(int,java.lang.Object), Outer.Inner3
3 errors
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234a_1.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234a_1.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6722234a.java:12:9: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T6722234a<compiler.misc.type.var: T, 1>, (compiler.misc.no.conforming.assignment.exists: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1)
+T6722234a.java:12:9: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T6722234a<compiler.misc.type.var: T, 1>, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1))
1 error
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234a_2.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234a_2.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6722234a.java:12:9: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T6722234a<compiler.misc.type.var: T, 1>, (compiler.misc.no.conforming.assignment.exists: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1)
+T6722234a.java:12:9: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T6722234a<compiler.misc.type.var: T, 1>, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1))
- compiler.misc.where.description.typevar.1: compiler.misc.type.var: T, 1,compiler.misc.type.var: T, 2,{(compiler.misc.where.typevar: compiler.misc.type.var: T, 1, java.lang.String, kindname.class, T6722234a),(compiler.misc.where.typevar: compiler.misc.type.var: T, 2, java.lang.Integer, kindname.method, <compiler.misc.type.var: T, 2>test(compiler.misc.type.var: T, 2))}
1 error
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234c.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234c.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6722234c.java:14:9: compiler.err.cant.apply.symbol.1: kindname.method, m, T6722234c.String, java.lang.String, kindname.class, T6722234c, (compiler.misc.infer.no.conforming.assignment.exists: T, java.lang.String, T6722234c.String)
+T6722234c.java:14:9: compiler.err.cant.apply.symbol.1: kindname.method, m, T6722234c.String, java.lang.String, kindname.class, T6722234c, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.lang.String, T6722234c.String))
1 error
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234d_1.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234d_1.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6722234d.java:18:20: compiler.err.prob.found.req: compiler.misc.intersection.type: 1, T6722234d.A
+T6722234d.java:18:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.intersection.type: 1, T6722234d.A)
- compiler.misc.where.description.intersection: compiler.misc.intersection.type: 1,{(compiler.misc.where.intersection: compiler.misc.intersection.type: 1, java.lang.Object,T6722234d.I1,T6722234d.I2)}
1 error
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234d_2.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234d_2.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6722234d.java:18:20: compiler.err.prob.found.req: compiler.misc.intersection.type: 1, T6722234d.A
+T6722234d.java:18:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.intersection.type: 1, T6722234d.A)
- compiler.misc.where.description.intersection: compiler.misc.intersection.type: 1,{(compiler.misc.where.intersection: compiler.misc.intersection.type: 1, Object,I1,I2)}
1 error
--- a/langtools/test/tools/javac/Diagnostics/6799605/T6799605.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6799605/T6799605.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
-T6799605.java:17:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.inferred.do.not.conform.to.upper.bounds: compiler.misc.type.captureof: 1, ?, T6799605<compiler.misc.type.captureof: 1, ?>))}
-T6799605.java:18:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,T6799605<compiler.misc.type.captureof: 2, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.inferred.do.not.conform.to.eq.bounds: compiler.misc.type.captureof: 2, ?, compiler.misc.type.captureof: 2, ?,compiler.misc.type.captureof: 1, ?)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.infer.arg.length.mismatch))}
-T6799605.java:19:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,T6799605<compiler.misc.type.captureof: 2, ?>,T6799605<compiler.misc.type.captureof: 3, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.inferred.do.not.conform.to.eq.bounds: compiler.misc.type.captureof: 3, ?, compiler.misc.type.captureof: 3, ?,compiler.misc.type.captureof: 2, ?,compiler.misc.type.captureof: 1, ?)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.infer.arg.length.mismatch))}
+T6799605.java:17:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.inferred.do.not.conform.to.upper.bounds: compiler.misc.type.captureof: 1, ?, T6799605<compiler.misc.type.captureof: 1, ?>))}
+T6799605.java:18:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,T6799605<compiler.misc.type.captureof: 2, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.inferred.do.not.conform.to.eq.bounds: compiler.misc.type.captureof: 2, ?, compiler.misc.type.captureof: 2, ?,compiler.misc.type.captureof: 1, ?)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T))}
+T6799605.java:19:9: compiler.err.cant.apply.symbols: kindname.method, m, T6799605<compiler.misc.type.captureof: 1, ?>,T6799605<compiler.misc.type.captureof: 2, ?>,T6799605<compiler.misc.type.captureof: 3, ?>,{(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>,T6799605<T>), (compiler.misc.inferred.do.not.conform.to.eq.bounds: compiler.misc.type.captureof: 3, ?, compiler.misc.type.captureof: 3, ?,compiler.misc.type.captureof: 2, ?,compiler.misc.type.captureof: 1, ?)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>,T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T)),(compiler.misc.inapplicable.method: kindname.method, T6799605, <T>m(T6799605<T>), (compiler.misc.infer.arg.length.mismatch: T))}
3 errors
--- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6862608a.java:19:33: compiler.err.cant.apply.symbol.1: kindname.method, compound, java.lang.Iterable<? extends java.util.Comparator<? super T>>, java.util.List<java.util.Comparator<?>>, kindname.class, T6862608a, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<java.util.Comparator<?>>, java.lang.Iterable<? extends java.util.Comparator<? super T>>)
+T6862608a.java:19:33: compiler.err.cant.apply.symbol.1: kindname.method, compound, java.lang.Iterable<? extends java.util.Comparator<? super T>>, java.util.List<java.util.Comparator<?>>, kindname.class, T6862608a, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<java.util.Comparator<?>>, java.lang.Iterable<? extends java.util.Comparator<? super T>>))
- compiler.misc.where.description.typevar: T,{(compiler.misc.where.typevar: T, java.lang.Object, kindname.method, <T>compound(java.lang.Iterable<? extends java.util.Comparator<? super T>>))}
1 error
--- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608b.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608b.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6862608b.java:11:7: compiler.err.cant.apply.symbol.1: kindname.method, test, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T66862608b<compiler.misc.type.var: T, 1,compiler.misc.type.var: S, 2>, (compiler.misc.no.conforming.assignment.exists: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1)
+T6862608b.java:11:7: compiler.err.cant.apply.symbol.1: kindname.method, test, compiler.misc.type.var: T, 1, compiler.misc.type.var: T, 2, kindname.class, T66862608b<compiler.misc.type.var: T, 1,compiler.misc.type.var: S, 2>, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: compiler.misc.type.var: T, 2, compiler.misc.type.var: T, 1))
- compiler.misc.where.description.typevar.1: compiler.misc.type.var: T, 1,compiler.misc.type.var: T, 2,compiler.misc.type.var: S, 1,compiler.misc.type.var: S, 2,{(compiler.misc.where.typevar: compiler.misc.type.var: T, 1, java.lang.String, kindname.class, T66862608b),(compiler.misc.where.typevar: compiler.misc.type.var: T, 2, compiler.misc.type.var: S, 1, kindname.method, <compiler.misc.type.var: S, 1,compiler.misc.type.var: T, 2>foo(compiler.misc.type.var: T, 2)),(compiler.misc.where.typevar: compiler.misc.type.var: S, 1, java.lang.Object, kindname.method, <compiler.misc.type.var: S, 1,compiler.misc.type.var: T, 2>foo(compiler.misc.type.var: T, 2)),(compiler.misc.where.typevar: compiler.misc.type.var: S, 2, java.lang.Object, kindname.class, T66862608b)}
1 error
--- a/langtools/test/tools/javac/OverrideChecks/6400189/T6400189a.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/OverrideChecks/6400189/T6400189a.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: <T>getAnnotation(java.lang.Class<T>), java.lang.reflect.Constructor
-T6400189a.java:14:35: compiler.err.prob.found.req: java.lang.annotation.Annotation, java.lang.annotation.Documented
+T6400189a.java:14:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.annotation.Annotation, java.lang.annotation.Documented)
1 error
1 warning
--- a/langtools/test/tools/javac/OverrideChecks/6400189/T6400189b.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/OverrideChecks/6400189/T6400189b.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: <T>m(T6400189b<T>), T6400189b.B
-T6400189b.java:24:24: compiler.err.prob.found.req: java.lang.Object, java.lang.Integer
+T6400189b.java:24:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer)
1 error
1 warning
--- a/langtools/test/tools/javac/StringsInSwitch/BadlyTypedLabel1.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/StringsInSwitch/BadlyTypedLabel1.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-BadlyTypedLabel1.java:13:14: compiler.err.prob.found.req: int, java.lang.String
+BadlyTypedLabel1.java:13:14: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String)
1 error
--- a/langtools/test/tools/javac/StringsInSwitch/BadlyTypedLabel2.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/StringsInSwitch/BadlyTypedLabel2.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-BadlyTypedLabel2.java:15:14: compiler.err.prob.found.req: java.math.RoundingMode, java.lang.String
+BadlyTypedLabel2.java:15:14: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.math.RoundingMode, java.lang.String)
1 error
--- a/langtools/test/tools/javac/T6326754.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/T6326754.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,6 +1,6 @@
T6326754.java:44:12: compiler.err.name.clash.same.erasure: TestConstructor(T), TestConstructor(K)
T6326754.java:52:17: compiler.err.name.clash.same.erasure: setT(K), setT(T)
-T6326754.java:64:18: compiler.err.prob.found.req: T, T
+T6326754.java:64:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T, T)
T6326754.java:70:11: compiler.err.cant.apply.symbol.1: kindname.method, setT, java.lang.Object, compiler.misc.no.args, kindname.class, TestC<T>, (compiler.misc.arg.length.mismatch)
- compiler.note.unchecked.filename: T6326754.java
- compiler.note.unchecked.recompile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/T7178324.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012, 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 7178324
+ * @summary Crash when compiling for(i : x) try(AutoCloseable x = ...) {}
+ * @compile T7178324.java
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+class T7178324 {
+ public static void main(String[] args) throws Exception {
+ List<File> files = new ArrayList<>();
+ for (File f : files)
+ try (FileInputStream is = new FileInputStream(f)) {
+ }
+ }
+}
--- a/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
-TwrOnNonResource.java:12:30: compiler.err.prob.found.req.1: (compiler.misc.try.not.applicable.to.type: TwrOnNonResource)
-TwrOnNonResource.java:15:30: compiler.err.prob.found.req.1: (compiler.misc.try.not.applicable.to.type: TwrOnNonResource)
-TwrOnNonResource.java:18:30: compiler.err.prob.found.req.1: (compiler.misc.try.not.applicable.to.type: TwrOnNonResource)
+TwrOnNonResource.java:12:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type: (compiler.misc.inconvertible.types: TwrOnNonResource, java.lang.AutoCloseable))
+TwrOnNonResource.java:15:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type: (compiler.misc.inconvertible.types: TwrOnNonResource, java.lang.AutoCloseable))
+TwrOnNonResource.java:18:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type: (compiler.misc.inconvertible.types: TwrOnNonResource, java.lang.AutoCloseable))
3 errors
--- a/langtools/test/tools/javac/cast/6270087/T6270087neg.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/6270087/T6270087neg.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6270087neg.java:36:29: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6270087neg.Foo<V>, T6270087neg.Foo<U>)
+T6270087neg.java:36:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6270087neg.Foo<V>, T6270087neg.Foo<U>)
1 error
--- a/langtools/test/tools/javac/cast/6557182/T6557182.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/6557182/T6557182.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,4 +1,4 @@
-T6557182.java:12:56: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T, java.lang.Comparable<java.lang.Integer>)
+T6557182.java:12:56: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T, java.lang.Comparable<java.lang.Integer>)
T6557182.java:16:56: compiler.warn.prob.found.req: (compiler.misc.unchecked.cast.to.type), T, java.lang.Comparable<java.lang.Integer>
1 error
1 warning
--- a/langtools/test/tools/javac/cast/6665356/T6665356.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/6665356/T6665356.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,8 +1,8 @@
-T6665356.java:31:55: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.Number>.Inner<java.lang.Long>)
-T6665356.java:35:58: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? super java.lang.Number>)
-T6665356.java:39:65: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.Number>.Inner<? super java.lang.Number>)
-T6665356.java:43:57: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? extends java.lang.String>.Inner<java.lang.Long>)
-T6665356.java:47:60: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? extends java.lang.String>)
-T6665356.java:51:55: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.String>.Inner<java.lang.Long>)
-T6665356.java:55:58: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? super java.lang.String>)
+T6665356.java:31:55: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.Number>.Inner<java.lang.Long>)
+T6665356.java:35:58: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? super java.lang.Number>)
+T6665356.java:39:65: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.Number>.Inner<? super java.lang.Number>)
+T6665356.java:43:57: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? extends java.lang.String>.Inner<java.lang.Long>)
+T6665356.java:47:60: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? extends java.lang.String>)
+T6665356.java:51:55: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<? super java.lang.String>.Inner<java.lang.Long>)
+T6665356.java:55:58: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6665356.Outer<java.lang.Integer>.Inner<java.lang.Long>, T6665356.Outer<java.lang.Integer>.Inner<? super java.lang.String>)
7 errors
--- a/langtools/test/tools/javac/cast/6795580/T6795580.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/6795580/T6795580.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,8 +1,8 @@
-T6795580.java:31:57: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.Number>.Inner<java.lang.Long>[])
-T6795580.java:35:60: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? super java.lang.Number>[])
-T6795580.java:39:67: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.Number>.Inner<? super java.lang.Number>[])
-T6795580.java:43:59: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? extends java.lang.String>.Inner<java.lang.Long>[])
-T6795580.java:47:62: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? extends java.lang.String>[])
-T6795580.java:51:57: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.String>.Inner<java.lang.Long>[])
-T6795580.java:55:60: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? super java.lang.String>[])
+T6795580.java:31:57: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.Number>.Inner<java.lang.Long>[])
+T6795580.java:35:60: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? super java.lang.Number>[])
+T6795580.java:39:67: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.Number>.Inner<? super java.lang.Number>[])
+T6795580.java:43:59: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? extends java.lang.String>.Inner<java.lang.Long>[])
+T6795580.java:47:62: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? extends java.lang.String>[])
+T6795580.java:51:57: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<? super java.lang.String>.Inner<java.lang.Long>[])
+T6795580.java:55:60: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6795580.Outer<java.lang.Integer>.Inner<java.lang.Long>[], T6795580.Outer<java.lang.Integer>.Inner<? super java.lang.String>[])
7 errors
--- a/langtools/test/tools/javac/cast/6932571/T6932571neg.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/6932571/T6932571neg.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6932571neg.java:39:19: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T6932571neg.S, G)
+T6932571neg.java:39:19: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T6932571neg.S, G)
1 error
--- a/langtools/test/tools/javac/cast/7005095/T7005095neg.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/7005095/T7005095neg.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T7005095neg.java:13:25: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: T7005095pos.FooImpl, T7005095pos.Foo<T>)
+T7005095neg.java:13:25: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T7005095pos.FooImpl, T7005095pos.Foo<T>)
1 error
--- a/langtools/test/tools/javac/cast/7005671/T7005671.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/cast/7005671/T7005671.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,17 +1,17 @@
-T7005671.java:12:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: byte[], X[])
-T7005671.java:13:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: short[], X[])
-T7005671.java:14:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: int[], X[])
-T7005671.java:15:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: long[], X[])
-T7005671.java:16:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: float[], X[])
-T7005671.java:17:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: double[], X[])
-T7005671.java:18:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: char[], X[])
-T7005671.java:19:26: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: boolean[], X[])
-T7005671.java:23:29: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], byte[])
-T7005671.java:24:30: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], short[])
-T7005671.java:25:28: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], int[])
-T7005671.java:26:29: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], long[])
-T7005671.java:27:30: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], float[])
-T7005671.java:28:31: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], double[])
-T7005671.java:29:29: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], char[])
-T7005671.java:30:32: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: X[], boolean[])
+T7005671.java:12:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: byte[], X[])
+T7005671.java:13:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: short[], X[])
+T7005671.java:14:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int[], X[])
+T7005671.java:15:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: long[], X[])
+T7005671.java:16:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: float[], X[])
+T7005671.java:17:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: double[], X[])
+T7005671.java:18:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: char[], X[])
+T7005671.java:19:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: boolean[], X[])
+T7005671.java:23:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], byte[])
+T7005671.java:24:30: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], short[])
+T7005671.java:25:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], int[])
+T7005671.java:26:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], long[])
+T7005671.java:27:30: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], float[])
+T7005671.java:28:31: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], double[])
+T7005671.java:29:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], char[])
+T7005671.java:30:32: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X[], boolean[])
16 errors
--- a/langtools/test/tools/javac/diags/examples/CantApplyDiamond1.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/CantApplyDiamond1.java Mon Aug 27 10:58:40 2012 -0700
@@ -21,7 +21,7 @@
* questions.
*/
-// key: compiler.err.prob.found.req.1
+// key: compiler.err.prob.found.req
// key: compiler.misc.cant.apply.diamond.1
// key: compiler.misc.inferred.do.not.conform.to.upper.bounds
// key: compiler.misc.diamond
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+// key: compiler.err.cant.ref.non.effectively.final.var
+// key: compiler.misc.inner.cls
+// options: -XDallowEffectivelyFinalInInnerClasses
+
+class CantRefNonEffectivelyFinalVar {
+ void test() {
+ int i = 0;
+ new Object() { int j = i; };
+ i = 2;
+ }
+}
--- a/langtools/test/tools/javac/diags/examples/IncompatibleTypes1.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/IncompatibleTypes1.java Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2012, 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
@@ -22,7 +22,7 @@
*/
// key: compiler.misc.infer.no.conforming.instance.exists
-// key: compiler.err.cant.apply.symbol.1
+// key: compiler.err.prob.found.req
class IncompatibleTypes1<V> {
<T> IncompatibleTypes1<Integer> m() {
--- a/langtools/test/tools/javac/diags/examples/InconvertibleTypes.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/InconvertibleTypes.java Mon Aug 27 10:58:40 2012 -0700
@@ -22,7 +22,7 @@
*/
// key: compiler.misc.inconvertible.types
-// key: compiler.err.prob.found.req.1
+// key: compiler.err.prob.found.req
class InconvertibleTypes {
class Outer<S> {
--- a/langtools/test/tools/javac/diags/examples/InferNoConformingAssignment.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/InferNoConformingAssignment.java Mon Aug 27 10:58:40 2012 -0700
@@ -22,6 +22,7 @@
*/
// key: compiler.err.cant.apply.symbol.1
+// key: compiler.misc.inconvertible.types
// key: compiler.misc.infer.no.conforming.assignment.exists
import java.util.*;
--- a/langtools/test/tools/javac/diags/examples/InferVarargsArgumentMismatch.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/InferVarargsArgumentMismatch.java Mon Aug 27 10:58:40 2012 -0700
@@ -23,6 +23,7 @@
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.infer.varargs.argument.mismatch
+// key: compiler.misc.inconvertible.types
class InferVarargsArgumentMismatch {
<X> void m(X x1, String... xs) {}
--- a/langtools/test/tools/javac/diags/examples/InferredDoNotConformToLower.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/InferredDoNotConformToLower.java Mon Aug 27 10:58:40 2012 -0700
@@ -21,7 +21,7 @@
* questions.
*/
-// key: compiler.err.cant.apply.symbol.1
+// key: compiler.err.prob.found.req
// key: compiler.misc.inferred.do.not.conform.to.lower.bounds
import java.util.*;
--- a/langtools/test/tools/javac/diags/examples/KindnameConstructor.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/KindnameConstructor.java Mon Aug 27 10:58:40 2012 -0700
@@ -27,6 +27,7 @@
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.arg.length.mismatch
// key: compiler.misc.no.conforming.assignment.exists
+// key: compiler.misc.inconvertible.types
// key: compiler.misc.count.error.plural
// key: compiler.err.error
// run: backdoor
--- a/langtools/test/tools/javac/diags/examples/NoUniqueMaximalInstance.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/NoUniqueMaximalInstance.java Mon Aug 27 10:58:40 2012 -0700
@@ -21,7 +21,7 @@
* questions.
*/
-// key: compiler.err.cant.apply.symbol.1
+// key: compiler.err.prob.found.req
// key: compiler.misc.no.unique.maximal.instance.exists
class NoUniqueMaximalInstance {
--- a/langtools/test/tools/javac/diags/examples/NotApplicableMethodFound.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/NotApplicableMethodFound.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,6 +25,7 @@
// key: compiler.note.verbose.resolve.multi.1
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.no.conforming.assignment.exists
+// key: compiler.misc.inconvertible.types
// options: -XDverboseResolution=inapplicable,failure
class NotApplicableMethodFound {
--- a/langtools/test/tools/javac/diags/examples/PossibleLossPrecision.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/PossibleLossPrecision.java Mon Aug 27 10:58:40 2012 -0700
@@ -22,7 +22,7 @@
*/
// key: compiler.misc.possible.loss.of.precision
-// key: compiler.err.prob.found.req.1
+// key: compiler.err.prob.found.req
class PossibleLossPrecision {
long l;
--- a/langtools/test/tools/javac/diags/examples/ResourceNotApplicableToType.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/ResourceNotApplicableToType.java Mon Aug 27 10:58:40 2012 -0700
@@ -22,7 +22,8 @@
*/
// key: compiler.misc.try.not.applicable.to.type
-// key: compiler.err.prob.found.req.1
+// key: compiler.err.prob.found.req
+// key: compiler.misc.inconvertible.types
class ResourceNotApplicableToType {
void m() {
--- a/langtools/test/tools/javac/diags/examples/VarargsArgumentMismatch.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/VarargsArgumentMismatch.java Mon Aug 27 10:58:40 2012 -0700
@@ -23,6 +23,7 @@
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.varargs.argument.mismatch
+// key: compiler.misc.inconvertible.types
class VarargsArgumentMismatch {
void m(String s, Integer... is) {}
--- a/langtools/test/tools/javac/diags/examples/VerboseResolveMulti1.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/VerboseResolveMulti1.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,6 +25,7 @@
// key: compiler.note.verbose.resolve.multi.1
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.no.conforming.assignment.exists
+// key: compiler.misc.inconvertible.types
// options: -XDverboseResolution=inapplicable,failure
class VerboseResolveMulti1 {
--- a/langtools/test/tools/javac/diags/examples/WhereFreshTvar.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/WhereFreshTvar.java Mon Aug 27 10:58:40 2012 -0700
@@ -23,8 +23,7 @@
// key: compiler.misc.where.fresh.typevar
// key: compiler.misc.where.description.typevar
-// key: compiler.err.cant.apply.symbol.1
-// key: compiler.misc.no.args
+// key: compiler.err.prob.found.req
// key: compiler.misc.inferred.do.not.conform.to.upper.bounds
// options: -XDdiags=where,simpleNames
// run: simple
--- a/langtools/test/tools/javac/diags/examples/WhereIntersection.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/WhereIntersection.java Mon Aug 27 10:58:40 2012 -0700
@@ -25,6 +25,7 @@
// key: compiler.misc.where.description.intersection
// key: compiler.misc.intersection.type
// key: compiler.err.prob.found.req
+// key: compiler.misc.inconvertible.types
// options: -XDdiags=where
// run: simple
--- a/langtools/test/tools/javac/diags/examples/WhereTypeVar.java Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/diags/examples/WhereTypeVar.java Mon Aug 27 10:58:40 2012 -0700
@@ -26,6 +26,7 @@
// key: compiler.misc.type.var
// key: compiler.err.cant.apply.symbol.1
// key: compiler.misc.no.conforming.assignment.exists
+// key: compiler.misc.inconvertible.types
// options: -XDdiags=where,disambiguateTvars
// run: simple
--- a/langtools/test/tools/javac/generics/6207386/T6207386.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/6207386/T6207386.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6207386.java:13:30: compiler.err.prob.found.req: X, T6207386.F<? super X>
+T6207386.java:13:30: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: X, T6207386.F<? super X>)
1 error
--- a/langtools/test/tools/javac/generics/diamond/neg/Neg05.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/diamond/neg/Neg05.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,19 +1,19 @@
Neg05.java:19:48: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:19:35: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<java.lang.String>
+Neg05.java:19:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<java.lang.String>)
Neg05.java:20:58: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:20:45: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? extends java.lang.String>
+Neg05.java:20:45: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? extends java.lang.String>)
Neg05.java:21:43: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:21:30: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<?>
+Neg05.java:21:30: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<?>)
Neg05.java:22:56: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:22:43: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? super java.lang.String>
+Neg05.java:22:43: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? super java.lang.String>)
Neg05.java:24:48: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:24:35: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<java.lang.String>
+Neg05.java:24:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<java.lang.String>)
Neg05.java:25:58: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:25:45: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? extends java.lang.String>
+Neg05.java:25:45: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? extends java.lang.String>)
Neg05.java:26:43: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:26:30: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<?>
+Neg05.java:26:30: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<?>)
Neg05.java:27:56: compiler.err.improperly.formed.type.inner.raw.param
-Neg05.java:27:43: compiler.err.prob.found.req: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? super java.lang.String>
+Neg05.java:27:43: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg05.Foo<java.lang.String>, Neg05<?>.Foo<? super java.lang.String>)
Neg05.java:31:37: compiler.err.improperly.formed.type.inner.raw.param
Neg05.java:32:47: compiler.err.improperly.formed.type.inner.raw.param
Neg05.java:33:32: compiler.err.improperly.formed.type.inner.raw.param
--- a/langtools/test/tools/javac/generics/diamond/neg/Neg06.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/diamond/neg/Neg06.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-Neg06.java:16:37: compiler.err.prob.found.req.1: (compiler.misc.cant.apply.diamond.1: (compiler.misc.diamond: Neg06.CFoo), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.String, java.lang.Number))
+Neg06.java:16:37: compiler.err.prob.found.req: (compiler.misc.cant.apply.diamond.1: (compiler.misc.diamond: Neg06.CFoo), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.String, java.lang.Number))
1 error
--- a/langtools/test/tools/javac/generics/diamond/neg/Neg10.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/diamond/neg/Neg10.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-Neg10.java:16:22: compiler.err.prob.found.req: Neg10.Foo<java.lang.Integer>, Neg10.Foo<java.lang.Number>
+Neg10.java:16:22: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Neg10.Foo<java.lang.Integer>, Neg10.Foo<java.lang.Number>)
1 error
--- a/langtools/test/tools/javac/generics/inference/6315770/T6315770.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6315770/T6315770.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-T6315770.java:16:42: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.no.args, compiler.misc.no.args, kindname.class, T6315770<V>, (compiler.misc.no.unique.maximal.instance.exists: T, java.lang.String,java.lang.Integer,java.lang.Runnable)
-T6315770.java:17:40: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.no.args, compiler.misc.no.args, kindname.class, T6315770<V>, (compiler.misc.inferred.do.not.conform.to.lower.bounds: java.lang.Integer&java.lang.Runnable, java.lang.String)
+T6315770.java:16:42: compiler.err.prob.found.req: (compiler.misc.no.unique.maximal.instance.exists: T, java.lang.String,java.lang.Integer,java.lang.Runnable)
+T6315770.java:17:40: compiler.err.prob.found.req: (compiler.misc.inferred.do.not.conform.to.lower.bounds: java.lang.Integer&java.lang.Runnable, java.lang.String)
2 errors
--- a/langtools/test/tools/javac/generics/inference/6611449/T6611449.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6611449/T6611449.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
-T6611449.java:18:9: compiler.err.cant.apply.symbols: kindname.constructor, T6611449, int,{(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T,T), (compiler.misc.infer.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S))}
-T6611449.java:19:9: compiler.err.cant.apply.symbols: kindname.constructor, T6611449, int,int,{(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T,T), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S)),(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T), (compiler.misc.infer.arg.length.mismatch))}
+T6611449.java:18:9: compiler.err.cant.apply.symbols: kindname.constructor, T6611449, int,{(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T,T), (compiler.misc.infer.arg.length.mismatch: T)),(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S))}
+T6611449.java:19:9: compiler.err.cant.apply.symbols: kindname.constructor, T6611449, int,int,{(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T,T), (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S)),(compiler.misc.inapplicable.method: kindname.constructor, T6611449, <T>T6611449(T), (compiler.misc.infer.arg.length.mismatch: T))}
T6611449.java:20:9: compiler.err.cant.apply.symbol.1: kindname.method, m1, T, int, kindname.class, T6611449<S>, (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S)
T6611449.java:21:9: compiler.err.cant.apply.symbol.1: kindname.method, m2, T,T, int,int, kindname.class, T6611449<S>, (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, S)
4 errors
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6638712a.java:16:33: compiler.err.cant.apply.symbol.1: kindname.method, compound, java.lang.Iterable<? extends java.util.Comparator<? super T>>, java.util.List<java.util.Comparator<?>>, kindname.class, T6638712a, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<java.util.Comparator<?>>, java.lang.Iterable<? extends java.util.Comparator<? super T>>)
+T6638712a.java:16:33: compiler.err.cant.apply.symbol.1: kindname.method, compound, java.lang.Iterable<? extends java.util.Comparator<? super T>>, java.util.List<java.util.Comparator<?>>, kindname.class, T6638712a, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<java.util.Comparator<?>>, java.lang.Iterable<? extends java.util.Comparator<? super T>>))
1 error
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712b.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712b.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6638712b.java:14:21: compiler.err.cant.apply.symbol.1: kindname.method, m, I, T6638712b<java.lang.Integer>, kindname.class, T6638712b<X>, (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, java.lang.String,java.lang.Object)
+T6638712b.java:14:21: compiler.err.prob.found.req: (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Integer, java.lang.String,java.lang.Object)
1 error
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712c.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712c.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.no.conforming.assignment.exists: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>)
+T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>))
1 error
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712e.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712e.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6638712e.java:17:27: compiler.err.cant.apply.symbol.1: kindname.method, m, T6638712e.Foo<? super X,? extends A>, T6638712e.Foo<java.lang.Boolean,java.lang.Boolean>, kindname.class, T6638712e.Foo<A,B>, (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Object, java.lang.Boolean,java.lang.Object)
+T6638712e.java:17:27: compiler.err.prob.found.req: (compiler.misc.inferred.do.not.conform.to.upper.bounds: java.lang.Object, java.lang.Boolean,java.lang.Object)
1 error
--- a/langtools/test/tools/javac/generics/inference/6650759/T6650759m.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6650759/T6650759m.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6650759m.java:43:36: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? extends java.util.List<? super Z>>, java.util.ArrayList<java.util.ArrayList<java.lang.Integer>>, kindname.class, T6650759m, (compiler.misc.inferred.do.not.conform.to.lower.bounds: java.lang.Integer, java.lang.String)
+T6650759m.java:43:36: compiler.err.prob.found.req: (compiler.misc.inferred.do.not.conform.to.lower.bounds: java.lang.Integer, java.lang.String)
1 error
--- a/langtools/test/tools/javac/generics/inference/6838943/T6838943.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/6838943/T6838943.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6838943.java:13:14: compiler.err.cant.apply.symbol.1: kindname.method, m, T6838943.A<Z>,Z, T6838943.A<T6838943.B>,T6838943.B, kindname.class, T6838943.C<X>, (compiler.misc.infer.no.conforming.assignment.exists: Z, T6838943.A<T6838943.B>, T6838943.A<Z>)
+T6838943.java:13:14: compiler.err.cant.apply.symbol.1: kindname.method, m, T6838943.A<Z>,Z, T6838943.A<T6838943.B>,T6838943.B, kindname.class, T6838943.C<X>, (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.inconvertible.types: T6838943.A<T6838943.B>, T6838943.A<Z>))
1 error
--- a/langtools/test/tools/javac/generics/inference/7086586/T7086586.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/7086586/T7086586.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,5 +1,5 @@
-T7086586.java:14:20: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>)
-T7086586.java:15:20: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>)
-T7086586.java:16:23: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>)
-T7086586.java:17:9: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>)
+T7086586.java:14:20: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>))
+T7086586.java:15:20: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>))
+T7086586.java:16:23: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>))
+T7086586.java:17:9: compiler.err.cant.apply.symbol.1: kindname.method, m, java.util.List<? super T>, java.util.List<compiler.misc.type.captureof: 1, ?>, kindname.class, T7086586, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.List<compiler.misc.type.captureof: 1, ?>, java.util.List<? super T>))
4 errors
--- a/langtools/test/tools/javac/generics/inference/7154127/T7154127.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/inference/7154127/T7154127.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T7154127.java:19:49: compiler.err.cant.apply.symbol.1: kindname.method, m, compiler.misc.no.args, compiler.misc.no.args, kindname.class, T7154127, (compiler.misc.inferred.do.not.conform.to.upper.bounds: Y, T7154127.D,T7154127.B<U>)
+T7154127.java:19:49: compiler.err.prob.found.req: (compiler.misc.inferred.do.not.conform.to.upper.bounds: Y, T7154127.D,T7154127.B<U>)
1 error
--- a/langtools/test/tools/javac/generics/rawOverride/7062745/T7062745neg.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/rawOverride/7062745/T7062745neg.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T7062745neg.java:16:36: compiler.err.prob.found.req: java.lang.Object, java.lang.Number
+T7062745neg.java:16:36: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Number)
1 error
--- a/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-T6886247_2.java:35:28: compiler.err.prob.found.req: compiler.misc.type.captureof: 1, ?, E
+T6886247_2.java:35:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.captureof: 1, ?, E)
1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/EffectivelyFinalTest.java Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012, 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
+ * @summary Integrate efectively final check with DA/DU analysis
+ * @compile/fail/ref=EffectivelyFinalTest01.out -XDallowEffectivelyFinalInInnerClasses -XDrawDiagnostics EffectivelyFinalTest.java
+ * @compile/fail/ref=EffectivelyFinalTest02.out -source 7 -Xlint:-options -XDrawDiagnostics EffectivelyFinalTest.java
+ */
+class EffectivelyFinalTest {
+
+ void m1(int x) {
+ int y = 1;
+ new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
+ }
+
+ void m2(int x) {
+ int y;
+ y = 1;
+ new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
+ }
+
+ void m3(int x, boolean cond) {
+ int y;
+ if (cond) y = 1;
+ new Object() { { System.out.println(x+y); } }; //error - y not DA
+ }
+
+ void m4(int x, boolean cond) {
+ int y;
+ if (cond) y = 1;
+ else y = 2;
+ new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
+ }
+
+ void m5(int x, boolean cond) {
+ int y;
+ if (cond) y = 1;
+ y = 2;
+ new Object() { { System.out.println(x+y); } }; //error - y not EF
+ }
+
+ void m6(int x) {
+ new Object() { { System.out.println(x+1); } }; //error - x not EF
+ x++;
+ }
+
+ void m7(int x) {
+ new Object() { { System.out.println(x=1); } }; //error - x not EF
+ }
+
+ void m8() {
+ int y;
+ new Object() { { System.out.println(y=1); } }; //error - y not EF
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/EffectivelyFinalTest01.out Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,6 @@
+EffectivelyFinalTest.java:46:47: compiler.err.var.might.not.have.been.initialized: y
+EffectivelyFinalTest.java:60:47: compiler.err.cant.ref.non.effectively.final.var: y, (compiler.misc.inner.cls)
+EffectivelyFinalTest.java:64:45: compiler.err.cant.ref.non.effectively.final.var: x, (compiler.misc.inner.cls)
+EffectivelyFinalTest.java:69:45: compiler.err.cant.ref.non.effectively.final.var: x, (compiler.misc.inner.cls)
+EffectivelyFinalTest.java:74:45: compiler.err.cant.ref.non.effectively.final.var: y, (compiler.misc.inner.cls)
+5 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/EffectivelyFinalTest02.out Mon Aug 27 10:58:40 2012 -0700
@@ -0,0 +1,14 @@
+EffectivelyFinalTest.java:46:47: compiler.err.var.might.not.have.been.initialized: y
+EffectivelyFinalTest.java:34:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:34:47: compiler.err.local.var.accessed.from.icls.needs.final: y
+EffectivelyFinalTest.java:40:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:40:47: compiler.err.local.var.accessed.from.icls.needs.final: y
+EffectivelyFinalTest.java:46:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:53:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:53:47: compiler.err.local.var.accessed.from.icls.needs.final: y
+EffectivelyFinalTest.java:60:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:60:47: compiler.err.local.var.accessed.from.icls.needs.final: y
+EffectivelyFinalTest.java:64:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:69:45: compiler.err.local.var.accessed.from.icls.needs.final: x
+EffectivelyFinalTest.java:74:45: compiler.err.local.var.accessed.from.icls.needs.final: y
+13 errors
--- a/langtools/test/tools/javac/multicatch/Neg06.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/multicatch/Neg06.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,3 +1,3 @@
-Neg06.java:14:16: compiler.err.prob.found.req: java.lang.String, java.lang.Throwable
-Neg06.java:14:25: compiler.err.prob.found.req: java.lang.Integer, java.lang.Throwable
+Neg06.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Throwable)
+Neg06.java:14:25: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.Throwable)
2 errors
--- a/langtools/test/tools/javac/multicatch/Neg07.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/multicatch/Neg07.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-Neg07.java:14:56: compiler.err.prob.found.req: java.lang.Class<compiler.misc.type.captureof: 1, ? extends Neg07.ParentException>, java.lang.Class<? extends Neg07.HasFoo>
+Neg07.java:14:56: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Class<compiler.misc.type.captureof: 1, ? extends Neg07.ParentException>, java.lang.Class<? extends Neg07.HasFoo>)
1 error
--- a/langtools/test/tools/javac/types/CastObjectToPrimitiveTest.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/types/CastObjectToPrimitiveTest.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,2 +1,2 @@
-CastObjectToPrimitiveTest.java:36:23: compiler.err.prob.found.req.1: (compiler.misc.inconvertible.types: java.lang.Object, int)
+CastObjectToPrimitiveTest.java:36:23: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, int)
1 error
--- a/langtools/test/tools/javac/varargs/6313164/T6313164.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/varargs/6313164/T6313164.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,6 +1,6 @@
T6313164.java:12:8: compiler.err.cant.apply.symbol.1: kindname.method, foo1, p1.A[], p1.B,p1.B, kindname.class, p1.B, (compiler.misc.inaccessible.varargs.type: p1.A, kindname.class, T6313164)
-T6313164.java:14:13: compiler.err.cant.apply.symbol.1: kindname.method, foo3, X[], compiler.misc.type.null,compiler.misc.type.null, kindname.class, p1.B, (compiler.misc.inaccessible.varargs.type: p1.A, kindname.class, T6313164)
-T6313164.java:15:13: compiler.err.cant.apply.symbol.1: kindname.method, foo4, X[], compiler.misc.type.null,compiler.misc.type.null, kindname.class, p1.B, (compiler.misc.inaccessible.varargs.type: p1.A, kindname.class, T6313164)
+T6313164.java:14:13: compiler.err.prob.found.req: (compiler.misc.inaccessible.varargs.type: p1.A, kindname.class, T6313164)
+T6313164.java:15:13: compiler.err.prob.found.req: (compiler.misc.inaccessible.varargs.type: p1.A, kindname.class, T6313164)
- compiler.note.unchecked.filename: B.java
- compiler.note.unchecked.recompile
3 errors
--- a/langtools/test/tools/javac/varargs/7097436/T7097436.out Mon Aug 27 10:23:43 2012 +0800
+++ b/langtools/test/tools/javac/varargs/7097436/T7097436.out Mon Aug 27 10:58:40 2012 -0700
@@ -1,6 +1,6 @@
T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls
T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls
-T7097436.java:15:20: compiler.err.prob.found.req: java.util.List<java.lang.String>[], java.lang.String
-T7097436.java:16:26: compiler.err.prob.found.req: java.util.List<java.lang.String>[], java.lang.Integer[]
+T7097436.java:15:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List<java.lang.String>[], java.lang.String)
+T7097436.java:16:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List<java.lang.String>[], java.lang.Integer[])
2 errors
2 warnings