--- a/.hgtags-top-repo Thu Apr 16 11:23:02 2009 -0700
+++ b/.hgtags-top-repo Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
4264c2fe66493e57c411045a1b61377796641e45 jdk7-b52
c235f4a8559d196879c56af80159f67ee5d0e720 jdk7-b53
2ef382b1bbd58a68e668391c6145a4b2066c5b96 jdk7-b54
+aea0ace7a1e43619800931d42bbf69c579361c2d jdk7-b55
--- a/corba/.hgtags Thu Apr 16 11:23:02 2009 -0700
+++ b/corba/.hgtags Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
bec82237d694f9802b820fa11bbb4f7fa9bf8e77 jdk7-b52
3c4d73194f6f89f040ae3b2d257335dfa8a1b2b5 jdk7-b53
8130ac858d6789d5853d23044ba4a992afda574a jdk7-b54
+7a869f16ba83060c34b77620406cfa89d1cd7084 jdk7-b55
--- a/hotspot/.hgtags Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/.hgtags Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
1b1e8f1a4fe8cebc01c022484f78148e17b62a0d jdk7-b52
032c6af894dae8d939b3dd31d82042549e7793e0 jdk7-b53
fafab5d5349c7c066d677538db67a1ee0fb33bd2 jdk7-b54
+f8e839c086152da70d6ec5913ba6f9f509282e8d jdk7-b55
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Wed Jul 05 16:51:32 2017 +0200
@@ -142,34 +142,35 @@
// from jvm.h
public static final long JVM_RECOGNIZED_CLASS_MODIFIERS = (JVM_ACC_PUBLIC |
- JVM_ACC_FINAL |
- JVM_ACC_SUPER |
- JVM_ACC_INTERFACE |
- JVM_ACC_ABSTRACT |
- JVM_ACC_ANNOTATION |
- JVM_ACC_SYNTHETIC);
+ JVM_ACC_FINAL |
+ JVM_ACC_SUPER |
+ JVM_ACC_INTERFACE |
+ JVM_ACC_ABSTRACT |
+ JVM_ACC_ANNOTATION |
+ JVM_ACC_ENUM |
+ JVM_ACC_SYNTHETIC);
public static final long JVM_RECOGNIZED_FIELD_MODIFIERS = (JVM_ACC_PUBLIC |
- JVM_ACC_PRIVATE |
- JVM_ACC_PROTECTED |
- JVM_ACC_STATIC |
- JVM_ACC_FINAL |
- JVM_ACC_VOLATILE |
- JVM_ACC_TRANSIENT |
- JVM_ACC_ENUM |
- JVM_ACC_SYNTHETIC);
+ JVM_ACC_PRIVATE |
+ JVM_ACC_PROTECTED |
+ JVM_ACC_STATIC |
+ JVM_ACC_FINAL |
+ JVM_ACC_VOLATILE |
+ JVM_ACC_TRANSIENT |
+ JVM_ACC_ENUM |
+ JVM_ACC_SYNTHETIC);
public static final long JVM_RECOGNIZED_METHOD_MODIFIERS = (JVM_ACC_PUBLIC |
- JVM_ACC_PRIVATE |
- JVM_ACC_PROTECTED |
- JVM_ACC_STATIC |
- JVM_ACC_FINAL |
- JVM_ACC_SYNCHRONIZED |
- JVM_ACC_BRIDGE |
- JVM_ACC_VARARGS |
- JVM_ACC_NATIVE |
- JVM_ACC_ABSTRACT |
- JVM_ACC_STRICT |
- JVM_ACC_SYNTHETIC);
+ JVM_ACC_PRIVATE |
+ JVM_ACC_PROTECTED |
+ JVM_ACC_STATIC |
+ JVM_ACC_FINAL |
+ JVM_ACC_SYNCHRONIZED |
+ JVM_ACC_BRIDGE |
+ JVM_ACC_VARARGS |
+ JVM_ACC_NATIVE |
+ JVM_ACC_ABSTRACT |
+ JVM_ACC_STRICT |
+ JVM_ACC_SYNTHETIC);
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Wed Jul 05 16:51:32 2017 +0200
@@ -48,6 +48,8 @@
private static AddressField lastJavaPCField;
private static CIntegerField threadStateField;
private static AddressField osThreadField;
+ private static AddressField stackBaseField;
+ private static CIntegerField stackSizeField;
private static JavaThreadPDAccess access;
@@ -83,6 +85,8 @@
lastJavaPCField = anchorType.getAddressField("_last_Java_pc");
threadStateField = type.getCIntegerField("_thread_state");
osThreadField = type.getAddressField("_osthread");
+ stackBaseField = type.getAddressField("_stack_base");
+ stackSizeField = type.getCIntegerField("_stack_size");
UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue();
NEW = db.lookupIntConstant("_thread_new").intValue();
@@ -312,6 +316,14 @@
return (OSThread) VMObjectFactory.newObject(OSThread.class, osThreadField.getValue(addr));
}
+ public Address getStackBase() {
+ return stackBaseField.getValue();
+ }
+
+ public long getStackSize() {
+ return stackSizeField.getValue();
+ }
+
/** Gets the Java-side thread object for this JavaThread */
public Oop getThreadObj() {
return VM.getVM().getObjectHeap().newOop(threadObjField.getValue(addr));
@@ -345,11 +357,18 @@
if (Assert.ASSERTS_ENABLED) {
Assert.that(VM.getVM().isDebugging(), "Not yet implemented for non-debugging system");
}
- Address highest = highestLock();
Address sp = lastSPDbg();
+ Address stackBase = getStackBase();
// Be robust
- if ((highest == null) || (sp == null)) return false;
- return (highest.greaterThanOrEqual(a) && sp.lessThanOrEqual(a));
+ if (sp == null) return false;
+ return stackBase.greaterThanOrEqual(a) && sp.lessThanOrEqual(a);
+ }
+
+ public boolean isLockOwned(Address a) {
+ Address stackBase = getStackBase();
+ Address stackLimit = stackBase.addOffsetTo(-getStackSize());
+
+ return stackBase.greaterThanOrEqual(a) && stackLimit.lessThanOrEqual(a);
// FIXME: should traverse MonitorArray/MonitorChunks as in VM
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java Wed Jul 05 16:51:32 2017 +0200
@@ -38,7 +38,6 @@
private static int HAS_ASYNC_EXCEPTION;
private static AddressField activeHandlesField;
- private static AddressField highestLockField;
private static AddressField currentPendingMonitorField;
private static AddressField currentWaitingMonitorField;
@@ -60,7 +59,6 @@
tlabFieldOffset = type.getField("_tlab").getOffset();
activeHandlesField = type.getAddressField("_active_handles");
- highestLockField = type.getAddressField("_highest_lock");
currentPendingMonitorField = type.getAddressField("_current_pending_monitor");
currentWaitingMonitorField = type.getAddressField("_current_waiting_monitor");
}
@@ -121,10 +119,6 @@
// pending exception
}
- public Address highestLock() {
- return highestLockField.getValue(addr);
- }
-
public ObjectMonitor getCurrentPendingMonitor() {
Address monitorAddr = currentPendingMonitorField.getValue(addr);
if (monitorAddr == null) {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java Wed Jul 05 16:51:32 2017 +0200
@@ -164,20 +164,11 @@
}
}
- long leastDiff = 0;
- boolean leastDiffInitialized = false;
- JavaThread theOwner = null;
for (JavaThread thread = first(); thread != null; thread = thread.next()) {
- Address addr = thread.highestLock();
- if (addr == null || addr.lessThan(o)) continue;
- long diff = addr.minus(o);
- if (!leastDiffInitialized || diff < leastDiff) {
- leastDiffInitialized = true;
- leastDiff = diff;
- theOwner = thread;
- }
+ if (thread.isLockOwned(o))
+ return thread;
}
- return theOwner;
+ return null;
}
public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ByteCodeRewriter.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ByteCodeRewriter.java Wed Jul 05 16:51:32 2017 +0200
@@ -89,29 +89,6 @@
// update the code buffer hotspot specific bytecode with the jvm bytecode
code[bci] = (byte) (0xFF & bytecode);
- // RewriteFrequentPairs
- if(hotspotcode == Bytecodes._fast_iaccess_0 ||
- hotspotcode == Bytecodes._fast_aaccess_0 ||
- hotspotcode == Bytecodes._fast_faccess_0) {
- // rewrite next bytecode as _getfield
- bci++;
- code[bci] = (byte) (0xFF & Bytecodes._getfield);
- bytecode = Bytecodes._getfield;
- hotspotcode = Bytecodes._getfield;
- } else if (hotspotcode == Bytecodes._fast_iload2) {
- // rewrite next bytecode as _iload
- bci++;
- code[bci] = (byte) (0xFF & Bytecodes._iload);
- bytecode = Bytecodes._iload;
- hotspotcode = Bytecodes._iload;
- } else if (hotspotcode == Bytecodes._fast_icaload) {
- // rewrite next bytecode as _caload
- bci++;
- code[bci] = (byte) (0xFF & Bytecodes._caload);
- bytecode = Bytecodes._caload;
- bytecode = Bytecodes._caload;
- }
-
short cpoolIndex = 0;
switch (bytecode) {
// bytecodes with ConstantPoolCache index
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassDump.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassDump.java Wed Jul 05 16:51:32 2017 +0200
@@ -59,8 +59,14 @@
SystemDictionary dict = VM.getVM().getSystemDictionary();
dict.classesDo(new SystemDictionary.ClassVisitor() {
public void visit(Klass k) {
- if (k instanceof InstanceKlass)
- dumpKlass((InstanceKlass) k);
+ if (k instanceof InstanceKlass) {
+ try {
+ dumpKlass((InstanceKlass) k);
+ } catch (Exception e) {
+ System.out.println(k.getName().asString());
+ e.printStackTrace();
+ }
+ }
}
});
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Wed Jul 05 16:51:32 2017 +0200
@@ -40,7 +40,6 @@
protected InstanceKlass klass;
protected DataOutputStream dos;
protected ConstantPool cpool;
- protected boolean is15Format;
// Map between class name to index of type CONSTANT_Class
protected Map classToIndex = new HashMap();
@@ -73,7 +72,6 @@
klass = kls;
dos = new DataOutputStream(os);
cpool = klass.getConstants();
- is15Format = is15ClassFile();
}
public void write() throws IOException {
@@ -82,7 +80,7 @@
// write magic
dos.writeInt(0xCAFEBABE);
- writeVersion(is15Format);
+ writeVersion();
writeConstantPool();
writeClassAccessFlags();
writeThisClass();
@@ -96,43 +94,14 @@
dos.flush();
}
- protected boolean is15ClassFile() {
- // if klass has generic signature, then it is 1.5 class file.
- if (klass.getGenericSignature() != null) {
- return true;
- }
-
- // if atleast one method has generic signature
- // , then we have 1.5 class file.
- ObjArray methods = klass.getMethods();
- final int numMethods = (int) methods.getLength();
- for (int m = 0; m < numMethods; m++) {
- Method curMethod = (Method) methods.getObjAt(m);
- if (curMethod.getGenericSignature() != null) {
- return true;
- }
- }
-
- // if atleast one field has non-zero generic signature index, then we have
- // 1.5 class file
- TypeArray fields = klass.getFields();
- final int numFields = (int) fields.getLength();
- for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) {
- short genSigIndex = fields.getShortAt(f + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET);
- if (genSigIndex != (short)0) return true;
- }
-
- return false;
+ protected void writeVersion() throws IOException {
+ dos.writeShort((short)klass.minorVersion());
+ dos.writeShort((short)klass.majorVersion());
}
- protected void writeVersion(boolean is15Format) throws IOException {
- if (is15Format) {
- dos.writeShort(MINOR_VERSION);
- dos.writeShort(MAJOR_VERSION);
- } else {
- dos.writeShort(MINOR_VERSION_OLD);
- dos.writeShort(MAJOR_VERSION_OLD);
- }
+ protected void writeIndex(int index) throws IOException {
+ if (index == 0) throw new InternalError();
+ dos.writeShort(index);
}
protected void writeConstantPool() throws IOException {
@@ -392,8 +361,8 @@
if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex);
short fieldAttributeCount = 0;
- boolean isSyn = isSynthetic(accessFlags);
- if (isSyn)
+ boolean hasSyn = hasSyntheticAttribute(accessFlags);
+ if (hasSyn)
fieldAttributeCount++;
short initvalIndex = fields.getShortAt(index + InstanceKlass.INITVAL_INDEX_OFFSET);
@@ -407,18 +376,18 @@
dos.writeShort(fieldAttributeCount);
// write synthetic, if applicable
- if (isSyn)
+ if (hasSyn)
writeSynthetic();
if (initvalIndex != 0) {
- dos.writeShort(_constantValueIndex);
+ writeIndex(_constantValueIndex);
dos.writeInt(2);
dos.writeShort(initvalIndex);
if (DEBUG) debugMessage("\tfield init value = " + initvalIndex);
}
if (genSigIndex != 0) {
- dos.writeShort(_signatureIndex);
+ writeIndex(_signatureIndex);
dos.writeInt(2);
dos.writeShort(genSigIndex);
if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex);
@@ -430,8 +399,13 @@
return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0;
}
+ protected boolean hasSyntheticAttribute(short accessFlags) {
+ // Check if flags have the attribute and if the constant pool contains an entry for it.
+ return isSynthetic(accessFlags) && _syntheticIndex != 0;
+ }
+
protected void writeSynthetic() throws IOException {
- dos.writeShort(_syntheticIndex);
+ writeIndex(_syntheticIndex);
dos.writeInt(0);
}
@@ -459,8 +433,8 @@
short methodAttributeCount = 0;
- final boolean isSyn = isSynthetic((short)accessFlags);
- if (isSyn)
+ final boolean hasSyn = hasSyntheticAttribute((short)accessFlags);
+ if (hasSyn)
methodAttributeCount++;
final boolean hasCheckedExceptions = m.hasCheckedExceptions();
@@ -478,27 +452,11 @@
dos.writeShort(methodAttributeCount);
if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount);
- if (isSyn) {
+ if (hasSyn) {
if (DEBUG) debugMessage("\tmethod is synthetic");
writeSynthetic();
}
- if (hasCheckedExceptions) {
- CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
- dos.writeShort(_exceptionsIndex);
-
- int attrSize = 2 /* number_of_exceptions */ +
- exceptions.length * 2 /* exception_index */;
- dos.writeInt(attrSize);
- dos.writeShort(exceptions.length);
- if (DEBUG) debugMessage("\tmethod has " + exceptions.length
- + " checked exception(s)");
- for (int e = 0; e < exceptions.length; e++) {
- short cpIndex = (short) exceptions[e].getClassCPIndex();
- dos.writeShort(cpIndex);
- }
- }
-
if (isCodeAvailable) {
byte[] code = m.getByteCode();
short codeAttrCount = 0;
@@ -574,7 +532,7 @@
// start writing Code
- dos.writeShort(_codeIndex);
+ writeIndex(_codeIndex);
dos.writeInt(codeSize);
if (DEBUG) debugMessage("\tcode attribute length = " + codeSize);
@@ -608,7 +566,7 @@
// write LineNumberTable, if available.
if (hasLineNumberTable) {
- dos.writeShort(_lineNumberTableIndex);
+ writeIndex(_lineNumberTableIndex);
dos.writeInt(lineNumberAttrLen);
dos.writeShort((short) lineNumberTable.length);
for (int l = 0; l < lineNumberTable.length; l++) {
@@ -619,7 +577,7 @@
// write LocalVariableTable, if available.
if (hasLocalVariableTable) {
- dos.writeShort((short) _localVariableTableIndex);
+ writeIndex((short) _localVariableTableIndex);
dos.writeInt(localVarAttrLen);
dos.writeShort((short) localVariableTable.length);
for (int l = 0; l < localVariableTable.length; l++) {
@@ -632,6 +590,22 @@
}
}
+ if (hasCheckedExceptions) {
+ CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
+ writeIndex(_exceptionsIndex);
+
+ int attrSize = 2 /* number_of_exceptions */ +
+ exceptions.length * 2 /* exception_index */;
+ dos.writeInt(attrSize);
+ dos.writeShort(exceptions.length);
+ if (DEBUG) debugMessage("\tmethod has " + exceptions.length
+ + " checked exception(s)");
+ for (int e = 0; e < exceptions.length; e++) {
+ short cpIndex = (short) exceptions[e].getClassCPIndex();
+ dos.writeShort(cpIndex);
+ }
+ }
+
if (isGeneric) {
writeGenericSignature(m.getGenericSignature().asString());
}
@@ -643,7 +617,7 @@
}
protected void writeGenericSignature(String signature) throws IOException {
- dos.writeShort(_signatureIndex);
+ writeIndex(_signatureIndex);
if (DEBUG) debugMessage("signature attribute = " + _signatureIndex);
dos.writeInt(2);
Short index = (Short) utf8ToIndex.get(signature);
@@ -653,12 +627,12 @@
protected void writeClassAttributes() throws IOException {
final long flags = klass.getAccessFlags();
- final boolean isSyn = isSynthetic((short) flags);
+ final boolean hasSyn = hasSyntheticAttribute((short) flags);
// check for source file
short classAttributeCount = 0;
- if (isSyn)
+ if (hasSyn)
classAttributeCount++;
Symbol sourceFileName = klass.getSourceFileName();
@@ -677,12 +651,12 @@
dos.writeShort(classAttributeCount);
if (DEBUG) debugMessage("class attribute count = " + classAttributeCount);
- if (isSyn)
+ if (hasSyn)
writeSynthetic();
// write SourceFile, if any
if (sourceFileName != null) {
- dos.writeShort(_sourceFileIndex);
+ writeIndex(_sourceFileIndex);
if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex);
dos.writeInt(2);
Short index = (Short) utf8ToIndex.get(sourceFileName.asString());
@@ -697,7 +671,7 @@
// write inner classes, if any
if (numInnerClasses != 0) {
- dos.writeShort(_innerClassesIndex);
+ writeIndex(_innerClassesIndex);
final int innerAttrLen = 2 /* number_of_inner_classes */ +
numInnerClasses * (
2 /* inner_class_info_index */ +
--- a/hotspot/make/hotspot_version Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/make/hotspot_version Wed Jul 05 16:51:32 2017 +0200
@@ -33,9 +33,9 @@
# Don't put quotes (fail windows build).
HOTSPOT_VM_COPYRIGHT=Copyright 2009
-HS_MAJOR_VER=15
+HS_MAJOR_VER=16
HS_MINOR_VER=0
-HS_BUILD_NUMBER=05
+HS_BUILD_NUMBER=01
JDK_MAJOR_VER=1
JDK_MINOR_VER=7
--- a/hotspot/make/jprt.config Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/make/jprt.config Wed Jul 05 16:51:32 2017 +0200
@@ -73,6 +73,7 @@
else
if [ "${JPRT_JOB_PRODUCT_RELEASE}" = "jdk6" -o \
"${JPRT_JOB_PRODUCT_RELEASE}" = "jdk6u10" -o \
+ "${JPRT_JOB_PRODUCT_RELEASE}" = "jdk6u14" -o \
"${JPRT_JOB_PRODUCT_RELEASE}" = "jdk6perf" ] ; then
# All jdk6 builds use SS11
compiler_name=SS11
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -3029,6 +3029,58 @@
+void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
+ Register temp_reg,
+ Label& wrong_method_type) {
+ assert_different_registers(mtype_reg, mh_reg, temp_reg);
+ // compare method type against that of the receiver
+ RegisterOrConstant mhtype_offset = delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg);
+ ld_ptr(mh_reg, mhtype_offset, temp_reg);
+ cmp(temp_reg, mtype_reg);
+ br(Assembler::notEqual, false, Assembler::pn, wrong_method_type);
+ delayed()->nop();
+}
+
+
+void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg) {
+ assert(mh_reg == G3_method_handle, "caller must put MH object in G3");
+ assert_different_registers(mh_reg, temp_reg);
+
+ // pick out the interpreted side of the handler
+ ld_ptr(mh_reg, delayed_value(java_dyn_MethodHandle::vmentry_offset_in_bytes, temp_reg), temp_reg);
+
+ // off we go...
+ ld_ptr(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes(), temp_reg);
+ jmp(temp_reg, 0);
+
+ // for the various stubs which take control at this point,
+ // see MethodHandles::generate_method_handle_stub
+
+ // (Can any caller use this delay slot? If so, add an option for supression.)
+ delayed()->nop();
+}
+
+RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot,
+ int extra_slot_offset) {
+ // cf. TemplateTable::prepare_invoke(), if (load_receiver).
+ int stackElementSize = Interpreter::stackElementWords() * wordSize;
+ int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0);
+ int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1);
+ assert(offset1 - offset == stackElementSize, "correct arithmetic");
+ if (arg_slot.is_constant()) {
+ offset += arg_slot.as_constant() * stackElementSize;
+ return offset;
+ } else {
+ Register temp = arg_slot.as_register();
+ sll_ptr(temp, exact_log2(stackElementSize), temp);
+ if (offset != 0)
+ add(temp, offset, temp);
+ return temp;
+ }
+}
+
+
+
void MacroAssembler::biased_locking_enter(Register obj_reg, Register mark_reg,
Register temp_reg,
Label& done, Label* slow_case,
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -84,6 +84,10 @@
REGISTER_DECLARATION(Register, Gtemp , G5);
+// JSR 292 fixed register usages:
+REGISTER_DECLARATION(Register, G5_method_type , G5);
+REGISTER_DECLARATION(Register, G3_method_handle , G3);
+
// The compiler requires that G5_megamorphic_method is G5_inline_cache_klass,
// because a single patchable "set" instruction (NativeMovConstReg,
// or NativeMovConstPatching for compiler1) instruction
@@ -91,9 +95,13 @@
// call site is an inline cache or is megamorphic. See the function
// CompiledIC::set_to_megamorphic.
//
-// On the other hand, G5_inline_cache_klass must differ from G5_method,
-// because both registers are needed for an inline cache that calls
-// an interpreted method.
+// If a inline cache targets an interpreted method, then the
+// G5 register will be used twice during the call. First,
+// the call site will be patched to load a compiledICHolder
+// into G5. (This is an ordered pair of ic_klass, method.)
+// The c2i adapter will first check the ic_klass, then load
+// G5_method with the method part of the pair just before
+// jumping into the interpreter.
//
// Note that G5_method is only the method-self for the interpreter,
// and is logically unrelated to G5_megamorphic_method.
@@ -1931,6 +1939,7 @@
inline void store_ptr_contents( Register s, Address& a, int offset = 0 );
inline void jumpl_to( Address& a, Register d, int offset = 0 );
inline void jump_to( Address& a, int offset = 0 );
+ inline void jump_indirect_to( Address& a, Register temp, int ld_offset = 0, int jmp_offset = 0 );
// ring buffer traceable jumps
@@ -2366,6 +2375,16 @@
Register temp2_reg,
Label& L_success);
+ // method handles (JSR 292)
+ void check_method_handle_type(Register mtype_reg, Register mh_reg,
+ Register temp_reg,
+ Label& wrong_method_type);
+ void jump_to_method_handle_entry(Register mh_reg, Register temp_reg);
+ // offset relative to Gargs of argument at tos[arg_slot].
+ // (arg_slot == 0 means the last argument, not the first).
+ RegisterOrConstant argument_offset(RegisterOrConstant arg_slot,
+ int extra_slot_offset = 0);
+
// Stack overflow checking
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.inline.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.inline.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -671,6 +671,15 @@
}
+inline void MacroAssembler::jump_indirect_to( Address& a, Register temp,
+ int ld_offset, int jmp_offset ) {
+ assert_not_delayed();
+ //sethi(a); // sethi is caller responsibility for this one
+ ld_ptr(a, temp, ld_offset);
+ jmp(temp, jmp_offset);
+}
+
+
inline void MacroAssembler::set_oop( jobject obj, Register d ) {
set_oop(allocate_oop_address(obj, d));
}
--- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1017,6 +1017,7 @@
const int slop_factor = 2*wordSize;
const int fixed_size = ((sizeof(BytecodeInterpreter) + slop_factor) >> LogBytesPerWord) + // what is the slop factor?
+ //6815692//methodOopDesc::extra_stack_words() + // extra push slots for MH adapters
frame::memory_parameter_word_sp_offset + // register save area + param window
(native ? frame::interpreter_frame_extra_outgoing_argument_words : 0); // JNI, class
@@ -1163,6 +1164,9 @@
__ st_ptr(O2, XXX_STATE(_stack)); // PREPUSH
__ lduh(max_stack, O3); // Full size expression stack
+ guarantee(!EnableMethodHandles, "no support yet for java.dyn.MethodHandle"); //6815692
+ //6815692//if (EnableMethodHandles)
+ //6815692// __ inc(O3, methodOopDesc::extra_stack_entries());
__ sll(O3, LogBytesPerWord, O3);
__ sub(O2, O3, O3);
// __ sub(O3, wordSize, O3); // so prepush doesn't look out of bounds
@@ -2017,7 +2021,9 @@
const int fixed_size = sizeof(BytecodeInterpreter)/wordSize + // interpreter state object
frame::memory_parameter_word_sp_offset; // register save area + param window
+ const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
return (round_to(max_stack +
+ extra_stack +
slop_factor +
fixed_size +
monitor_size +
@@ -2104,7 +2110,8 @@
// Need +1 here because stack_base points to the word just above the first expr stack entry
// and stack_limit is supposed to point to the word just below the last expr stack entry.
// See generate_compute_interpreter_state.
- to_fill->_stack_limit = stack_base - (method->max_stack() + 1);
+ int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
+ to_fill->_stack_limit = stack_base - (method->max_stack() + 1 + extra_stack);
to_fill->_monitor_base = (BasicObjectLock*) monitor_base;
// sparc specific
--- a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
address generate_normal_entry(bool synchronized);
address generate_native_entry(bool synchronized);
address generate_abstract_entry(void);
+ address generate_method_handle_entry(void);
address generate_math_entry(AbstractInterpreter::MethodKind kind);
address generate_empty_entry(void);
address generate_accessor_entry(void);
--- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -235,6 +235,19 @@
}
+
+// Method handle invoker
+// Dispatch a method of the form java.dyn.MethodHandles::invoke(...)
+address InterpreterGenerator::generate_method_handle_entry(void) {
+ if (!EnableMethodHandles) {
+ return generate_abstract_entry();
+ }
+ return generate_abstract_entry(); //6815692//
+}
+
+
+
+
//----------------------------------------------------------------------------------------------------
// Entry points & stack frame layout
//
@@ -364,6 +377,7 @@
case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
+ case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break;
case Interpreter::java_lang_math_sin : break;
case Interpreter::java_lang_math_cos : break;
case Interpreter::java_lang_math_tan : break;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+#include "incls/_precompiled.incl"
+#include "incls/_methodHandles_sparc.cpp.incl"
+
+#define __ _masm->
+
+address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm,
+ address interpreted_entry) {
+ __ align(wordSize);
+ address target = __ pc() + sizeof(Data);
+ while (__ pc() < target) {
+ __ nop();
+ __ align(wordSize);
+ }
+
+ MethodHandleEntry* me = (MethodHandleEntry*) __ pc();
+ me->set_end_address(__ pc()); // set a temporary end_address
+ me->set_from_interpreted_entry(interpreted_entry);
+ me->set_type_checking_entry(NULL);
+
+ return (address) me;
+}
+
+MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm,
+ address start_addr) {
+ MethodHandleEntry* me = (MethodHandleEntry*) start_addr;
+ assert(me->end_address() == start_addr, "valid ME");
+
+ // Fill in the real end_address:
+ __ align(wordSize);
+ me->set_end_address(__ pc());
+
+ return me;
+}
+
+
+// Code generation
+address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
+ ShouldNotReachHere(); //NYI, 6815692
+ return NULL;
+}
+
+// Generate an "entry" field for a method handle.
+// This determines how the method handle will respond to calls.
+void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) {
+ ShouldNotReachHere(); //NYI, 6815692
+}
--- a/hotspot/src/cpu/sparc/vm/register_definitions_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/register_definitions_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -142,6 +142,8 @@
REGISTER_DEFINITION(Register, G3_scratch);
REGISTER_DEFINITION(Register, G4_scratch);
REGISTER_DEFINITION(Register, Gtemp);
+REGISTER_DEFINITION(Register, G5_method_type);
+REGISTER_DEFINITION(Register, G3_method_handle);
REGISTER_DEFINITION(Register, Lentry_args);
#ifdef CC_INTERP
--- a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -937,12 +937,12 @@
// Inputs:
// G2_thread - TLS
// G5_method - Method oop
- // O0 - Flag telling us to restore SP from O5
- // O4_args - Pointer to interpreter's args
- // O5 - Caller's saved SP, to be restored if needed
+ // G4 (Gargs) - Pointer to interpreter's args
+ // O0..O4 - free for scratch
+ // O5_savedSP - Caller's saved SP, to be restored if needed
// O6 - Current SP!
// O7 - Valid return address
- // L0-L7, I0-I7 - Caller's temps (no frame pushed yet)
+ // L0-L7, I0-I7 - Caller's temps (no frame pushed yet)
// Outputs:
// G2_thread - TLS
@@ -954,7 +954,7 @@
// F0-F7 - more outgoing args
- // O4 is about to get loaded up with compiled callee's args
+ // Gargs is the incoming argument base, and also an outgoing argument.
__ sub(Gargs, BytesPerWord, Gargs);
#ifdef ASSERT
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -108,6 +108,24 @@
}
+// Arguments are: required type in G5_method_type, and
+// failing object (or NULL) in G3_method_handle.
+address TemplateInterpreterGenerator::generate_WrongMethodType_handler() {
+ address entry = __ pc();
+ // expression stack must be empty before entering the VM if an exception
+ // happened
+ __ empty_expression_stack();
+ // load exception object
+ __ call_VM(Oexception,
+ CAST_FROM_FN_PTR(address,
+ InterpreterRuntime::throw_WrongMethodTypeException),
+ G5_method_type, // required
+ G3_method_handle); // actual
+ __ should_not_reach_here();
+ return entry;
+}
+
+
address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) {
address entry = __ pc();
// expression stack must be empty before entering the VM if an exception happened
@@ -448,6 +466,7 @@
const int extra_space =
rounded_vm_local_words + // frame local scratch space
+ //6815692//methodOopDesc::extra_stack_words() + // extra push slots for MH adapters
frame::memory_parameter_word_sp_offset + // register save area
(native_call ? frame::interpreter_frame_extra_outgoing_argument_words : 0);
@@ -1447,6 +1466,7 @@
round_to(callee_extra_locals * Interpreter::stackElementWords(), WordsPerLong);
const int max_stack_words = max_stack * Interpreter::stackElementWords();
return (round_to((max_stack_words
+ //6815692//+ methodOopDesc::extra_stack_words()
+ rounded_vm_local_words
+ frame::memory_parameter_word_sp_offset), WordsPerLong)
// already rounded
--- a/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/sparc/vm/vtableStubs_sparc.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -114,6 +114,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 2*BytesPerInstWord; // 32-bit offset is this much larger than a 13-bit one
+ assert(vtable_index > 10 || __ pc() + slop <= s->code_end(), "room for sethi;add");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -208,6 +211,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 2*BytesPerInstWord; // 32-bit offset is this much larger than a 13-bit one
+ assert(itable_index > 10 || __ pc() + slop <= s->code_end(), "room for sethi;add");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -233,6 +239,50 @@
return (basic + slop);
}
}
+
+ // In order to tune these parameters, run the JVM with VM options
+ // +PrintMiscellaneous and +WizardMode to see information about
+ // actual itable stubs. Look for lines like this:
+ // itable #1 at 0x5551212[116] left over: 8
+ // Reduce the constants so that the "left over" number is 8
+ // Do not aim at a left-over number of zero, because a very
+ // large vtable or itable offset (> 4K) will require an extra
+ // sethi/or pair of instructions.
+ //
+ // The JVM98 app. _202_jess has a megamorphic interface call.
+ // The itable code looks like this:
+ // Decoding VtableStub itbl[1]@16
+ // ld [ %o0 + 4 ], %g3
+ // save %sp, -64, %sp
+ // ld [ %g3 + 0xe8 ], %l2
+ // sll %l2, 2, %l2
+ // add %l2, 0x134, %l2
+ // and %l2, -8, %l2 ! NOT_LP64 only
+ // add %g3, %l2, %l2
+ // add %g3, 4, %g3
+ // ld [ %l2 ], %l5
+ // brz,pn %l5, throw_icce
+ // cmp %l5, %g5
+ // be %icc, success
+ // add %l2, 8, %l2
+ // loop:
+ // ld [ %l2 ], %l5
+ // brz,pn %l5, throw_icce
+ // cmp %l5, %g5
+ // bne,pn %icc, loop
+ // add %l2, 8, %l2
+ // success:
+ // ld [ %l2 + -4 ], %l2
+ // ld [ %g3 + %l2 ], %l5
+ // restore %l5, 0, %g5
+ // ld [ %g5 + 0x44 ], %g3
+ // jmp %g3
+ // nop
+ // throw_icce:
+ // sethi %hi(throw_ICCE_entry), %g3
+ // ! 5 more instructions here, LP64_ONLY
+ // jmp %g3 + %lo(throw_ICCE_entry)
+ // restore
}
--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -7609,6 +7609,83 @@
}
+// registers on entry:
+// - rax ('check' register): required MethodType
+// - rcx: method handle
+// - rdx, rsi, or ?: killable temp
+void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
+ Register temp_reg,
+ Label& wrong_method_type) {
+ if (UseCompressedOops) unimplemented(); // field accesses must decode
+ // compare method type against that of the receiver
+ cmpptr(mtype_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg)));
+ jcc(Assembler::notEqual, wrong_method_type);
+}
+
+
+// A method handle has a "vmslots" field which gives the size of its
+// argument list in JVM stack slots. This field is either located directly
+// in every method handle, or else is indirectly accessed through the
+// method handle's MethodType. This macro hides the distinction.
+void MacroAssembler::load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
+ Register temp_reg) {
+ if (UseCompressedOops) unimplemented(); // field accesses must decode
+ // load mh.type.form.vmslots
+ if (java_dyn_MethodHandle::vmslots_offset_in_bytes() != 0) {
+ // hoist vmslots into every mh to avoid dependent load chain
+ movl(vmslots_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::vmslots_offset_in_bytes, temp_reg)));
+ } else {
+ Register temp2_reg = vmslots_reg;
+ movptr(temp2_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg)));
+ movptr(temp2_reg, Address(temp2_reg, delayed_value(java_dyn_MethodType::form_offset_in_bytes, temp_reg)));
+ movl(vmslots_reg, Address(temp2_reg, delayed_value(java_dyn_MethodTypeForm::vmslots_offset_in_bytes, temp_reg)));
+ }
+}
+
+
+// registers on entry:
+// - rcx: method handle
+// - rdx: killable temp (interpreted only)
+// - rax: killable temp (compiled only)
+void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg) {
+ assert(mh_reg == rcx, "caller must put MH object in rcx");
+ assert_different_registers(mh_reg, temp_reg);
+
+ if (UseCompressedOops) unimplemented(); // field accesses must decode
+
+ // pick out the interpreted side of the handler
+ movptr(temp_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::vmentry_offset_in_bytes, temp_reg)));
+
+ // off we go...
+ jmp(Address(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes()));
+
+ // for the various stubs which take control at this point,
+ // see MethodHandles::generate_method_handle_stub
+}
+
+
+Address MacroAssembler::argument_address(RegisterOrConstant arg_slot,
+ int extra_slot_offset) {
+ // cf. TemplateTable::prepare_invoke(), if (load_receiver).
+ int stackElementSize = Interpreter::stackElementSize();
+ int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0);
+#ifdef ASSERT
+ int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1);
+ assert(offset1 - offset == stackElementSize, "correct arithmetic");
+#endif
+ Register scale_reg = noreg;
+ Address::ScaleFactor scale_factor = Address::no_scale;
+ if (arg_slot.is_constant()) {
+ offset += arg_slot.as_constant() * stackElementSize;
+ } else {
+ scale_reg = arg_slot.as_register();
+ scale_factor = Address::times(stackElementSize);
+ }
+ offset += wordSize; // return PC is on stack
+ return Address(rsp, scale_reg, scale_factor, offset);
+}
+
+
void MacroAssembler::verify_oop_addr(Address addr, const char* s) {
if (!VerifyOops) return;
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1857,6 +1857,16 @@
Register temp_reg,
Label& L_success);
+ // method handles (JSR 292)
+ void check_method_handle_type(Register mtype_reg, Register mh_reg,
+ Register temp_reg,
+ Label& wrong_method_type);
+ void load_method_handle_vmslots(Register vmslots_reg, Register mh_reg,
+ Register temp_reg);
+ void jump_to_method_handle_entry(Register mh_reg, Register temp_reg);
+ Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0);
+
+
//----
void set_word_if_not_zero(Register reg); // sets reg to 1 if not zero, otherwise 0
--- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -513,10 +513,11 @@
// compute full expression stack limit
const Address size_of_stack (rbx, methodOopDesc::max_stack_offset());
+ const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_words();
__ load_unsigned_short(rdx, size_of_stack); // get size of expression stack in words
__ negptr(rdx); // so we can subtract in next step
// Allocate expression stack
- __ lea(rsp, Address(rsp, rdx, Address::times_ptr));
+ __ lea(rsp, Address(rsp, rdx, Address::times_ptr, -extra_stack));
__ movptr(STATE(_stack_limit), rsp);
}
@@ -659,8 +660,9 @@
// Always give one monitor to allow us to start interp if sync method.
// Any additional monitors need a check when moving the expression stack
const int one_monitor = frame::interpreter_frame_monitor_size() * wordSize;
+ const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
__ load_unsigned_short(rax, size_of_stack); // get size of expression stack in words
- __ lea(rax, Address(noreg, rax, Interpreter::stackElementScale(), one_monitor));
+ __ lea(rax, Address(noreg, rax, Interpreter::stackElementScale(), extra_stack + one_monitor));
__ lea(rax, Address(rax, rdx, Interpreter::stackElementScale(), overhead_size));
#ifdef ASSERT
@@ -2185,6 +2187,7 @@
case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
+ case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break;
case Interpreter::java_lang_math_sin : // fall thru
case Interpreter::java_lang_math_cos : // fall thru
@@ -2224,7 +2227,8 @@
const int overhead_size = sizeof(BytecodeInterpreter)/wordSize +
( frame::sender_sp_offset - frame::link_offset) + 2;
- const int method_stack = (method->max_locals() + method->max_stack()) *
+ const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
+ const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) *
Interpreter::stackElementWords();
return overhead_size + method_stack + stub_code;
}
@@ -2289,7 +2293,8 @@
// Need +1 here because stack_base points to the word just above the first expr stack entry
// and stack_limit is supposed to point to the word just below the last expr stack entry.
// See generate_compute_interpreter_state.
- to_fill->_stack_limit = stack_base - (method->max_stack() + 1);
+ int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
+ to_fill->_stack_limit = stack_base - (method->max_stack() + extra_stack + 1);
to_fill->_monitor_base = (BasicObjectLock*) monitor_base;
to_fill->_self_link = to_fill;
@@ -2335,7 +2340,8 @@
monitor_size);
// Now with full size expression stack
- int full_frame_size = short_frame_size + method->max_stack() * BytesPerWord;
+ int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries();
+ int full_frame_size = short_frame_size + (method->max_stack() + extra_stack) * BytesPerWord;
// and now with only live portion of the expression stack
short_frame_size = short_frame_size + tempcount * BytesPerWord;
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -555,13 +555,18 @@
}
-// Jump to from_interpreted entry of a call unless single stepping is possible
-// in this thread in which case we must call the i2i entry
-void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) {
+void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() {
// set sender sp
lea(rsi, Address(rsp, wordSize));
// record last_sp
movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), rsi);
+}
+
+
+// Jump to from_interpreted entry of a call unless single stepping is possible
+// in this thread in which case we must call the i2i entry
+void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) {
+ prepare_to_jump_from_interpreted();
if (JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -161,6 +161,7 @@
// jump to an invoked target
+ void prepare_to_jump_from_interpreted();
void jump_from_interpreted(Register method, Register temp);
// Returning from interpreted functions
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -551,13 +551,18 @@
MacroAssembler::call_VM_leaf_base(entry_point, 3);
}
-// Jump to from_interpreted entry of a call unless single stepping is possible
-// in this thread in which case we must call the i2i entry
-void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) {
+void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() {
// set sender sp
lea(r13, Address(rsp, wordSize));
// record last_sp
movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), r13);
+}
+
+
+// Jump to from_interpreted entry of a call unless single stepping is possible
+// in this thread in which case we must call the i2i entry
+void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) {
+ prepare_to_jump_from_interpreted();
if (JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
--- a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -176,6 +176,7 @@
void dispatch_via (TosState state, address* table);
// jump to an invoked target
+ void prepare_to_jump_from_interpreted();
void jump_from_interpreted(Register method, Register temp);
--- a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
address generate_normal_entry(bool synchronized);
address generate_native_entry(bool synchronized);
address generate_abstract_entry(void);
+ address generate_method_handle_entry(void);
address generate_math_entry(AbstractInterpreter::MethodKind kind);
address generate_empty_entry(void);
address generate_accessor_entry(void);
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -201,11 +201,12 @@
address entry_point = __ pc();
// abstract method entry
- // remove return address. Not really needed, since exception handling throws away expression stack
- __ pop(rbx);
- // adjust stack to what a normal return would do
- __ mov(rsp, rsi);
+ // pop return address, reset last_sp to NULL
+ __ empty_expression_stack();
+ __ restore_bcp(); // rsi must be correct for exception handler (was destroyed)
+ __ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
+
// throw exception
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
// the call_VM checks for exception, so we should never return here.
@@ -214,6 +215,20 @@
return entry_point;
}
+
+// Method handle invoker
+// Dispatch a method of the form java.dyn.MethodHandles::invoke(...)
+address InterpreterGenerator::generate_method_handle_entry(void) {
+ if (!EnableMethodHandles) {
+ return generate_abstract_entry();
+ }
+
+ address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm);
+
+ return entry_point;
+}
+
+
// This method tells the deoptimizer how big an interpreted frame must be:
int AbstractInterpreter::size_activation(methodOop method,
int tempcount,
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -294,6 +294,16 @@
}
+// Method handle invoker
+// Dispatch a method of the form java.dyn.MethodHandles::invoke(...)
+address InterpreterGenerator::generate_method_handle_entry(void) {
+ if (!EnableMethodHandles) {
+ return generate_abstract_entry();
+ }
+ return generate_abstract_entry(); //6815692//
+}
+
+
// Empty method, generate a very fast return.
address InterpreterGenerator::generate_empty_entry(void) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,1133 @@
+/*
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+#include "incls/_precompiled.incl"
+#include "incls/_methodHandles_x86.cpp.incl"
+
+#define __ _masm->
+
+address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm,
+ address interpreted_entry) {
+ // Just before the actual machine code entry point, allocate space
+ // for a MethodHandleEntry::Data record, so that we can manage everything
+ // from one base pointer.
+ __ align(wordSize);
+ address target = __ pc() + sizeof(Data);
+ while (__ pc() < target) {
+ __ nop();
+ __ align(wordSize);
+ }
+
+ MethodHandleEntry* me = (MethodHandleEntry*) __ pc();
+ me->set_end_address(__ pc()); // set a temporary end_address
+ me->set_from_interpreted_entry(interpreted_entry);
+ me->set_type_checking_entry(NULL);
+
+ return (address) me;
+}
+
+MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm,
+ address start_addr) {
+ MethodHandleEntry* me = (MethodHandleEntry*) start_addr;
+ assert(me->end_address() == start_addr, "valid ME");
+
+ // Fill in the real end_address:
+ __ align(wordSize);
+ me->set_end_address(__ pc());
+
+ return me;
+}
+
+#ifdef ASSERT
+static void verify_argslot(MacroAssembler* _masm, Register rax_argslot,
+ const char* error_message) {
+ // Verify that argslot lies within (rsp, rbp].
+ Label L_ok, L_bad;
+ __ cmpptr(rax_argslot, rbp);
+ __ jcc(Assembler::above, L_bad);
+ __ cmpptr(rsp, rax_argslot);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop(error_message);
+ __ bind(L_ok);
+}
+#endif
+
+
+// Code generation
+address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
+ // rbx: methodOop
+ // rcx: receiver method handle (must load from sp[MethodTypeForm.vmslots])
+ // rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
+ // rdx: garbage temp, blown away
+
+ Register rbx_method = rbx;
+ Register rcx_recv = rcx;
+ Register rax_mtype = rax;
+ Register rdx_temp = rdx;
+
+ // emit WrongMethodType path first, to enable jccb back-branch from main path
+ Label wrong_method_type;
+ __ bind(wrong_method_type);
+ __ push(rax_mtype); // required mtype
+ __ push(rcx_recv); // bad mh (1st stacked argument)
+ __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
+
+ // here's where control starts out:
+ __ align(CodeEntryAlignment);
+ address entry_point = __ pc();
+
+ // fetch the MethodType from the method handle into rax (the 'check' register)
+ {
+ Register tem = rbx_method;
+ for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) {
+ __ movptr(rax_mtype, Address(tem, *pchase));
+ tem = rax_mtype; // in case there is another indirection
+ }
+ }
+ Register rbx_temp = rbx_method; // done with incoming methodOop
+
+ // given the MethodType, find out where the MH argument is buried
+ __ movptr(rdx_temp, Address(rax_mtype,
+ __ delayed_value(java_dyn_MethodType::form_offset_in_bytes, rbx_temp)));
+ __ movl(rdx_temp, Address(rdx_temp,
+ __ delayed_value(java_dyn_MethodTypeForm::vmslots_offset_in_bytes, rbx_temp)));
+ __ movptr(rcx_recv, __ argument_address(rdx_temp));
+
+ __ check_method_handle_type(rax_mtype, rcx_recv, rdx_temp, wrong_method_type);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+
+ return entry_point;
+}
+
+// Helper to insert argument slots into the stack.
+// arg_slots must be a multiple of stack_move_unit() and <= 0
+void MethodHandles::insert_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ int arg_mask,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp) {
+ assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
+ (!arg_slots.is_register() ? rsp : arg_slots.as_register()));
+
+#ifdef ASSERT
+ verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame");
+ if (arg_slots.is_register()) {
+ Label L_ok, L_bad;
+ __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
+ __ jcc(Assembler::greater, L_bad);
+ __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
+ __ jcc(Assembler::zero, L_ok);
+ __ bind(L_bad);
+ __ stop("assert arg_slots <= 0 and clear low bits");
+ __ bind(L_ok);
+ } else {
+ assert(arg_slots.as_constant() <= 0, "");
+ assert(arg_slots.as_constant() % -stack_move_unit() == 0, "");
+ }
+#endif //ASSERT
+
+#ifdef _LP64
+ if (arg_slots.is_register()) {
+ // clean high bits of stack motion register (was loaded as an int)
+ __ movslq(arg_slots.as_register(), arg_slots.as_register());
+ }
+#endif
+
+ // Make space on the stack for the inserted argument(s).
+ // Then pull down everything shallower than rax_argslot.
+ // The stacked return address gets pulled down with everything else.
+ // That is, copy [rsp, argslot) downward by -size words. In pseudo-code:
+ // rsp -= size;
+ // for (rdx = rsp + size; rdx < argslot; rdx++)
+ // rdx[-size] = rdx[0]
+ // argslot -= size;
+ __ mov(rdx_temp, rsp); // source pointer for copy
+ __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ {
+ Label loop;
+ __ bind(loop);
+ // pull one word down each time through the loop
+ __ movptr(rbx_temp, Address(rdx_temp, 0));
+ __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp);
+ __ addptr(rdx_temp, wordSize);
+ __ cmpptr(rdx_temp, rax_argslot);
+ __ jcc(Assembler::less, loop);
+ }
+
+ // Now move the argslot down, to point to the opened-up space.
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+
+ if (TaggedStackInterpreter && arg_mask != _INSERT_NO_MASK) {
+ // The caller has specified a bitmask of tags to put into the opened space.
+ // This only works when the arg_slots value is an assembly-time constant.
+ int constant_arg_slots = arg_slots.as_constant() / stack_move_unit();
+ int tag_offset = Interpreter::tag_offset_in_bytes() - Interpreter::value_offset_in_bytes();
+ for (int slot = 0; slot < constant_arg_slots; slot++) {
+ BasicType slot_type = ((arg_mask & (1 << slot)) == 0 ? T_OBJECT : T_INT);
+ int slot_offset = Interpreter::stackElementSize() * slot;
+ Address tag_addr(rax_argslot, slot_offset + tag_offset);
+ __ movptr(tag_addr, frame::tag_for_basic_type(slot_type));
+ }
+ // Note that the new argument slots are tagged properly but contain
+ // garbage at this point. The value portions must be initialized
+ // by the caller. (Especially references!)
+ }
+}
+
+// Helper to remove argument slots from the stack.
+// arg_slots must be a multiple of stack_move_unit() and >= 0
+void MethodHandles::remove_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp) {
+ assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
+ (!arg_slots.is_register() ? rsp : arg_slots.as_register()));
+
+#ifdef ASSERT
+ {
+ // Verify that [argslot..argslot+size) lies within (rsp, rbp).
+ Label L_ok, L_bad;
+ __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr));
+ __ cmpptr(rbx_temp, rbp);
+ __ jcc(Assembler::above, L_bad);
+ __ cmpptr(rsp, rax_argslot);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop("deleted argument(s) must fall within current frame");
+ __ bind(L_ok);
+ }
+ if (arg_slots.is_register()) {
+ Label L_ok, L_bad;
+ __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
+ __ jcc(Assembler::less, L_bad);
+ __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
+ __ jcc(Assembler::zero, L_ok);
+ __ bind(L_bad);
+ __ stop("assert arg_slots >= 0 and clear low bits");
+ __ bind(L_ok);
+ } else {
+ assert(arg_slots.as_constant() >= 0, "");
+ assert(arg_slots.as_constant() % -stack_move_unit() == 0, "");
+ }
+#endif //ASSERT
+
+#ifdef _LP64
+ if (false) { // not needed, since register is positive
+ // clean high bits of stack motion register (was loaded as an int)
+ if (arg_slots.is_register())
+ __ movslq(arg_slots.as_register(), arg_slots.as_register());
+ }
+#endif
+
+ // Pull up everything shallower than rax_argslot.
+ // Then remove the excess space on the stack.
+ // The stacked return address gets pulled up with everything else.
+ // That is, copy [rsp, argslot) upward by size words. In pseudo-code:
+ // for (rdx = argslot-1; rdx >= rsp; --rdx)
+ // rdx[size] = rdx[0]
+ // argslot += size;
+ // rsp += size;
+ __ lea(rdx_temp, Address(rax_argslot, -wordSize)); // source pointer for copy
+ {
+ Label loop;
+ __ bind(loop);
+ // pull one word up each time through the loop
+ __ movptr(rbx_temp, Address(rdx_temp, 0));
+ __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp);
+ __ addptr(rdx_temp, -wordSize);
+ __ cmpptr(rdx_temp, rsp);
+ __ jcc(Assembler::greaterEqual, loop);
+ }
+
+ // Now move the argslot up, to point to the just-copied block.
+ __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ // And adjust the argslot address to point at the deletion point.
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+}
+
+#ifndef PRODUCT
+void trace_method_handle_stub(const char* adaptername,
+ oop mh,
+ intptr_t* entry_sp,
+ intptr_t* saved_sp) {
+ // called as a leaf from native code: do not block the JVM!
+ printf("MH %s "PTR_FORMAT" "PTR_FORMAT" "INTX_FORMAT"\n", adaptername, mh, entry_sp, entry_sp - saved_sp);
+}
+#endif //PRODUCT
+
+// Generate an "entry" field for a method handle.
+// This determines how the method handle will respond to calls.
+void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) {
+ // Here is the register state during an interpreted call,
+ // as set up by generate_method_handle_interpreter_entry():
+ // - rbx: garbage temp (was MethodHandle.invoke methodOop, unused)
+ // - rcx: receiver method handle
+ // - rax: method handle type (only used by the check_mtype entry point)
+ // - rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
+ // - rdx: garbage temp, can blow away
+
+ Register rcx_recv = rcx;
+ Register rax_argslot = rax;
+ Register rbx_temp = rbx;
+ Register rdx_temp = rdx;
+
+ guarantee(java_dyn_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
+
+ // some handy addresses
+ Address rbx_method_fie( rbx, methodOopDesc::from_interpreted_offset() );
+
+ Address rcx_mh_vmtarget( rcx_recv, java_dyn_MethodHandle::vmtarget_offset_in_bytes() );
+ Address rcx_dmh_vmindex( rcx_recv, sun_dyn_DirectMethodHandle::vmindex_offset_in_bytes() );
+
+ Address rcx_bmh_vmargslot( rcx_recv, sun_dyn_BoundMethodHandle::vmargslot_offset_in_bytes() );
+ Address rcx_bmh_argument( rcx_recv, sun_dyn_BoundMethodHandle::argument_offset_in_bytes() );
+
+ Address rcx_amh_vmargslot( rcx_recv, sun_dyn_AdapterMethodHandle::vmargslot_offset_in_bytes() );
+ Address rcx_amh_argument( rcx_recv, sun_dyn_AdapterMethodHandle::argument_offset_in_bytes() );
+ Address rcx_amh_conversion( rcx_recv, sun_dyn_AdapterMethodHandle::conversion_offset_in_bytes() );
+ Address vmarg; // __ argument_address(vmargslot)
+
+ int tag_offset = -1;
+ if (TaggedStackInterpreter) {
+ tag_offset = Interpreter::tag_offset_in_bytes() - Interpreter::value_offset_in_bytes();
+ assert(tag_offset = wordSize, "stack grows as expected");
+ }
+
+ if (have_entry(ek)) {
+ __ nop(); // empty stubs make SG sick
+ return;
+ }
+
+ address interp_entry = __ pc();
+ if (UseCompressedOops) __ unimplemented("UseCompressedOops");
+
+#ifndef PRODUCT
+ if (TraceMethodHandles) {
+ __ push(rax); __ push(rbx); __ push(rcx); __ push(rdx); __ push(rsi); __ push(rdi);
+ __ lea(rax, Address(rsp, wordSize*6)); // entry_sp
+ // arguments:
+ __ push(rsi); // saved_sp
+ __ push(rax); // entry_sp
+ __ push(rcx); // mh
+ __ push(rcx);
+ __ movptr(Address(rsp, 0), (intptr_t)entry_name(ek));
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), 4);
+ __ pop(rdi); __ pop(rsi); __ pop(rdx); __ pop(rcx); __ pop(rbx); __ pop(rax);
+ }
+#endif //PRODUCT
+
+ switch ((int) ek) {
+ case _check_mtype:
+ {
+ // this stub is special, because it requires a live mtype argument
+ Register rax_mtype = rax;
+
+ // emit WrongMethodType path first, to enable jccb back-branch
+ Label wrong_method_type;
+ __ bind(wrong_method_type);
+ __ movptr(rdx_temp, ExternalAddress((address) &_entries[_wrong_method_type]));
+ __ jmp(Address(rdx_temp, MethodHandleEntry::from_interpreted_entry_offset_in_bytes()));
+ __ hlt();
+
+ interp_entry = __ pc();
+ __ check_method_handle_type(rax_mtype, rcx_recv, rdx_temp, wrong_method_type);
+ // now rax_mtype is dead; subsequent stubs will use it as a temp
+
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _wrong_method_type:
+ {
+ // this stub is special, because it requires a live mtype argument
+ Register rax_mtype = rax;
+
+ interp_entry = __ pc();
+ __ push(rax_mtype); // required mtype
+ __ push(rcx_recv); // random mh (1st stacked argument)
+ __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
+ }
+ break;
+
+ case _invokestatic_mh:
+ case _invokespecial_mh:
+ {
+ Register rbx_method = rbx_temp;
+ __ movptr(rbx_method, rcx_mh_vmtarget); // target is a methodOop
+ __ verify_oop(rbx_method);
+ // same as TemplateTable::invokestatic or invokespecial,
+ // minus the CP setup and profiling:
+ if (ek == _invokespecial_mh) {
+ // Must load & check the first argument before entering the target method.
+ __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
+ __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
+ __ null_check(rcx_recv);
+ __ verify_oop(rcx_recv);
+ }
+ __ jmp(rbx_method_fie);
+ }
+ break;
+
+ case _invokevirtual_mh:
+ {
+ // same as TemplateTable::invokevirtual,
+ // minus the CP setup and profiling:
+
+ // pick out the vtable index and receiver offset from the MH,
+ // and then we can discard it:
+ __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
+ Register rbx_index = rbx_temp;
+ __ movl(rbx_index, rcx_dmh_vmindex);
+ // Note: The verifier allows us to ignore rcx_mh_vmtarget.
+ __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
+ __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes());
+
+ // get receiver klass
+ Register rax_klass = rax_argslot;
+ __ load_klass(rax_klass, rcx_recv);
+ __ verify_oop(rax_klass);
+
+ // get target methodOop & entry point
+ const int base = instanceKlass::vtable_start_offset() * wordSize;
+ assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below");
+ Address vtable_entry_addr(rax_klass,
+ rbx_index, Address::times_ptr,
+ base + vtableEntry::method_offset_in_bytes());
+ Register rbx_method = rbx_temp;
+ __ movl(rbx_method, vtable_entry_addr);
+
+ __ verify_oop(rbx_method);
+ __ jmp(rbx_method_fie);
+ }
+ break;
+
+ case _invokeinterface_mh:
+ {
+ // same as TemplateTable::invokeinterface,
+ // minus the CP setup and profiling:
+
+ // pick out the interface and itable index from the MH.
+ __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp);
+ Register rdx_intf = rdx_temp;
+ Register rbx_index = rbx_temp;
+ __ movptr(rdx_intf, rcx_mh_vmtarget);
+ __ movl(rbx_index, rcx_dmh_vmindex);
+ __ movptr(rcx_recv, __ argument_address(rax_argslot, -1));
+ __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes());
+
+ // get receiver klass
+ Register rax_klass = rax_argslot;
+ __ load_klass(rax_klass, rcx_recv);
+ __ verify_oop(rax_klass);
+
+ Register rcx_temp = rcx_recv;
+ Register rbx_method = rbx_index;
+
+ // get interface klass
+ Label no_such_interface;
+ __ verify_oop(rdx_intf);
+ __ lookup_interface_method(rax_klass, rdx_intf,
+ // note: next two args must be the same:
+ rbx_index, rbx_method,
+ rcx_temp,
+ no_such_interface);
+
+ __ verify_oop(rbx_method);
+ __ jmp(rbx_method_fie);
+ __ hlt();
+
+ __ bind(no_such_interface);
+ // Throw an exception.
+ // For historical reasons, it will be IncompatibleClassChangeError.
+ __ should_not_reach_here(); // %%% FIXME NYI
+ }
+ break;
+
+ case _bound_ref_mh:
+ case _bound_int_mh:
+ case _bound_long_mh:
+ case _bound_ref_direct_mh:
+ case _bound_int_direct_mh:
+ case _bound_long_direct_mh:
+ {
+ bool direct_to_method = (ek >= _bound_ref_direct_mh);
+ BasicType arg_type = T_ILLEGAL;
+ if (ek == _bound_long_mh || ek == _bound_long_direct_mh) {
+ arg_type = T_LONG;
+ } else if (ek == _bound_int_mh || ek == _bound_int_direct_mh) {
+ arg_type = T_INT;
+ } else {
+ assert(ek == _bound_ref_mh || ek == _bound_ref_direct_mh, "must be ref");
+ arg_type = T_OBJECT;
+ }
+ int arg_slots = type2size[arg_type];
+ int arg_mask = (arg_type == T_OBJECT ? _INSERT_REF_MASK :
+ arg_slots == 1 ? _INSERT_INT_MASK : _INSERT_LONG_MASK);
+
+ // make room for the new argument:
+ __ movl(rax_argslot, rcx_bmh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot));
+ insert_arg_slots(_masm, arg_slots * stack_move_unit(), arg_mask,
+ rax_argslot, rbx_temp, rdx_temp);
+
+ // store bound argument into the new stack slot:
+ __ movptr(rbx_temp, rcx_bmh_argument);
+ Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type));
+ if (arg_type == T_OBJECT) {
+ __ movptr(Address(rax_argslot, 0), rbx_temp);
+ } else {
+ __ load_sized_value(rbx_temp, prim_value_addr,
+ type2aelembytes(arg_type), is_signed_subword_type(arg_type));
+ __ movptr(Address(rax_argslot, 0), rbx_temp);
+#ifndef _LP64
+ if (arg_slots == 2) {
+ __ movl(rbx_temp, prim_value_addr.plus_disp(wordSize));
+ __ movl(Address(rax_argslot, Interpreter::stackElementSize()), rbx_temp);
+ }
+#endif //_LP64
+ break;
+ }
+
+ if (direct_to_method) {
+ Register rbx_method = rbx_temp;
+ __ movptr(rbx_method, rcx_mh_vmtarget);
+ __ verify_oop(rbx_method);
+ __ jmp(rbx_method_fie);
+ } else {
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ verify_oop(rcx_recv);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ }
+ break;
+
+ case _adapter_retype_only:
+ // immediately jump to the next MH layer:
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ verify_oop(rcx_recv);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ // This is OK when all parameter types widen.
+ // It is also OK when a return type narrows.
+ break;
+
+ case _adapter_check_cast:
+ {
+ // temps:
+ Register rbx_klass = rbx_temp; // interesting AMH data
+
+ // check a reference argument before jumping to the next layer of MH:
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ vmarg = __ argument_address(rax_argslot);
+
+ // What class are we casting to?
+ __ movptr(rbx_klass, rcx_amh_argument); // this is a Class object!
+ __ movptr(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+
+ // get the new MH:
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ // (now we are done with the old MH)
+
+ Label done;
+ __ movptr(rdx_temp, vmarg);
+ __ testl(rdx_temp, rdx_temp);
+ __ jcc(Assembler::zero, done); // no cast if null
+ __ load_klass(rdx_temp, rdx_temp);
+
+ // live at this point:
+ // - rbx_klass: klass required by the target method
+ // - rdx_temp: argument klass to test
+ // - rcx_recv: method handle to invoke (after cast succeeds)
+ __ check_klass_subtype(rdx_temp, rbx_klass, rax_argslot, done);
+
+ // If we get here, the type check failed!
+ // Call the wrong_method_type stub, passing the failing argument type in rax.
+ Register rax_mtype = rax_argslot;
+ __ push(rbx_klass); // missed klass (required type)
+ __ push(rdx_temp); // bad actual type (1st stacked argument)
+ __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
+
+ __ bind(done);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_prim_to_prim:
+ case _adapter_ref_to_prim:
+ // handled completely by optimized cases
+ __ stop("init_AdapterMethodHandle should not issue this");
+ break;
+
+ case _adapter_opt_i2i: // optimized subcase of adapt_prim_to_prim
+//case _adapter_opt_f2i: // optimized subcase of adapt_prim_to_prim
+ case _adapter_opt_l2i: // optimized subcase of adapt_prim_to_prim
+ case _adapter_opt_unboxi: // optimized subcase of adapt_ref_to_prim
+ {
+ // perform an in-place conversion to int or an int subword
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ vmarg = __ argument_address(rax_argslot);
+
+ switch (ek) {
+ case _adapter_opt_i2i:
+ __ movl(rdx_temp, vmarg);
+ break;
+ case _adapter_opt_l2i:
+ {
+ // just delete the extra slot; on a little-endian machine we keep the first
+ __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
+ remove_arg_slots(_masm, -stack_move_unit(),
+ rax_argslot, rbx_temp, rdx_temp);
+ vmarg = Address(rax_argslot, -Interpreter::stackElementSize());
+ __ movl(rdx_temp, vmarg);
+ }
+ break;
+ case _adapter_opt_unboxi:
+ {
+ // Load the value up from the heap.
+ __ movptr(rdx_temp, vmarg);
+ int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT);
+#ifdef ASSERT
+ for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
+ if (is_subword_type(BasicType(bt)))
+ assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(BasicType(bt)), "");
+ }
+#endif
+ __ null_check(rdx_temp, value_offset);
+ __ movl(rdx_temp, Address(rdx_temp, value_offset));
+ // We load this as a word. Because we are little-endian,
+ // the low bits will be correct, but the high bits may need cleaning.
+ // The vminfo will guide us to clean those bits.
+ }
+ break;
+ default:
+ assert(false, "");
+ }
+ goto finish_int_conversion;
+ }
+
+ finish_int_conversion:
+ {
+ Register rbx_vminfo = rbx_temp;
+ __ movl(rbx_vminfo, rcx_amh_conversion);
+ assert(CONV_VMINFO_SHIFT == 0, "preshifted");
+
+ // get the new MH:
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ // (now we are done with the old MH)
+
+ // original 32-bit vmdata word must be of this form:
+ // | MBZ:16 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 |
+ __ xchgl(rcx, rbx_vminfo); // free rcx for shifts
+ __ shll(rdx_temp /*, rcx*/);
+ Label zero_extend, done;
+ __ testl(rcx, CONV_VMINFO_SIGN_FLAG);
+ __ jcc(Assembler::zero, zero_extend);
+
+ // this path is taken for int->byte, int->short
+ __ sarl(rdx_temp /*, rcx*/);
+ __ jmp(done);
+
+ __ bind(zero_extend);
+ // this is taken for int->char
+ __ shrl(rdx_temp /*, rcx*/);
+
+ __ bind(done);
+ __ movptr(vmarg, rdx_temp);
+ __ xchgl(rcx, rbx_vminfo); // restore rcx_recv
+
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_opt_i2l: // optimized subcase of adapt_prim_to_prim
+ case _adapter_opt_unboxl: // optimized subcase of adapt_ref_to_prim
+ {
+ // perform an in-place int-to-long or ref-to-long conversion
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+
+ // on a little-endian machine we keep the first slot and add another after
+ __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
+ insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK,
+ rax_argslot, rbx_temp, rdx_temp);
+ Address vmarg1(rax_argslot, -Interpreter::stackElementSize());
+ Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize());
+
+ switch (ek) {
+ case _adapter_opt_i2l:
+ {
+ __ movl(rdx_temp, vmarg1);
+ __ sarl(rdx_temp, 31); // __ extend_sign()
+ __ movl(vmarg2, rdx_temp); // store second word
+ }
+ break;
+ case _adapter_opt_unboxl:
+ {
+ // Load the value up from the heap.
+ __ movptr(rdx_temp, vmarg1);
+ int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_LONG);
+ assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(T_DOUBLE), "");
+ __ null_check(rdx_temp, value_offset);
+ __ movl(rbx_temp, Address(rdx_temp, value_offset + 0*BytesPerInt));
+ __ movl(rdx_temp, Address(rdx_temp, value_offset + 1*BytesPerInt));
+ __ movl(vmarg1, rbx_temp);
+ __ movl(vmarg2, rdx_temp);
+ }
+ break;
+ default:
+ assert(false, "");
+ }
+
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_opt_f2d: // optimized subcase of adapt_prim_to_prim
+ case _adapter_opt_d2f: // optimized subcase of adapt_prim_to_prim
+ {
+ // perform an in-place floating primitive conversion
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot, 1));
+ if (ek == _adapter_opt_f2d) {
+ insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK,
+ rax_argslot, rbx_temp, rdx_temp);
+ }
+ Address vmarg(rax_argslot, -Interpreter::stackElementSize());
+
+#ifdef _LP64
+ if (ek == _adapter_opt_f2d) {
+ __ movflt(xmm0, vmarg);
+ __ cvtss2sd(xmm0, xmm0);
+ __ movdbl(vmarg, xmm0);
+ } else {
+ __ movdbl(xmm0, vmarg);
+ __ cvtsd2ss(xmm0, xmm0);
+ __ movflt(vmarg, xmm0);
+ }
+#else //_LP64
+ if (ek == _adapter_opt_f2d) {
+ __ fld_s(vmarg); // load float to ST0
+ __ fstp_s(vmarg); // store single
+ } else if (!TaggedStackInterpreter) {
+ __ fld_d(vmarg); // load double to ST0
+ __ fstp_s(vmarg); // store single
+ } else {
+ Address vmarg_tag = vmarg.plus_disp(tag_offset);
+ Address vmarg2 = vmarg.plus_disp(Interpreter::stackElementSize());
+ // vmarg2_tag does not participate in this code
+ Register rbx_tag = rbx_temp;
+ __ movl(rbx_tag, vmarg_tag); // preserve tag
+ __ movl(rdx_temp, vmarg2); // get second word of double
+ __ movl(vmarg_tag, rdx_temp); // align with first word
+ __ fld_d(vmarg); // load double to ST0
+ __ movl(vmarg_tag, rbx_tag); // restore tag
+ __ fstp_s(vmarg); // store single
+ }
+#endif //_LP64
+
+ if (ek == _adapter_opt_d2f) {
+ remove_arg_slots(_masm, -stack_move_unit(),
+ rax_argslot, rbx_temp, rdx_temp);
+ }
+
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_prim_to_ref:
+ __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
+ break;
+
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ // handled completely by optimized cases
+ __ stop("init_AdapterMethodHandle should not issue this");
+ break;
+
+ case _adapter_opt_swap_1:
+ case _adapter_opt_swap_2:
+ case _adapter_opt_rot_1_up:
+ case _adapter_opt_rot_1_down:
+ case _adapter_opt_rot_2_up:
+ case _adapter_opt_rot_2_down:
+ {
+ int rotate = 0, swap_slots = 0;
+ switch ((int)ek) {
+ case _adapter_opt_swap_1: swap_slots = 1; break;
+ case _adapter_opt_swap_2: swap_slots = 2; break;
+ case _adapter_opt_rot_1_up: swap_slots = 1; rotate++; break;
+ case _adapter_opt_rot_1_down: swap_slots = 1; rotate--; break;
+ case _adapter_opt_rot_2_up: swap_slots = 2; rotate++; break;
+ case _adapter_opt_rot_2_down: swap_slots = 2; rotate--; break;
+ default: assert(false, "");
+ }
+
+ // the real size of the move must be doubled if TaggedStackInterpreter:
+ int swap_bytes = (int)( swap_slots * Interpreter::stackElementWords() * wordSize );
+
+ // 'argslot' is the position of the first argument to swap
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot));
+
+ // 'vminfo' is the second
+ Register rbx_destslot = rbx_temp;
+ __ movl(rbx_destslot, rcx_amh_conversion);
+ assert(CONV_VMINFO_SHIFT == 0, "preshifted");
+ __ andl(rbx_destslot, CONV_VMINFO_MASK);
+ __ lea(rbx_destslot, __ argument_address(rbx_destslot));
+ DEBUG_ONLY(verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame"));
+
+ if (!rotate) {
+ for (int i = 0; i < swap_bytes; i += wordSize) {
+ __ movptr(rdx_temp, Address(rax_argslot , i));
+ __ push(rdx_temp);
+ __ movptr(rdx_temp, Address(rbx_destslot, i));
+ __ movptr(Address(rax_argslot, i), rdx_temp);
+ __ pop(rdx_temp);
+ __ movptr(Address(rbx_destslot, i), rdx_temp);
+ }
+ } else {
+ // push the first chunk, which is going to get overwritten
+ for (int i = swap_bytes; (i -= wordSize) >= 0; ) {
+ __ movptr(rdx_temp, Address(rax_argslot, i));
+ __ push(rdx_temp);
+ }
+
+ if (rotate > 0) {
+ // rotate upward
+ __ subptr(rax_argslot, swap_bytes);
+#ifdef ASSERT
+ {
+ // Verify that argslot > destslot, by at least swap_bytes.
+ Label L_ok;
+ __ cmpptr(rax_argslot, rbx_destslot);
+ __ jcc(Assembler::aboveEqual, L_ok);
+ __ stop("source must be above destination (upward rotation)");
+ __ bind(L_ok);
+ }
+#endif
+ // work argslot down to destslot, copying contiguous data upwards
+ // pseudo-code:
+ // rax = src_addr - swap_bytes
+ // rbx = dest_addr
+ // while (rax >= rbx) *(rax + swap_bytes) = *(rax + 0), rax--;
+ Label loop;
+ __ bind(loop);
+ __ movptr(rdx_temp, Address(rax_argslot, 0));
+ __ movptr(Address(rax_argslot, swap_bytes), rdx_temp);
+ __ addptr(rax_argslot, -wordSize);
+ __ cmpptr(rax_argslot, rbx_destslot);
+ __ jcc(Assembler::aboveEqual, loop);
+ } else {
+ __ addptr(rax_argslot, swap_bytes);
+#ifdef ASSERT
+ {
+ // Verify that argslot < destslot, by at least swap_bytes.
+ Label L_ok;
+ __ cmpptr(rax_argslot, rbx_destslot);
+ __ jcc(Assembler::belowEqual, L_ok);
+ __ stop("source must be below destination (downward rotation)");
+ __ bind(L_ok);
+ }
+#endif
+ // work argslot up to destslot, copying contiguous data downwards
+ // pseudo-code:
+ // rax = src_addr + swap_bytes
+ // rbx = dest_addr
+ // while (rax <= rbx) *(rax - swap_bytes) = *(rax + 0), rax++;
+ Label loop;
+ __ bind(loop);
+ __ movptr(rdx_temp, Address(rax_argslot, 0));
+ __ movptr(Address(rax_argslot, -swap_bytes), rdx_temp);
+ __ addptr(rax_argslot, wordSize);
+ __ cmpptr(rax_argslot, rbx_destslot);
+ __ jcc(Assembler::belowEqual, loop);
+ }
+
+ // pop the original first chunk into the destination slot, now free
+ for (int i = 0; i < swap_bytes; i += wordSize) {
+ __ pop(rdx_temp);
+ __ movptr(Address(rbx_destslot, i), rdx_temp);
+ }
+ }
+
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_dup_args:
+ {
+ // 'argslot' is the position of the first argument to duplicate
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot));
+
+ // 'stack_move' is negative number of words to duplicate
+ Register rdx_stack_move = rdx_temp;
+ __ movl(rdx_stack_move, rcx_amh_conversion);
+ __ sarl(rdx_stack_move, CONV_STACK_MOVE_SHIFT);
+
+ int argslot0_num = 0;
+ Address argslot0 = __ argument_address(RegisterOrConstant(argslot0_num));
+ assert(argslot0.base() == rsp, "");
+ int pre_arg_size = argslot0.disp();
+ assert(pre_arg_size % wordSize == 0, "");
+ assert(pre_arg_size > 0, "must include PC");
+
+ // remember the old rsp+1 (argslot[0])
+ Register rbx_oldarg = rbx_temp;
+ __ lea(rbx_oldarg, argslot0);
+
+ // move rsp down to make room for dups
+ __ lea(rsp, Address(rsp, rdx_stack_move, Address::times_ptr));
+
+ // compute the new rsp+1 (argslot[0])
+ Register rdx_newarg = rdx_temp;
+ __ lea(rdx_newarg, argslot0);
+
+ __ push(rdi); // need a temp
+ // (preceding push must be done after arg addresses are taken!)
+
+ // pull down the pre_arg_size data (PC)
+ for (int i = -pre_arg_size; i < 0; i += wordSize) {
+ __ movptr(rdi, Address(rbx_oldarg, i));
+ __ movptr(Address(rdx_newarg, i), rdi);
+ }
+
+ // copy from rax_argslot[0...] down to new_rsp[1...]
+ // pseudo-code:
+ // rbx = old_rsp+1
+ // rdx = new_rsp+1
+ // rax = argslot
+ // while (rdx < rbx) *rdx++ = *rax++
+ Label loop;
+ __ bind(loop);
+ __ movptr(rdi, Address(rax_argslot, 0));
+ __ movptr(Address(rdx_newarg, 0), rdi);
+ __ addptr(rax_argslot, wordSize);
+ __ addptr(rdx_newarg, wordSize);
+ __ cmpptr(rdx_newarg, rbx_oldarg);
+ __ jcc(Assembler::less, loop);
+
+ __ pop(rdi); // restore temp
+
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_drop_args:
+ {
+ // 'argslot' is the position of the first argument to nuke
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot));
+
+ __ push(rdi); // need a temp
+ // (must do previous push after argslot address is taken)
+
+ // 'stack_move' is number of words to drop
+ Register rdi_stack_move = rdi;
+ __ movl(rdi_stack_move, rcx_amh_conversion);
+ __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ remove_arg_slots(_masm, rdi_stack_move,
+ rax_argslot, rbx_temp, rdx_temp);
+
+ __ pop(rdi); // restore temp
+
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ }
+ break;
+
+ case _adapter_collect_args:
+ __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
+ break;
+
+ case _adapter_spread_args:
+ // handled completely by optimized cases
+ __ stop("init_AdapterMethodHandle should not issue this");
+ break;
+
+ case _adapter_opt_spread_0:
+ case _adapter_opt_spread_1:
+ case _adapter_opt_spread_more:
+ {
+ // spread an array out into a group of arguments
+ int length_constant = -1;
+ switch (ek) {
+ case _adapter_opt_spread_0: length_constant = 0; break;
+ case _adapter_opt_spread_1: length_constant = 1; break;
+ }
+
+ // find the address of the array argument
+ __ movl(rax_argslot, rcx_amh_vmargslot);
+ __ lea(rax_argslot, __ argument_address(rax_argslot));
+
+ // grab some temps
+ { __ push(rsi); __ push(rdi); }
+ // (preceding pushes must be done after argslot address is taken!)
+#define UNPUSH_RSI_RDI \
+ { __ pop(rdi); __ pop(rsi); }
+
+ // arx_argslot points both to the array and to the first output arg
+ vmarg = Address(rax_argslot, 0);
+
+ // Get the array value.
+ Register rsi_array = rsi;
+ Register rdx_array_klass = rdx_temp;
+ BasicType elem_type = T_OBJECT;
+ int length_offset = arrayOopDesc::length_offset_in_bytes();
+ int elem0_offset = arrayOopDesc::base_offset_in_bytes(elem_type);
+ __ movptr(rsi_array, vmarg);
+ Label skip_array_check;
+ if (length_constant == 0) {
+ __ testptr(rsi_array, rsi_array);
+ __ jcc(Assembler::zero, skip_array_check);
+ }
+ __ null_check(rsi_array, oopDesc::klass_offset_in_bytes());
+ __ load_klass(rdx_array_klass, rsi_array);
+
+ // Check the array type.
+ Register rbx_klass = rbx_temp;
+ __ movptr(rbx_klass, rcx_amh_argument); // this is a Class object!
+ __ movptr(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+
+ Label ok_array_klass, bad_array_klass, bad_array_length;
+ __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi, ok_array_klass);
+ // If we get here, the type check failed!
+ __ jmp(bad_array_klass);
+ __ bind(ok_array_klass);
+
+ // Check length.
+ if (length_constant >= 0) {
+ __ cmpl(Address(rsi_array, length_offset), length_constant);
+ } else {
+ Register rbx_vminfo = rbx_temp;
+ __ movl(rbx_vminfo, rcx_amh_conversion);
+ assert(CONV_VMINFO_SHIFT == 0, "preshifted");
+ __ andl(rbx_vminfo, CONV_VMINFO_MASK);
+ __ cmpl(rbx_vminfo, Address(rsi_array, length_offset));
+ }
+ __ jcc(Assembler::notEqual, bad_array_length);
+
+ Register rdx_argslot_limit = rdx_temp;
+
+ // Array length checks out. Now insert any required stack slots.
+ if (length_constant == -1) {
+ // Form a pointer to the end of the affected region.
+ __ lea(rdx_argslot_limit, Address(rax_argslot, Interpreter::stackElementSize()));
+ // 'stack_move' is negative number of words to insert
+ Register rdi_stack_move = rdi;
+ __ movl(rdi_stack_move, rcx_amh_conversion);
+ __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ Register rsi_temp = rsi_array; // spill this
+ insert_arg_slots(_masm, rdi_stack_move, -1,
+ rax_argslot, rbx_temp, rsi_temp);
+ // reload the array (since rsi was killed)
+ __ movptr(rsi_array, vmarg);
+ } else if (length_constant > 1) {
+ int arg_mask = 0;
+ int new_slots = (length_constant - 1);
+ for (int i = 0; i < new_slots; i++) {
+ arg_mask <<= 1;
+ arg_mask |= _INSERT_REF_MASK;
+ }
+ insert_arg_slots(_masm, new_slots * stack_move_unit(), arg_mask,
+ rax_argslot, rbx_temp, rdx_temp);
+ } else if (length_constant == 1) {
+ // no stack resizing required
+ } else if (length_constant == 0) {
+ remove_arg_slots(_masm, -stack_move_unit(),
+ rax_argslot, rbx_temp, rdx_temp);
+ }
+
+ // Copy from the array to the new slots.
+ // Note: Stack change code preserves integrity of rax_argslot pointer.
+ // So even after slot insertions, rax_argslot still points to first argument.
+ if (length_constant == -1) {
+ // [rax_argslot, rdx_argslot_limit) is the area we are inserting into.
+ Register rsi_source = rsi_array;
+ __ lea(rsi_source, Address(rsi_array, elem0_offset));
+ Label loop;
+ __ bind(loop);
+ __ movptr(rbx_temp, Address(rsi_source, 0));
+ __ movptr(Address(rax_argslot, 0), rbx_temp);
+ __ addptr(rsi_source, type2aelembytes(elem_type));
+ if (TaggedStackInterpreter) {
+ __ movptr(Address(rax_argslot, tag_offset),
+ frame::tag_for_basic_type(elem_type));
+ }
+ __ addptr(rax_argslot, Interpreter::stackElementSize());
+ __ cmpptr(rax_argslot, rdx_argslot_limit);
+ __ jcc(Assembler::less, loop);
+ } else if (length_constant == 0) {
+ __ bind(skip_array_check);
+ // nothing to copy
+ } else {
+ int elem_offset = elem0_offset;
+ int slot_offset = 0;
+ for (int index = 0; index < length_constant; index++) {
+ __ movptr(rbx_temp, Address(rsi_array, elem_offset));
+ __ movptr(Address(rax_argslot, slot_offset), rbx_temp);
+ elem_offset += type2aelembytes(elem_type);
+ if (TaggedStackInterpreter) {
+ __ movptr(Address(rax_argslot, slot_offset + tag_offset),
+ frame::tag_for_basic_type(elem_type));
+ }
+ slot_offset += Interpreter::stackElementSize();
+ }
+ }
+
+ // Arguments are spread. Move to next method handle.
+ UNPUSH_RSI_RDI;
+ __ movptr(rcx_recv, rcx_mh_vmtarget);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+
+ __ bind(bad_array_klass);
+ UNPUSH_RSI_RDI;
+ __ stop("bad array klass NYI");
+
+ __ bind(bad_array_length);
+ UNPUSH_RSI_RDI;
+ __ stop("bad array length NYI");
+
+#undef UNPUSH_RSI_RDI
+ }
+ break;
+
+ case _adapter_flyby:
+ case _adapter_ricochet:
+ __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
+ break;
+
+ default: ShouldNotReachHere();
+ }
+ __ hlt();
+
+ address me_cookie = MethodHandleEntry::start_compiled_entry(_masm, interp_entry);
+ __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
+
+ init_entry(ek, MethodHandleEntry::finish_compiled_entry(_masm, me_cookie));
+}
--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -2219,6 +2219,16 @@
// arraycopy stubs used by compilers
generate_arraycopy_stubs();
+
+ // generic method handle stubs
+ if (EnableMethodHandles && SystemDictionary::MethodHandle_klass() != NULL) {
+ for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
+ ek < MethodHandles::_EK_LIMIT;
+ ek = MethodHandles::EntryKind(1 + (int)ek)) {
+ StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
+ MethodHandles::generate_method_handle_stub(_masm, ek);
+ }
+ }
}
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -92,6 +92,33 @@
return entry;
}
+// Arguments are: required type at TOS+8, failing object (or NULL) at TOS+4.
+// pc at TOS (just for debugging)
+address TemplateInterpreterGenerator::generate_WrongMethodType_handler() {
+ address entry = __ pc();
+
+ __ pop(rbx); // actual failing object is at TOS
+ __ pop(rax); // required type is at TOS+4
+
+ __ verify_oop(rbx);
+ __ verify_oop(rax);
+
+ // Various method handle types use interpreter registers as temps.
+ __ restore_bcp();
+ __ restore_locals();
+
+ // Expression stack must be empty before entering the VM for an exception.
+ __ empty_expression_stack();
+ __ empty_FPU_stack();
+ __ call_VM(noreg,
+ CAST_FROM_FN_PTR(address,
+ InterpreterRuntime::throw_WrongMethodTypeException),
+ // pass required type, failing object (or NULL)
+ rax, rbx);
+ return entry;
+}
+
+
address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) {
assert(!pass_oop || message == NULL, "either oop or message but not both");
address entry = __ pc();
@@ -1370,6 +1397,7 @@
case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break;
+ case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break;
case Interpreter::java_lang_math_sin : // fall thru
case Interpreter::java_lang_math_cos : // fall thru
@@ -1400,7 +1428,8 @@
// be sure to change this if you add/subtract anything to/from the overhead area
const int overhead_size = -frame::interpreter_frame_initial_sp_offset;
- const int method_stack = (method->max_locals() + method->max_stack()) *
+ const int extra_stack = methodOopDesc::extra_stack_entries();
+ const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) *
Interpreter::stackElementWords();
return overhead_size + method_stack + stub_code;
}
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -100,6 +100,26 @@
return entry;
}
+// Arguments are: required type in rarg1, failing object (or NULL) in rarg2
+address TemplateInterpreterGenerator::generate_WrongMethodType_handler() {
+ address entry = __ pc();
+
+ __ pop(c_rarg2); // failing object is at TOS
+ __ pop(c_rarg1); // required type is at TOS+8
+
+ // expression stack must be empty before entering the VM if an
+ // exception happened
+ __ empty_expression_stack();
+
+ __ call_VM(noreg,
+ CAST_FROM_FN_PTR(address,
+ InterpreterRuntime::
+ throw_WrongMethodTypeException),
+ // pass required type, failing object (or NULL)
+ c_rarg1, c_rarg2);
+ return entry;
+}
+
address TemplateInterpreterGenerator::generate_exception_handler_common(
const char* name, const char* message, bool pass_oop) {
assert(!pass_oop || message == NULL, "either oop or message but not both");
@@ -1393,12 +1413,14 @@
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 : break;
- case Interpreter::java_lang_math_cos : break;
- case Interpreter::java_lang_math_tan : break;
- case Interpreter::java_lang_math_abs : break;
- case Interpreter::java_lang_math_log : break;
- case Interpreter::java_lang_math_log10 : 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
+ case Interpreter::java_lang_math_tan : // fall thru
+ case Interpreter::java_lang_math_abs : // fall thru
+ case Interpreter::java_lang_math_log : // fall thru
+ case Interpreter::java_lang_math_log10 : // fall thru
case Interpreter::java_lang_math_sqrt : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break;
default : ShouldNotReachHere(); break;
}
@@ -1422,7 +1444,8 @@
-(frame::interpreter_frame_initial_sp_offset) + entry_size;
const int stub_code = frame::entry_frame_after_call_words;
- const int method_stack = (method->max_locals() + method->max_stack()) *
+ const int extra_stack = methodOopDesc::extra_stack_entries();
+ const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) *
Interpreter::stackElementWords();
return (overhead_size + method_stack + stub_code);
}
--- a/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_32.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -108,6 +108,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 3; // 32-bit offset is this much larger than an 8-bit one
+ assert(vtable_index > 10 || __ pc() + slop <= s->code_end(), "room for 32-bit offset");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -181,6 +184,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 3; // 32-bit offset is this much larger than an 8-bit one
+ assert(itable_index > 10 || __ pc() + slop <= s->code_end(), "room for 32-bit offset");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -196,6 +202,41 @@
// Itable stub size
return (DebugVtables ? 256 : 66) + (CountCompiledCalls ? 6 : 0);
}
+ // In order to tune these parameters, run the JVM with VM options
+ // +PrintMiscellaneous and +WizardMode to see information about
+ // actual itable stubs. Look for lines like this:
+ // itable #1 at 0x5551212[65] left over: 3
+ // Reduce the constants so that the "left over" number is >=3
+ // for the common cases.
+ // Do not aim at a left-over number of zero, because a
+ // large vtable or itable index (> 16) will require a 32-bit
+ // immediate displacement instead of an 8-bit one.
+ //
+ // The JVM98 app. _202_jess has a megamorphic interface call.
+ // The itable code looks like this:
+ // Decoding VtableStub itbl[1]@1
+ // mov 0x4(%ecx),%esi
+ // mov 0xe8(%esi),%edi
+ // lea 0x130(%esi,%edi,4),%edi
+ // add $0x7,%edi
+ // and $0xfffffff8,%edi
+ // lea 0x4(%esi),%esi
+ // mov (%edi),%ebx
+ // cmp %ebx,%eax
+ // je success
+ // loop:
+ // test %ebx,%ebx
+ // je throw_icce
+ // add $0x8,%edi
+ // mov (%edi),%ebx
+ // cmp %ebx,%eax
+ // jne loop
+ // success:
+ // mov 0x4(%edi),%edi
+ // mov (%esi,%edi,1),%ebx
+ // jmp *0x44(%ebx)
+ // throw_icce:
+ // jmp throw_ICCE_entry
}
int VtableStub::pd_code_alignment() {
--- a/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -106,6 +106,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 3; // 32-bit offset is this much larger than an 8-bit one
+ assert(vtable_index > 10 || __ pc() + slop <= s->code_end(), "room for 32-bit offset");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -191,6 +194,9 @@
(int)(s->code_end() - __ pc()));
}
guarantee(__ pc() <= s->code_end(), "overflowed buffer");
+ // shut the door on sizing bugs
+ int slop = 3; // 32-bit offset is this much larger than an 8-bit one
+ assert(itable_index > 10 || __ pc() + slop <= s->code_end(), "room for 32-bit offset");
s->set_exception_points(npe_addr, ame_addr);
return s;
@@ -206,6 +212,39 @@
return (DebugVtables ? 512 : 72) + (CountCompiledCalls ? 13 : 0) +
(UseCompressedOops ? 32 : 0); // 2 leaqs
}
+ // In order to tune these parameters, run the JVM with VM options
+ // +PrintMiscellaneous and +WizardMode to see information about
+ // actual itable stubs. Look for lines like this:
+ // itable #1 at 0x5551212[71] left over: 3
+ // Reduce the constants so that the "left over" number is >=3
+ // for the common cases.
+ // Do not aim at a left-over number of zero, because a
+ // large vtable or itable index (>= 32) will require a 32-bit
+ // immediate displacement instead of an 8-bit one.
+ //
+ // The JVM98 app. _202_jess has a megamorphic interface call.
+ // The itable code looks like this:
+ // Decoding VtableStub itbl[1]@12
+ // mov 0x8(%rsi),%r10
+ // mov 0x198(%r10),%r11d
+ // lea 0x218(%r10,%r11,8),%r11
+ // lea 0x8(%r10),%r10
+ // mov (%r11),%rbx
+ // cmp %rbx,%rax
+ // je success
+ // loop:
+ // test %rbx,%rbx
+ // je throw_icce
+ // add $0x10,%r11
+ // mov (%r11),%rbx
+ // cmp %rbx,%rax
+ // jne loop
+ // success:
+ // mov 0x8(%r11),%r11d
+ // mov (%r10,%r11,1),%rbx
+ // jmpq *0x60(%rbx)
+ // throw_icce:
+ // jmpq throw_ICCE_entry
}
int VtableStub::pd_code_alignment() {
--- a/hotspot/src/share/vm/ci/ciMethod.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/ci/ciMethod.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -675,6 +675,30 @@
}
// ------------------------------------------------------------------
+// invokedynamic support
+//
+bool ciMethod::is_method_handle_invoke() {
+ check_is_loaded();
+ bool flag = ((flags().as_int() & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS);
+#ifdef ASSERT
+ {
+ VM_ENTRY_MARK;
+ bool flag2 = get_methodOop()->is_method_handle_invoke();
+ assert(flag == flag2, "consistent");
+ }
+#endif //ASSERT
+ return flag;
+}
+
+ciInstance* ciMethod::method_handle_type() {
+ check_is_loaded();
+ VM_ENTRY_MARK;
+ oop mtype = get_methodOop()->method_handle_type();
+ return CURRENT_THREAD_ENV->get_object(mtype)->as_instance();
+}
+
+
+// ------------------------------------------------------------------
// ciMethod::build_method_data
//
// Generate new methodDataOop objects at compile time.
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/ci/ciMethod.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -207,6 +207,8 @@
bool check_call(int refinfo_index, bool is_static) const;
void build_method_data(); // make sure it exists in the VM also
int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC
+ bool is_method_handle_invoke();
+ ciInstance* method_handle_type();
// What kind of ciObject is this?
bool is_method() { return true; }
--- a/hotspot/src/share/vm/classfile/classFileParser.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1842,6 +1842,11 @@
_has_vanilla_constructor = true;
}
+ if (EnableMethodHandles && m->is_method_handle_invoke()) {
+ THROW_MSG_(vmSymbols::java_lang_VirtualMachineError(),
+ "Method handle invokers must be defined internally to the VM", nullHandle);
+ }
+
return m;
}
@@ -2465,9 +2470,84 @@
}
+// Force MethodHandle.vmentry to be an unmanaged pointer.
+// There is no way for a classfile to express this, so we must help it.
+void ClassFileParser::java_dyn_MethodHandle_fix_pre(constantPoolHandle cp,
+ typeArrayHandle* fields_ptr,
+ FieldAllocationCount *fac_ptr,
+ TRAPS) {
+ // Add fake fields for java.dyn.MethodHandle instances
+ //
+ // This is not particularly nice, but since there is no way to express
+ // a native wordSize field in Java, we must do it at this level.
+
+ if (!EnableMethodHandles) return;
+
+ int word_sig_index = 0;
+ const int cp_size = cp->length();
+ for (int index = 1; index < cp_size; index++) {
+ if (cp->tag_at(index).is_utf8() &&
+ cp->symbol_at(index) == vmSymbols::machine_word_signature()) {
+ word_sig_index = index;
+ break;
+ }
+ }
+
+ if (word_sig_index == 0)
+ THROW_MSG(vmSymbols::java_lang_VirtualMachineError(),
+ "missing I or J signature (for vmentry) in java.dyn.MethodHandle");
+
+ bool found_vmentry = false;
+
+ const int n = (*fields_ptr)()->length();
+ for (int i = 0; i < n; i += instanceKlass::next_offset) {
+ int name_index = (*fields_ptr)->ushort_at(i + instanceKlass::name_index_offset);
+ int sig_index = (*fields_ptr)->ushort_at(i + instanceKlass::signature_index_offset);
+ int acc_flags = (*fields_ptr)->ushort_at(i + instanceKlass::access_flags_offset);
+ symbolOop f_name = cp->symbol_at(name_index);
+ symbolOop f_sig = cp->symbol_at(sig_index);
+ if (f_sig == vmSymbols::byte_signature() &&
+ f_name == vmSymbols::vmentry_name() &&
+ (acc_flags & JVM_ACC_STATIC) == 0) {
+ // Adjust the field type from byte to an unmanaged pointer.
+ assert(fac_ptr->nonstatic_byte_count > 0, "");
+ fac_ptr->nonstatic_byte_count -= 1;
+ (*fields_ptr)->ushort_at_put(i + instanceKlass::signature_index_offset,
+ word_sig_index);
+ if (wordSize == jintSize) {
+ fac_ptr->nonstatic_word_count += 1;
+ } else {
+ fac_ptr->nonstatic_double_count += 1;
+ }
+
+ FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i+4);
+ assert(atype == NONSTATIC_BYTE, "");
+ FieldAllocationType new_atype = NONSTATIC_WORD;
+ if (wordSize > jintSize) {
+ if (Universe::field_type_should_be_aligned(T_LONG)) {
+ atype = NONSTATIC_ALIGNED_DOUBLE;
+ } else {
+ atype = NONSTATIC_DOUBLE;
+ }
+ }
+ (*fields_ptr)->ushort_at_put(i+4, new_atype);
+
+ found_vmentry = true;
+ break;
+ }
+ }
+
+ if (!found_vmentry)
+ THROW_MSG(vmSymbols::java_lang_VirtualMachineError(),
+ "missing vmentry byte field in java.dyn.MethodHandle");
+
+}
+
+
instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name,
Handle class_loader,
Handle protection_domain,
+ KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
symbolHandle& parsed_name,
TRAPS) {
@@ -2500,6 +2580,7 @@
}
}
+ _host_klass = host_klass;
_cp_patches = cp_patches;
instanceKlassHandle nullHandle;
@@ -2808,6 +2889,11 @@
java_lang_Class_fix_pre(&methods, &fac, CHECK_(nullHandle));
}
+ // adjust the vmentry field declaration in java.dyn.MethodHandle
+ if (EnableMethodHandles && class_name() == vmSymbols::sun_dyn_MethodHandleImpl() && class_loader.is_null()) {
+ java_dyn_MethodHandle_fix_pre(cp, &fields, &fac, CHECK_(nullHandle));
+ }
+
// Add a fake "discovered" field if it is not present
// for compatibility with earlier jdk's.
if (class_name() == vmSymbols::java_lang_ref_Reference()
@@ -3134,7 +3220,7 @@
this_klass->set_method_ordering(method_ordering());
this_klass->set_initial_method_idnum(methods->length());
this_klass->set_name(cp->klass_name_at(this_class_index));
- if (LinkWellKnownClasses) // I am well known to myself
+ if (LinkWellKnownClasses || is_anonymous()) // I am well known to myself
cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve
this_klass->set_protection_domain(protection_domain());
this_klass->set_fields_annotations(fields_annotations());
--- a/hotspot/src/share/vm/classfile/classFileParser.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
u2 _major_version;
u2 _minor_version;
symbolHandle _class_name;
+ KlassHandle _host_klass;
GrowableArray<Handle>* _cp_patches; // overrides for CP entries
bool _has_finalizer;
@@ -145,6 +146,11 @@
// Adjust the next_nonstatic_oop_offset to place the fake fields
// before any Java fields.
void java_lang_Class_fix_post(int* next_nonstatic_oop_offset);
+ // Adjust the field allocation counts for java.dyn.MethodHandle to add
+ // a fake address (void*) field.
+ void java_dyn_MethodHandle_fix_pre(constantPoolHandle cp,
+ typeArrayHandle* fields_ptr,
+ FieldAllocationCount *fac_ptr, TRAPS);
// Format checker methods
void classfile_parse_error(const char* msg, TRAPS);
@@ -204,6 +210,10 @@
char* skip_over_field_name(char* name, bool slash_ok, unsigned int length);
char* skip_over_field_signature(char* signature, bool void_ok, unsigned int length, TRAPS);
+ bool is_anonymous() {
+ assert(AnonymousClasses || _host_klass.is_null(), "");
+ return _host_klass.not_null();
+ }
bool has_cp_patch_at(int index) {
assert(AnonymousClasses, "");
assert(index >= 0, "oob");
@@ -249,11 +259,13 @@
Handle protection_domain,
symbolHandle& parsed_name,
TRAPS) {
- return parseClassFile(name, class_loader, protection_domain, NULL, parsed_name, THREAD);
+ KlassHandle no_host_klass;
+ return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, THREAD);
}
instanceKlassHandle parseClassFile(symbolHandle name,
Handle class_loader,
Handle protection_domain,
+ KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
symbolHandle& parsed_name,
TRAPS);
--- a/hotspot/src/share/vm/classfile/dictionary.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/dictionary.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -549,6 +549,63 @@
}
}
+SymbolPropertyTable::SymbolPropertyTable(int table_size)
+ : Hashtable(table_size, sizeof(SymbolPropertyEntry))
+{
+}
+SymbolPropertyTable::SymbolPropertyTable(int table_size, HashtableBucket* t,
+ int number_of_entries)
+ : Hashtable(table_size, sizeof(SymbolPropertyEntry), t, number_of_entries)
+{
+}
+
+
+SymbolPropertyEntry* SymbolPropertyTable::find_entry(int index, unsigned int hash,
+ symbolHandle sym) {
+ assert(index == index_for(sym), "incorrect index?");
+ for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) {
+ if (p->hash() == hash && p->symbol() == sym()) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+
+SymbolPropertyEntry* SymbolPropertyTable::add_entry(int index, unsigned int hash,
+ symbolHandle sym) {
+ assert_locked_or_safepoint(SystemDictionary_lock);
+ assert(index == index_for(sym), "incorrect index?");
+ assert(find_entry(index, hash, sym) == NULL, "no double entry");
+
+ SymbolPropertyEntry* p = new_entry(hash, sym());
+ Hashtable::add_entry(index, p);
+ return p;
+}
+
+
+void SymbolPropertyTable::oops_do(OopClosure* f) {
+ for (int index = 0; index < table_size(); index++) {
+ for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) {
+ f->do_oop((oop*) p->symbol_addr());
+ if (p->property_oop() != NULL) {
+ f->do_oop(p->property_oop_addr());
+ }
+ }
+ }
+}
+
+void SymbolPropertyTable::methods_do(void f(methodOop)) {
+ for (int index = 0; index < table_size(); index++) {
+ for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) {
+ oop prop = p->property_oop();
+ if (prop != NULL && prop->is_method()) {
+ f((methodOop)prop);
+ }
+ }
+ }
+}
+
// ----------------------------------------------------------------------------
#ifndef PRODUCT
--- a/hotspot/src/share/vm/classfile/dictionary.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/dictionary.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -217,3 +217,112 @@
tty->print_cr("pd set = #%d", count);
}
};
+
+// Entry in a SymbolPropertyTable, mapping a single symbolOop
+// to a managed and an unmanaged pointer.
+class SymbolPropertyEntry : public HashtableEntry {
+ friend class VMStructs;
+ private:
+ oop _property_oop;
+ address _property_data;
+
+ public:
+ symbolOop symbol() const { return (symbolOop) literal(); }
+
+ oop property_oop() const { return _property_oop; }
+ void set_property_oop(oop p) { _property_oop = p; }
+
+ address property_data() const { return _property_data; }
+ void set_property_data(address p) { _property_data = p; }
+
+ SymbolPropertyEntry* next() const {
+ return (SymbolPropertyEntry*)HashtableEntry::next();
+ }
+
+ SymbolPropertyEntry** next_addr() {
+ return (SymbolPropertyEntry**)HashtableEntry::next_addr();
+ }
+
+ oop* symbol_addr() { return literal_addr(); }
+ oop* property_oop_addr() { return &_property_oop; }
+
+ void print_on(outputStream* st) const {
+ symbol()->print_value_on(st);
+ st->print(" -> ");
+ bool printed = false;
+ if (property_oop() != NULL) {
+ property_oop()->print_value_on(st);
+ printed = true;
+ }
+ if (property_data() != NULL) {
+ if (printed) st->print(" and ");
+ st->print(INTPTR_FORMAT, property_data());
+ printed = true;
+ }
+ st->print_cr(printed ? "" : "(empty)");
+ }
+};
+
+// A system-internal mapping of symbols to pointers, both managed
+// and unmanaged. Used to record the auto-generation of each method
+// MethodHandle.invoke(S)T, for all signatures (S)T.
+class SymbolPropertyTable : public Hashtable {
+ friend class VMStructs;
+private:
+ SymbolPropertyEntry* bucket(int i) {
+ return (SymbolPropertyEntry*) Hashtable::bucket(i);
+ }
+
+ // The following method is not MT-safe and must be done under lock.
+ SymbolPropertyEntry** bucket_addr(int i) {
+ return (SymbolPropertyEntry**) Hashtable::bucket_addr(i);
+ }
+
+ void add_entry(int index, SymbolPropertyEntry* new_entry) {
+ ShouldNotReachHere();
+ }
+ void set_entry(int index, SymbolPropertyEntry* new_entry) {
+ ShouldNotReachHere();
+ }
+
+ SymbolPropertyEntry* new_entry(unsigned int hash, symbolOop symbol) {
+ SymbolPropertyEntry* entry = (SymbolPropertyEntry*) Hashtable::new_entry(hash, symbol);
+ entry->set_property_oop(NULL);
+ entry->set_property_data(NULL);
+ return entry;
+ }
+
+public:
+ SymbolPropertyTable(int table_size);
+ SymbolPropertyTable(int table_size, HashtableBucket* t, int number_of_entries);
+
+ void free_entry(SymbolPropertyEntry* entry) {
+ Hashtable::free_entry(entry);
+ }
+
+ unsigned int compute_hash(symbolHandle sym) {
+ // Use the regular identity_hash.
+ return Hashtable::compute_hash(sym);
+ }
+
+ // need not be locked; no state change
+ SymbolPropertyEntry* find_entry(int index, unsigned int hash, symbolHandle name);
+
+ // must be done under SystemDictionary_lock
+ SymbolPropertyEntry* add_entry(int index, unsigned int hash, symbolHandle name);
+
+ // GC support
+ void oops_do(OopClosure* f);
+ void methods_do(void f(methodOop));
+
+ // Sharing support
+ void dump(SerializeOopClosure* soc);
+ void restore(SerializeOopClosure* soc);
+ void reorder_dictionary();
+
+#ifndef PRODUCT
+ void print();
+#endif
+ void verify();
+};
+
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -25,13 +25,24 @@
# include "incls/_precompiled.incl"
# include "incls/_javaClasses.cpp.incl"
+static bool find_field(instanceKlass* ik,
+ symbolOop name_symbol, symbolOop signature_symbol,
+ fieldDescriptor* fd,
+ bool allow_super = false) {
+ if (allow_super)
+ return ik->find_field(name_symbol, signature_symbol, fd) != NULL;
+ else
+ return ik->find_local_field(name_symbol, signature_symbol, fd);
+}
+
// Helpful routine for computing field offsets at run time rather than hardcoding them
static void
compute_offset(int &dest_offset,
- klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol) {
+ klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol,
+ bool allow_super = false) {
fieldDescriptor fd;
instanceKlass* ik = instanceKlass::cast(klass_oop);
- if (!ik->find_local_field(name_symbol, signature_symbol, &fd)) {
+ if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
ResourceMark rm;
tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
fatal("Invalid layout of preloaded class");
@@ -42,14 +53,16 @@
// Same as above but for "optional" offsets that might not be present in certain JDK versions
static void
compute_optional_offset(int& dest_offset,
- klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol) {
+ klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol,
+ bool allow_super = false) {
fieldDescriptor fd;
instanceKlass* ik = instanceKlass::cast(klass_oop);
- if (ik->find_local_field(name_symbol, signature_symbol, &fd)) {
+ if (find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
dest_offset = fd.offset();
}
}
+
Handle java_lang_String::basic_create(int length, bool tenured, TRAPS) {
// Create the String object first, so there's a chance that the String
// and the char array it points to end up in the same cache line.
@@ -2107,13 +2120,324 @@
}
+// Support for java_dyn_MethodHandle
+
+int java_dyn_MethodHandle::_type_offset;
+int java_dyn_MethodHandle::_vmtarget_offset;
+int java_dyn_MethodHandle::_vmentry_offset;
+int java_dyn_MethodHandle::_vmslots_offset;
+
+int sun_dyn_MemberName::_clazz_offset;
+int sun_dyn_MemberName::_name_offset;
+int sun_dyn_MemberName::_type_offset;
+int sun_dyn_MemberName::_flags_offset;
+int sun_dyn_MemberName::_vmtarget_offset;
+int sun_dyn_MemberName::_vmindex_offset;
+
+int sun_dyn_DirectMethodHandle::_vmindex_offset;
+
+int sun_dyn_BoundMethodHandle::_argument_offset;
+int sun_dyn_BoundMethodHandle::_vmargslot_offset;
+
+int sun_dyn_AdapterMethodHandle::_conversion_offset;
+
+void java_dyn_MethodHandle::compute_offsets() {
+ klassOop k = SystemDictionary::MethodHandle_klass();
+ if (k != NULL && EnableMethodHandles) {
+ compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::java_dyn_MethodType_signature(), true);
+ compute_offset(_vmtarget_offset, k, vmSymbols::vmtarget_name(), vmSymbols::object_signature(), true);
+ compute_offset(_vmentry_offset, k, vmSymbols::vmentry_name(), vmSymbols::machine_word_signature(), true);
+
+ // Note: MH.vmslots (if it is present) is a hoisted copy of MH.type.form.vmslots.
+ // It is optional pending experiments to keep or toss.
+ compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
+ }
+}
+
+void sun_dyn_MemberName::compute_offsets() {
+ klassOop k = SystemDictionary::MemberName_klass();
+ if (k != NULL && EnableMethodHandles) {
+ compute_offset(_clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature());
+ compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature());
+ compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::object_signature());
+ compute_offset(_flags_offset, k, vmSymbols::flags_name(), vmSymbols::int_signature());
+ compute_offset(_vmtarget_offset, k, vmSymbols::vmtarget_name(), vmSymbols::object_signature());
+ compute_offset(_vmindex_offset, k, vmSymbols::vmindex_name(), vmSymbols::int_signature());
+ }
+}
+
+void sun_dyn_DirectMethodHandle::compute_offsets() {
+ klassOop k = SystemDictionary::DirectMethodHandle_klass();
+ if (k != NULL && EnableMethodHandles) {
+ compute_offset(_vmindex_offset, k, vmSymbols::vmindex_name(), vmSymbols::int_signature(), true);
+ }
+}
+
+void sun_dyn_BoundMethodHandle::compute_offsets() {
+ klassOop k = SystemDictionary::BoundMethodHandle_klass();
+ if (k != NULL && EnableMethodHandles) {
+ compute_offset(_vmargslot_offset, k, vmSymbols::vmargslot_name(), vmSymbols::int_signature(), true);
+ compute_offset(_argument_offset, k, vmSymbols::argument_name(), vmSymbols::object_signature(), true);
+ }
+}
+
+void sun_dyn_AdapterMethodHandle::compute_offsets() {
+ klassOop k = SystemDictionary::AdapterMethodHandle_klass();
+ if (k != NULL && EnableMethodHandles) {
+ compute_offset(_conversion_offset, k, vmSymbols::conversion_name(), vmSymbols::int_signature(), true);
+ }
+}
+
+oop java_dyn_MethodHandle::type(oop mh) {
+ return mh->obj_field(_type_offset);
+}
+
+void java_dyn_MethodHandle::set_type(oop mh, oop mtype) {
+ mh->obj_field_put(_type_offset, mtype);
+}
+
+int java_dyn_MethodHandle::vmslots(oop mh) {
+ int vmslots_offset = _vmslots_offset;
+ if (vmslots_offset != 0) {
+#ifdef ASSERT
+ int x = mh->int_field(vmslots_offset);
+ int y = compute_vmslots(mh);
+ assert(x == y, "correct hoisted value");
+#endif
+ return mh->int_field(vmslots_offset);
+ } else {
+ return compute_vmslots(mh);
+ }
+}
+
+// if MH.vmslots exists, hoist into it the value of type.form.vmslots
+void java_dyn_MethodHandle::init_vmslots(oop mh) {
+ int vmslots_offset = _vmslots_offset;
+ if (vmslots_offset != 0) {
+ mh->int_field_put(vmslots_offset, compute_vmslots(mh));
+ }
+}
+
+// fetch type.form.vmslots, which is the number of JVM stack slots
+// required to carry the arguments of this MH
+int java_dyn_MethodHandle::compute_vmslots(oop mh) {
+ oop mtype = type(mh);
+ if (mtype == NULL) return 0; // Java code would get NPE
+ oop form = java_dyn_MethodType::form(mtype);
+ if (form == NULL) return 0; // Java code would get NPE
+ return java_dyn_MethodTypeForm::vmslots(form);
+}
+
+// fetch the low-level entry point for this mh
+MethodHandleEntry* java_dyn_MethodHandle::vmentry(oop mh) {
+ return (MethodHandleEntry*) mh->address_field(_vmentry_offset);
+}
+
+void java_dyn_MethodHandle::set_vmentry(oop mh, MethodHandleEntry* me) {
+ assert(_vmentry_offset != 0, "must be present");
+
+ // This is always the final step that initializes a valid method handle:
+ mh->release_address_field_put(_vmentry_offset, (address) me);
+
+ // There should be enough memory barriers on exit from native methods
+ // to ensure that the MH is fully initialized to all threads before
+ // Java code can publish it in global data structures.
+ // But just in case, we use release_address_field_put.
+}
+
+/// MemberName accessors
+
+oop sun_dyn_MemberName::clazz(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->obj_field(_clazz_offset);
+}
+
+void sun_dyn_MemberName::set_clazz(oop mname, oop clazz) {
+ assert(is_instance(mname), "wrong type");
+ mname->obj_field_put(_clazz_offset, clazz);
+}
+
+oop sun_dyn_MemberName::name(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->obj_field(_name_offset);
+}
+
+void sun_dyn_MemberName::set_name(oop mname, oop name) {
+ assert(is_instance(mname), "wrong type");
+ mname->obj_field_put(_name_offset, name);
+}
+
+oop sun_dyn_MemberName::type(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->obj_field(_type_offset);
+}
+
+void sun_dyn_MemberName::set_type(oop mname, oop type) {
+ assert(is_instance(mname), "wrong type");
+ mname->obj_field_put(_type_offset, type);
+}
+
+int sun_dyn_MemberName::flags(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->int_field(_flags_offset);
+}
+
+void sun_dyn_MemberName::set_flags(oop mname, int flags) {
+ assert(is_instance(mname), "wrong type");
+ mname->int_field_put(_flags_offset, flags);
+}
+
+oop sun_dyn_MemberName::vmtarget(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->obj_field(_vmtarget_offset);
+}
+
+void sun_dyn_MemberName::set_vmtarget(oop mname, oop ref) {
+ assert(is_instance(mname), "wrong type");
+ mname->obj_field_put(_vmtarget_offset, ref);
+}
+
+int sun_dyn_MemberName::vmindex(oop mname) {
+ assert(is_instance(mname), "wrong type");
+ return mname->int_field(_vmindex_offset);
+}
+
+void sun_dyn_MemberName::set_vmindex(oop mname, int index) {
+ assert(is_instance(mname), "wrong type");
+ mname->int_field_put(_vmindex_offset, index);
+}
+
+oop java_dyn_MethodHandle::vmtarget(oop mh) {
+ assert(is_instance(mh), "MH only");
+ return mh->obj_field(_vmtarget_offset);
+}
+
+void java_dyn_MethodHandle::set_vmtarget(oop mh, oop ref) {
+ assert(is_instance(mh), "MH only");
+ mh->obj_field_put(_vmtarget_offset, ref);
+}
+
+int sun_dyn_DirectMethodHandle::vmindex(oop mh) {
+ assert(is_instance(mh), "DMH only");
+ return mh->int_field(_vmindex_offset);
+}
+
+void sun_dyn_DirectMethodHandle::set_vmindex(oop mh, int index) {
+ assert(is_instance(mh), "DMH only");
+ mh->int_field_put(_vmindex_offset, index);
+}
+
+int sun_dyn_BoundMethodHandle::vmargslot(oop mh) {
+ assert(is_instance(mh), "BMH only");
+ return mh->int_field(_vmargslot_offset);
+}
+
+oop sun_dyn_BoundMethodHandle::argument(oop mh) {
+ assert(is_instance(mh), "BMH only");
+ return mh->obj_field(_argument_offset);
+}
+
+int sun_dyn_AdapterMethodHandle::conversion(oop mh) {
+ assert(is_instance(mh), "AMH only");
+ return mh->int_field(_conversion_offset);
+}
+
+void sun_dyn_AdapterMethodHandle::set_conversion(oop mh, int conv) {
+ assert(is_instance(mh), "AMH only");
+ mh->int_field_put(_conversion_offset, conv);
+}
+
+
+// Support for java_dyn_MethodType
+
+int java_dyn_MethodType::_rtype_offset;
+int java_dyn_MethodType::_ptypes_offset;
+int java_dyn_MethodType::_form_offset;
+
+void java_dyn_MethodType::compute_offsets() {
+ klassOop k = SystemDictionary::MethodType_klass();
+ if (k != NULL) {
+ compute_offset(_rtype_offset, k, vmSymbols::rtype_name(), vmSymbols::class_signature());
+ compute_offset(_ptypes_offset, k, vmSymbols::ptypes_name(), vmSymbols::class_array_signature());
+ compute_offset(_form_offset, k, vmSymbols::form_name(), vmSymbols::java_dyn_MethodTypeForm_signature());
+ }
+}
+
+void java_dyn_MethodType::print_signature(oop mt, outputStream* st) {
+ st->print("(");
+ objArrayOop pts = ptypes(mt);
+ for (int i = 0, limit = pts->length(); i < limit; i++) {
+ java_lang_Class::print_signature(pts->obj_at(i), st);
+ }
+ st->print(")");
+ java_lang_Class::print_signature(rtype(mt), st);
+}
+
+symbolOop java_dyn_MethodType::as_signature(oop mt, bool intern_if_not_found, TRAPS) {
+ ResourceMark rm;
+ stringStream buffer(128);
+ print_signature(mt, &buffer);
+ const char* sigstr = buffer.base();
+ int siglen = (int) buffer.size();
+ if (!intern_if_not_found)
+ return SymbolTable::probe(sigstr, siglen);
+ else
+ return oopFactory::new_symbol(sigstr, siglen, THREAD);
+}
+
+oop java_dyn_MethodType::rtype(oop mt) {
+ assert(is_instance(mt), "must be a MethodType");
+ return mt->obj_field(_rtype_offset);
+}
+
+objArrayOop java_dyn_MethodType::ptypes(oop mt) {
+ assert(is_instance(mt), "must be a MethodType");
+ return (objArrayOop) mt->obj_field(_ptypes_offset);
+}
+
+oop java_dyn_MethodType::form(oop mt) {
+ assert(is_instance(mt), "must be a MethodType");
+ return mt->obj_field(_form_offset);
+}
+
+oop java_dyn_MethodType::ptype(oop mt, int idx) {
+ return ptypes(mt)->obj_at(idx);
+}
+
+
+
+// Support for java_dyn_MethodTypeForm
+
+int java_dyn_MethodTypeForm::_vmslots_offset;
+int java_dyn_MethodTypeForm::_erasedType_offset;
+
+void java_dyn_MethodTypeForm::compute_offsets() {
+ klassOop k = SystemDictionary::MethodTypeForm_klass();
+ if (k != NULL) {
+ compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
+ compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_dyn_MethodType_signature(), true);
+ }
+}
+
+int java_dyn_MethodTypeForm::vmslots(oop mtform) {
+ assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
+ return mtform->int_field(_vmslots_offset);
+}
+
+oop java_dyn_MethodTypeForm::erasedType(oop mtform) {
+ assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
+ return mtform->obj_field(_erasedType_offset);
+}
+
+
+
+
// Support for java_security_AccessControlContext
int java_security_AccessControlContext::_context_offset = 0;
int java_security_AccessControlContext::_privilegedContext_offset = 0;
int java_security_AccessControlContext::_isPrivileged_offset = 0;
-
void java_security_AccessControlContext::compute_offsets() {
assert(_isPrivileged_offset == 0, "offsets should be initialized only once");
fieldDescriptor fd;
@@ -2442,6 +2766,15 @@
java_lang_System::compute_offsets();
java_lang_Thread::compute_offsets();
java_lang_ThreadGroup::compute_offsets();
+ if (EnableMethodHandles) {
+ java_dyn_MethodHandle::compute_offsets();
+ sun_dyn_MemberName::compute_offsets();
+ sun_dyn_DirectMethodHandle::compute_offsets();
+ sun_dyn_BoundMethodHandle::compute_offsets();
+ sun_dyn_AdapterMethodHandle::compute_offsets();
+ java_dyn_MethodType::compute_offsets();
+ java_dyn_MethodTypeForm::compute_offsets();
+ }
java_security_AccessControlContext::compute_offsets();
// Initialize reflection classes. The layouts of these classes
// changed with the new reflection implementation in JDK 1.4, and
@@ -2459,6 +2792,9 @@
sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets();
}
sun_misc_AtomicLongCSImpl::compute_offsets();
+
+ // generated interpreter code wants to know about the offsets we just computed:
+ AbstractAssembler::update_delayed_values();
}
#ifndef PRODUCT
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -151,6 +151,12 @@
// Conversion
static klassOop as_klassOop(oop java_class);
static BasicType as_BasicType(oop java_class, klassOop* reference_klass = NULL);
+ static BasicType as_BasicType(oop java_class, KlassHandle* reference_klass) {
+ klassOop refk_oop = NULL;
+ BasicType result = as_BasicType(java_class, &refk_oop);
+ (*reference_klass) = KlassHandle(refk_oop);
+ return result;
+ }
static symbolOop as_signature(oop java_class, bool intern_if_not_found, TRAPS);
static void print_signature(oop java_class, outputStream *st);
// Testing
@@ -778,6 +784,284 @@
};
+// Interface to java.dyn.MethodHandle objects
+
+class MethodHandleEntry;
+
+class java_dyn_MethodHandle: AllStatic {
+ friend class JavaClasses;
+
+ private:
+ static int _vmentry_offset; // assembly code trampoline for MH
+ static int _vmtarget_offset; // class-specific target reference
+ static int _type_offset; // the MethodType of this MH
+ static int _vmslots_offset; // OPTIONAL hoisted type.form.vmslots
+
+ static void compute_offsets();
+
+ public:
+ // Accessors
+ static oop type(oop mh);
+ static void set_type(oop mh, oop mtype);
+
+ static oop vmtarget(oop mh);
+ static void set_vmtarget(oop mh, oop target);
+
+ static MethodHandleEntry* vmentry(oop mh);
+ static void set_vmentry(oop mh, MethodHandleEntry* data);
+
+ static int vmslots(oop mh);
+ static void init_vmslots(oop mh);
+ static int compute_vmslots(oop mh);
+
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::MethodHandle_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
+ // Accessors for code generation:
+ static int type_offset_in_bytes() { return _type_offset; }
+ static int vmtarget_offset_in_bytes() { return _vmtarget_offset; }
+ static int vmentry_offset_in_bytes() { return _vmentry_offset; }
+ static int vmslots_offset_in_bytes() { return _vmslots_offset; }
+};
+
+class sun_dyn_DirectMethodHandle: public java_dyn_MethodHandle {
+ friend class JavaClasses;
+
+ private:
+ // _vmtarget_offset; // method or class or interface
+ static int _vmindex_offset; // negative or vtable idx or itable idx
+ static void compute_offsets();
+
+ public:
+ // Accessors
+ static int vmindex(oop mh);
+ static void set_vmindex(oop mh, int index);
+
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::DirectMethodHandle_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
+ // Accessors for code generation:
+ static int vmindex_offset_in_bytes() { return _vmindex_offset; }
+};
+
+class sun_dyn_BoundMethodHandle: public java_dyn_MethodHandle {
+ friend class JavaClasses;
+
+ private:
+ static int _argument_offset; // argument value bound into this MH
+ static int _vmargslot_offset; // relevant argument slot (<= vmslots)
+ static void compute_offsets();
+
+public:
+ static oop argument(oop mh);
+ static void set_argument(oop mh, oop ref);
+
+ static jint vmargslot(oop mh);
+ static void set_vmargslot(oop mh, jint slot);
+
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::BoundMethodHandle_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
+ static int argument_offset_in_bytes() { return _argument_offset; }
+ static int vmargslot_offset_in_bytes() { return _vmargslot_offset; }
+};
+
+class sun_dyn_AdapterMethodHandle: public sun_dyn_BoundMethodHandle {
+ friend class JavaClasses;
+
+ private:
+ static int _conversion_offset; // type of conversion to apply
+ static void compute_offsets();
+
+ public:
+ static int conversion(oop mh);
+ static void set_conversion(oop mh, int conv);
+
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::AdapterMethodHandle_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
+ // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants):
+ enum {
+ OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
+ OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument
+ OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another
+ OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive
+ OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI)
+ OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg)
+ OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg)
+ OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS)
+ OP_DROP_ARGS = 0x8, // remove one or more argument slots
+ OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI)
+ OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size)
+ OP_FLYBY = 0xB, // operate first on reified argument list (NYI)
+ OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI)
+ CONV_OP_LIMIT = 0xD, // limit of CONV_OP enumeration
+
+ CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
+ CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
+ CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
+ CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
+ CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
+ CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
+ CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
+ CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1
+ };
+
+ static int conversion_offset_in_bytes() { return _conversion_offset; }
+};
+
+
+// Interface to sun.dyn.MemberName objects
+// (These are a private interface for Java code to query the class hierarchy.)
+
+class sun_dyn_MemberName: AllStatic {
+ friend class JavaClasses;
+
+ private:
+ // From java.dyn.MemberName:
+ // private Class<?> clazz; // class in which the method is defined
+ // private String name; // may be null if not yet materialized
+ // private Object type; // may be null if not yet materialized
+ // private int flags; // modifier bits; see reflect.Modifier
+ // private Object vmtarget; // VM-specific target value
+ // private int vmindex; // method index within class or interface
+ static int _clazz_offset;
+ static int _name_offset;
+ static int _type_offset;
+ static int _flags_offset;
+ static int _vmtarget_offset;
+ static int _vmindex_offset;
+
+ static void compute_offsets();
+
+ public:
+ // Accessors
+ static oop clazz(oop mname);
+ static void set_clazz(oop mname, oop clazz);
+
+ static oop type(oop mname);
+ static void set_type(oop mname, oop type);
+
+ static oop name(oop mname);
+ static void set_name(oop mname, oop name);
+
+ static int flags(oop mname);
+ static void set_flags(oop mname, int flags);
+
+ static int modifiers(oop mname) { return (u2) flags(mname); }
+ static void set_modifiers(oop mname, int mods)
+ { set_flags(mname, (flags(mname) &~ (u2)-1) | (u2)mods); }
+
+ static oop vmtarget(oop mname);
+ static void set_vmtarget(oop mname, oop target);
+
+ static int vmindex(oop mname);
+ static void set_vmindex(oop mname, int index);
+
+ // Testers
+ static bool is_subclass(klassOop klass) {
+ return Klass::cast(klass)->is_subclass_of(SystemDictionary::MemberName_klass());
+ }
+ static bool is_instance(oop obj) {
+ return obj != NULL && is_subclass(obj->klass());
+ }
+
+ // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants):
+ enum {
+ MN_IS_METHOD = 0x00010000, // method (not constructor)
+ MN_IS_CONSTRUCTOR = 0x00020000, // constructor
+ MN_IS_FIELD = 0x00040000, // field
+ MN_IS_TYPE = 0x00080000, // nested type
+ MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
+ MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
+ VM_INDEX_UNINITIALIZED = -99
+ };
+
+ // Accessors for code generation:
+ static int clazz_offset_in_bytes() { return _clazz_offset; }
+ static int type_offset_in_bytes() { return _type_offset; }
+ static int name_offset_in_bytes() { return _name_offset; }
+ static int flags_offset_in_bytes() { return _flags_offset; }
+ static int vmtarget_offset_in_bytes() { return _vmtarget_offset; }
+ static int vmindex_offset_in_bytes() { return _vmindex_offset; }
+};
+
+
+// Interface to java.dyn.MethodType objects
+
+class java_dyn_MethodType: AllStatic {
+ friend class JavaClasses;
+
+ private:
+ static int _rtype_offset;
+ static int _ptypes_offset;
+ static int _form_offset;
+
+ static void compute_offsets();
+
+ public:
+ // Accessors
+ static oop rtype(oop mt);
+ static objArrayOop ptypes(oop mt);
+ static oop form(oop mt);
+
+ static oop ptype(oop mt, int index);
+
+ static symbolOop as_signature(oop mt, bool intern_if_not_found, TRAPS);
+ static void print_signature(oop mt, outputStream* st);
+
+ static bool is_instance(oop obj) {
+ return obj != NULL && obj->klass() == SystemDictionary::MethodType_klass();
+ }
+
+ // Accessors for code generation:
+ static int rtype_offset_in_bytes() { return _rtype_offset; }
+ static int ptypes_offset_in_bytes() { return _ptypes_offset; }
+ static int form_offset_in_bytes() { return _form_offset; }
+};
+
+class java_dyn_MethodTypeForm: AllStatic {
+ friend class JavaClasses;
+
+ private:
+ static int _vmslots_offset; // number of argument slots needed
+ static int _erasedType_offset; // erasedType = canonical MethodType
+
+ static void compute_offsets();
+
+ public:
+ // Accessors
+ static int vmslots(oop mtform);
+ static oop erasedType(oop mtform);
+
+ // Accessors for code generation:
+ static int vmslots_offset_in_bytes() { return _vmslots_offset; }
+ static int erasedType_offset_in_bytes() { return _erasedType_offset; }
+};
+
+
+
+
// Interface to java.security.AccessControlContext objects
class java_security_AccessControlContext: AllStatic {
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -31,6 +31,7 @@
Dictionary* SystemDictionary::_shared_dictionary = NULL;
LoaderConstraintTable* SystemDictionary::_loader_constraints = NULL;
ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL;
+SymbolPropertyTable* SystemDictionary::_invoke_method_table = NULL;
int SystemDictionary::_number_of_modifications = 0;
@@ -966,6 +967,8 @@
instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
class_loader,
protection_domain,
+ host_klass,
+ cp_patches,
parsed_name,
THREAD);
@@ -1691,6 +1694,10 @@
// represent classes we're actively loading.
placeholders_do(blk);
+ // Visit extra methods
+ if (invoke_method_table() != NULL)
+ invoke_method_table()->oops_do(blk);
+
// Loader constraints. We must keep the symbolOop used in the name alive.
constraints()->always_strong_classes_do(blk);
@@ -1726,6 +1733,10 @@
// Adjust dictionary
dictionary()->oops_do(f);
+ // Visit extra methods
+ if (invoke_method_table() != NULL)
+ invoke_method_table()->oops_do(f);
+
// Partially loaded classes
placeholders()->oops_do(f);
@@ -1798,6 +1809,8 @@
void SystemDictionary::methods_do(void f(methodOop)) {
dictionary()->methods_do(f);
+ if (invoke_method_table() != NULL)
+ invoke_method_table()->methods_do(f);
}
// ----------------------------------------------------------------------------
@@ -1830,6 +1843,7 @@
_number_of_modifications = 0;
_loader_constraints = new LoaderConstraintTable(_loader_constraint_size);
_resolution_errors = new ResolutionErrorTable(_resolution_error_size);
+ // _invoke_method_table is allocated lazily in find_method_handle_invoke()
// Allocate private object used as system class loader lock
_system_loader_lock_obj = oopFactory::new_system_objArray(0, CHECK);
@@ -1891,6 +1905,9 @@
wk_klass_name_limits[0] = s;
}
}
+
+ // move the starting value forward to the limit:
+ start_id = limit_id;
}
@@ -1924,6 +1941,17 @@
instanceKlass::cast(WK_KLASS(final_reference_klass))->set_reference_type(REF_FINAL);
instanceKlass::cast(WK_KLASS(phantom_reference_klass))->set_reference_type(REF_PHANTOM);
+ WKID meth_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass);
+ WKID meth_group_end = WK_KLASS_ENUM_NAME(WrongMethodTypeException_klass);
+ initialize_wk_klasses_until(meth_group_start, scan, CHECK);
+ if (EnableMethodHandles) {
+ initialize_wk_klasses_through(meth_group_start, scan, CHECK);
+ }
+ if (_well_known_klasses[meth_group_start] == NULL) {
+ // Skip the rest of the method handle classes, if MethodHandle is not loaded.
+ scan = WKID(meth_group_end+1);
+ }
+
initialize_wk_klasses_until(WKID_LIMIT, scan, CHECK);
_box_klasses[T_BOOLEAN] = WK_KLASS(boolean_klass);
@@ -2254,6 +2282,91 @@
}
+methodOop SystemDictionary::find_method_handle_invoke(symbolHandle signature,
+ Handle class_loader,
+ Handle protection_domain,
+ TRAPS) {
+ if (!EnableMethodHandles) return NULL;
+ assert(class_loader.is_null() && protection_domain.is_null(),
+ "cannot load specialized versions of MethodHandle.invoke");
+ if (invoke_method_table() == NULL) {
+ // create this side table lazily
+ _invoke_method_table = new SymbolPropertyTable(_invoke_method_size);
+ }
+ unsigned int hash = invoke_method_table()->compute_hash(signature);
+ int index = invoke_method_table()->hash_to_index(hash);
+ SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature);
+ if (spe == NULL || spe->property_oop() == NULL) {
+ // Must create lots of stuff here, but outside of the SystemDictionary lock.
+ Handle mt = compute_method_handle_type(signature(),
+ class_loader, protection_domain,
+ CHECK_NULL);
+ KlassHandle mh_klass = SystemDictionaryHandles::MethodHandle_klass();
+ methodHandle m = methodOopDesc::make_invoke_method(mh_klass, signature,
+ mt, CHECK_NULL);
+ // Now grab the lock. We might have to throw away the new method,
+ // if a racing thread has managed to install one at the same time.
+ {
+ MutexLocker ml(SystemDictionary_lock, Thread::current());
+ spe = invoke_method_table()->find_entry(index, hash, signature);
+ if (spe == NULL)
+ spe = invoke_method_table()->add_entry(index, hash, signature);
+ if (spe->property_oop() == NULL)
+ spe->set_property_oop(m());
+ }
+ }
+ methodOop m = (methodOop) spe->property_oop();
+ assert(m->is_method(), "");
+ return m;
+}
+
+// Ask Java code to find or construct a java.dyn.MethodType for the given
+// signature, as interpreted relative to the given class loader.
+// Because of class loader constraints, all method handle usage must be
+// consistent with this loader.
+Handle SystemDictionary::compute_method_handle_type(symbolHandle signature,
+ Handle class_loader,
+ Handle protection_domain,
+ TRAPS) {
+ Handle empty;
+ int npts = ArgumentCount(signature()).size();
+ objArrayHandle pts = oopFactory::new_objArray(SystemDictionary::class_klass(), npts, CHECK_(empty));
+ int arg = 0;
+ Handle rt; // the return type from the signature
+ for (SignatureStream ss(signature()); !ss.is_done(); ss.next()) {
+ oop mirror;
+ if (!ss.is_object()) {
+ mirror = Universe::java_mirror(ss.type());
+ } else {
+ symbolOop name_oop = ss.as_symbol(CHECK_(empty));
+ symbolHandle name(THREAD, name_oop);
+ klassOop klass = resolve_or_fail(name,
+ class_loader, protection_domain,
+ true, CHECK_(empty));
+ mirror = Klass::cast(klass)->java_mirror();
+ }
+ if (ss.at_return_type())
+ rt = Handle(THREAD, mirror);
+ else
+ pts->obj_at_put(arg++, mirror);
+ }
+ assert(arg == npts, "");
+
+ // call MethodType java.dyn.MethodType::makeImpl(Class rt, Class[] pts, false, true)
+ bool varargs = false, trusted = true;
+ JavaCallArguments args(Handle(THREAD, rt()));
+ args.push_oop(pts());
+ args.push_int(false);
+ args.push_int(trusted);
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_static(&result,
+ SystemDictionary::MethodType_klass(),
+ vmSymbols::makeImpl_name(), vmSymbols::makeImpl_signature(),
+ &args, CHECK_(empty));
+ return Handle(THREAD, (oop) result.get_jobject());
+}
+
+
// Since the identity hash code for symbols changes when the symbols are
// moved from the regular perm gen (hash in the mark word) to the shared
// spaces (hash is the address), the classes loaded into the dictionary
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -63,6 +63,7 @@
class LoaderConstraintTable;
class HashtableBucket;
class ResolutionErrorTable;
+class SymbolPropertyTable;
// Certain classes are preloaded, such as java.lang.Object and java.lang.String.
// They are all "well-known", in the sense that no class loader is allowed
@@ -131,6 +132,16 @@
template(reflect_constant_pool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \
template(reflect_unsafe_static_field_accessor_impl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \
\
+ /* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \
+ template(MethodHandle_klass, java_dyn_MethodHandle, Opt) \
+ template(MemberName_klass, sun_dyn_MemberName, Opt) \
+ template(MethodHandleImpl_klass, sun_dyn_MethodHandleImpl, Opt) \
+ template(AdapterMethodHandle_klass, sun_dyn_AdapterMethodHandle, Opt) \
+ template(BoundMethodHandle_klass, sun_dyn_BoundMethodHandle, Opt) \
+ template(DirectMethodHandle_klass, sun_dyn_DirectMethodHandle, Opt) \
+ template(MethodType_klass, java_dyn_MethodType, Opt) \
+ template(MethodTypeForm_klass, java_dyn_MethodTypeForm, Opt) \
+ template(WrongMethodTypeException_klass, java_dyn_WrongMethodTypeException, Opt) \
template(vector_klass, java_util_Vector, Pre) \
template(hashtable_klass, java_util_Hashtable, Pre) \
template(stringBuffer_klass, java_lang_StringBuffer, Pre) \
@@ -444,6 +455,17 @@
static char* check_signature_loaders(symbolHandle signature, Handle loader1,
Handle loader2, bool is_method, TRAPS);
+ // JSR 292
+ // find the java.dyn.MethodHandles::invoke method for a given signature
+ static methodOop find_method_handle_invoke(symbolHandle signature,
+ Handle class_loader,
+ Handle protection_domain,
+ TRAPS);
+ // ask Java to compute the java.dyn.MethodType object for a given signature
+ static Handle compute_method_handle_type(symbolHandle signature,
+ Handle class_loader,
+ Handle protection_domain,
+ TRAPS);
// Utility for printing loader "name" as part of tracing constraints
static const char* loader_name(oop loader) {
return ((loader) == NULL ? "<bootloader>" :
@@ -460,6 +482,7 @@
enum Constants {
_loader_constraint_size = 107, // number of entries in constraint table
_resolution_error_size = 107, // number of entries in resolution error table
+ _invoke_method_size = 139, // number of entries in invoke method table
_nof_buckets = 1009 // number of buckets in hash table
};
@@ -489,6 +512,9 @@
// Resolution errors
static ResolutionErrorTable* _resolution_errors;
+ // Invoke methods (JSR 292)
+ static SymbolPropertyTable* _invoke_method_table;
+
public:
// for VM_CounterDecay iteration support
friend class CounterDecay;
@@ -506,6 +532,7 @@
static PlaceholderTable* placeholders() { return _placeholders; }
static LoaderConstraintTable* constraints() { return _loader_constraints; }
static ResolutionErrorTable* resolution_errors() { return _resolution_errors; }
+ static SymbolPropertyTable* invoke_method_table() { return _invoke_method_table; }
// Basic loading operations
static klassOop resolve_instance_class_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS);
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -216,7 +216,26 @@
template(sun_reflect_UnsafeStaticFieldAccessorImpl, "sun/reflect/UnsafeStaticFieldAccessorImpl")\
template(base_name, "base") \
\
- /* common method names */ \
+ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \
+ template(java_dyn_MethodHandle, "java/dyn/MethodHandle") \
+ template(java_dyn_MethodType, "java/dyn/MethodType") \
+ template(java_dyn_WrongMethodTypeException, "java/dyn/WrongMethodTypeException") \
+ template(java_dyn_MethodType_signature, "Ljava/dyn/MethodType;") \
+ template(java_dyn_MethodHandle_signature, "Ljava/dyn/MethodHandle;") \
+ /* internal classes known only to the JVM: */ \
+ template(java_dyn_MethodTypeForm, "java/dyn/MethodTypeForm") \
+ template(java_dyn_MethodTypeForm_signature, "Ljava/dyn/MethodTypeForm;") \
+ template(sun_dyn_MemberName, "sun/dyn/MemberName") \
+ template(sun_dyn_MethodHandleImpl, "sun/dyn/MethodHandleImpl") \
+ template(sun_dyn_AdapterMethodHandle, "sun/dyn/AdapterMethodHandle") \
+ template(sun_dyn_BoundMethodHandle, "sun/dyn/BoundMethodHandle") \
+ template(sun_dyn_DirectMethodHandle, "sun/dyn/DirectMethodHandle") \
+ template(makeImpl_name, "makeImpl") /*MethodType::makeImpl*/ \
+ template(makeImpl_signature, "(Ljava/lang/Class;[Ljava/lang/Class;ZZ)Ljava/dyn/MethodType;") \
+ NOT_LP64( do_alias(machine_word_signature, int_signature) ) \
+ LP64_ONLY( do_alias(machine_word_signature, long_signature) ) \
+ \
+ /* common method and field names */ \
template(object_initializer_name, "<init>") \
template(class_initializer_name, "<clinit>") \
template(println_name, "println") \
@@ -289,6 +308,21 @@
template(bitCount_name, "bitCount") \
template(profile_name, "profile") \
template(equals_name, "equals") \
+ template(toString_name, "toString") \
+ template(values_name, "values") \
+ template(receiver_name, "receiver") \
+ template(vmtarget_name, "vmtarget") \
+ template(vmentry_name, "vmentry") \
+ template(vmslots_name, "vmslots") \
+ template(vmindex_name, "vmindex") \
+ template(vmargslot_name, "vmargslot") \
+ template(flags_name, "flags") \
+ template(argument_name, "argument") \
+ template(conversion_name, "conversion") \
+ template(rtype_name, "rtype") \
+ template(ptypes_name, "ptypes") \
+ template(form_name, "form") \
+ template(erasedType_name, "erasedType") \
\
/* non-intrinsic name/signature pairs: */ \
template(register_method_name, "register") \
@@ -353,6 +387,7 @@
template(void_classloader_signature, "()Ljava/lang/ClassLoader;") \
template(void_object_signature, "()Ljava/lang/Object;") \
template(void_class_signature, "()Ljava/lang/Class;") \
+ template(void_string_signature, "()Ljava/lang/String;") \
template(object_array_object_object_signature, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")\
template(exception_void_signature, "(Ljava/lang/Exception;)V") \
template(protectiondomain_signature, "[Ljava/security/ProtectionDomain;") \
--- a/hotspot/src/share/vm/code/vtableStubs.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/code/vtableStubs.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -107,13 +107,11 @@
s = create_itable_stub(vtable_index);
}
enter(is_vtable_stub, vtable_index, s);
-#ifndef PRODUCT
if (PrintAdapterHandlers) {
tty->print_cr("Decoding VtableStub %s[%d]@%d",
is_vtable_stub? "vtbl": "itbl", vtable_index, VtableStub::receiver_location());
Disassembler::decode(s->code_begin(), s->code_end());
}
-#endif
}
return s->entry_point();
}
--- a/hotspot/src/share/vm/includeDB_core Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/includeDB_core Wed Jul 05 16:51:32 2017 +0200
@@ -254,6 +254,7 @@
assembler_<arch>.cpp collectedHeap.inline.hpp
assembler_<arch>.cpp interfaceSupport.hpp
assembler_<arch>.cpp interpreter.hpp
+assembler_<arch>.cpp methodHandles.hpp
assembler_<arch>.cpp objectMonitor.hpp
assembler_<arch>.cpp os.hpp
assembler_<arch>.cpp resourceArea.hpp
@@ -1274,6 +1275,7 @@
cpCacheKlass.cpp collectedHeap.hpp
cpCacheKlass.cpp constantPoolOop.hpp
cpCacheKlass.cpp cpCacheKlass.hpp
+cpCacheKlass.cpp genOopClosures.inline.hpp
cpCacheKlass.cpp handles.inline.hpp
cpCacheKlass.cpp javaClasses.hpp
cpCacheKlass.cpp markSweep.inline.hpp
@@ -2202,6 +2204,7 @@
interpreter_<arch_model>.cpp jvmtiExport.hpp
interpreter_<arch_model>.cpp jvmtiThreadState.hpp
interpreter_<arch_model>.cpp methodDataOop.hpp
+interpreter_<arch_model>.cpp methodHandles.hpp
interpreter_<arch_model>.cpp methodOop.hpp
interpreter_<arch_model>.cpp oop.inline.hpp
interpreter_<arch_model>.cpp sharedRuntime.hpp
@@ -2596,6 +2599,7 @@
linkResolver.cpp instanceKlass.hpp
linkResolver.cpp interpreterRuntime.hpp
linkResolver.cpp linkResolver.hpp
+linkResolver.cpp methodHandles.hpp
linkResolver.cpp nativeLookup.hpp
linkResolver.cpp objArrayOop.hpp
linkResolver.cpp reflection.hpp
@@ -2812,6 +2816,25 @@
methodDataOop.hpp orderAccess.hpp
methodDataOop.hpp universe.hpp
+methodHandles.hpp frame.inline.hpp
+methodHandles.hpp globals.hpp
+methodHandles.hpp interfaceSupport.hpp
+methodHandles.hpp javaClasses.hpp
+methodHandles.hpp vmSymbols.hpp
+
+methodHandles.cpp allocation.inline.hpp
+methodHandles.cpp interpreter.hpp
+methodHandles.cpp javaCalls.hpp
+methodHandles.cpp methodHandles.hpp
+methodHandles.cpp oopFactory.hpp
+methodHandles.cpp reflection.hpp
+methodHandles.cpp signature.hpp
+methodHandles.cpp symbolTable.hpp
+
+methodHandles_<arch>.cpp allocation.inline.hpp
+methodHandles_<arch>.cpp interpreter.hpp
+methodHandles_<arch>.cpp methodHandles.hpp
+
methodKlass.cpp collectedHeap.inline.hpp
methodKlass.cpp constMethodKlass.hpp
methodKlass.cpp gcLocker.hpp
@@ -3061,6 +3084,7 @@
oop.inline.hpp arrayOop.hpp
oop.inline.hpp atomic.hpp
oop.inline.hpp barrierSet.inline.hpp
+oop.inline.hpp bytes_<arch>.hpp
oop.inline.hpp cardTableModRefBS.hpp
oop.inline.hpp collectedHeap.inline.hpp
oop.inline.hpp compactingPermGenGen.hpp
@@ -3674,6 +3698,7 @@
sharedRuntime.cpp interpreter.hpp
sharedRuntime.cpp javaCalls.hpp
sharedRuntime.cpp jvmtiExport.hpp
+sharedRuntime.cpp methodHandles.hpp
sharedRuntime.cpp jvmtiRedefineClassesTrace.hpp
sharedRuntime.cpp nativeInst_<arch>.hpp
sharedRuntime.cpp nativeLookup.hpp
@@ -3862,6 +3887,7 @@
stubGenerator_<arch_model>.cpp handles.inline.hpp
stubGenerator_<arch_model>.cpp instanceOop.hpp
stubGenerator_<arch_model>.cpp interpreter.hpp
+stubGenerator_<arch_model>.cpp methodHandles.hpp
stubGenerator_<arch_model>.cpp methodOop.hpp
stubGenerator_<arch_model>.cpp nativeInst_<arch>.hpp
stubGenerator_<arch_model>.cpp objArrayKlass.hpp
--- a/hotspot/src/share/vm/includeDB_gc_parallel Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/includeDB_gc_parallel Wed Jul 05 16:51:32 2017 +0200
@@ -36,6 +36,12 @@
constantPoolKlass.cpp psScavenge.inline.hpp
constantPoolKlass.cpp parOopClosures.inline.hpp
+constantPoolKlass.cpp cardTableRS.hpp
+constantPoolKlass.cpp oop.pcgc.inline.hpp
+constantPoolKlass.cpp psPromotionManager.inline.hpp
+constantPoolKlass.cpp psScavenge.inline.hpp
+constantPoolKlass.cpp parOopClosures.inline.hpp
+
genCollectedHeap.cpp concurrentMarkSweepThread.hpp
genCollectedHeap.cpp vmCMSOperations.hpp
--- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,6 +61,7 @@
empty, // empty method (code: _return)
accessor, // accessor method (code: _aload_0, _getfield, _(a|i)return)
abstract, // abstract method (throws an AbstractMethodException)
+ method_handle, // java.dyn.MethodHandles::invoke
java_lang_math_sin, // implementation of java.lang.Math.sin (x)
java_lang_math_cos, // implementation of java.lang.Math.cos (x)
java_lang_math_tan, // implementation of java.lang.Math.tan (x)
@@ -91,8 +92,6 @@
static address _rethrow_exception_entry; // rethrows an activation in previous frame
-
-
friend class AbstractInterpreterGenerator;
friend class InterpreterGenerator;
friend class InterpreterMacroAssembler;
--- a/hotspot/src/share/vm/interpreter/cppInterpreter.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/cppInterpreter.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -114,6 +114,7 @@
method_entry(empty);
method_entry(accessor);
method_entry(abstract);
+ method_entry(method_handle);
method_entry(java_lang_math_sin );
method_entry(java_lang_math_cos );
method_entry(java_lang_math_tan );
--- a/hotspot/src/share/vm/interpreter/interpreter.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/interpreter.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -168,10 +168,14 @@
// Abstract method?
if (m->is_abstract()) return abstract;
+ // Invoker for method handles?
+ if (m->is_method_handle_invoke()) return method_handle;
+
// Native method?
// Note: This test must come _before_ the test for intrinsic
// methods. See also comments below.
if (m->is_native()) {
+ assert(!m->is_method_handle_invoke(), "overlapping bits here, watch out");
return m->is_synchronized() ? native_synchronized : native;
}
@@ -249,6 +253,7 @@
case empty : tty->print("empty" ); break;
case accessor : tty->print("accessor" ); break;
case abstract : tty->print("abstract" ); break;
+ case method_handle : tty->print("method_handle" ); break;
case java_lang_math_sin : tty->print("java_lang_math_sin" ); break;
case java_lang_math_cos : tty->print("java_lang_math_cos" ); break;
case java_lang_math_tan : tty->print("java_lang_math_tan" ); break;
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -293,6 +293,24 @@
THROW_MSG(vmSymbols::java_lang_ClassCastException(), message);
IRT_END
+// required can be either a MethodType, or a Class (for a single argument)
+// actual (if not null) can be either a MethodHandle, or an arbitrary value (for a single argument)
+IRT_ENTRY(void, InterpreterRuntime::throw_WrongMethodTypeException(JavaThread* thread,
+ oopDesc* required,
+ oopDesc* actual)) {
+ ResourceMark rm(thread);
+ char* message = SharedRuntime::generate_wrong_method_type_message(thread, required, actual);
+
+ if (ProfileTraps) {
+ note_trap(thread, Deoptimization::Reason_constraint, CHECK);
+ }
+
+ // create exception
+ THROW_MSG(vmSymbols::java_dyn_WrongMethodTypeException(), message);
+}
+IRT_END
+
+
// exception_handler_for_exception(...) returns the continuation address,
// the exception oop (via TLS) and sets the bci/bcp for the continuation.
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -66,6 +66,7 @@
static void throw_StackOverflowError(JavaThread* thread);
static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index);
static void throw_ClassCastException(JavaThread* thread, oopDesc* obj);
+ static void throw_WrongMethodTypeException(JavaThread* thread, oopDesc* mtype = NULL, oopDesc* mhandle = NULL);
static void create_exception(JavaThread* thread, char* name, char* message);
static void create_klass_exception(JavaThread* thread, char* name, oopDesc* obj);
static address exception_handler_for_exception(JavaThread* thread, oopDesc* exception);
--- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -151,6 +151,20 @@
result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name(), signature()));
}
+void LinkResolver::lookup_implicit_method(methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) {
+ if (EnableMethodHandles && MethodHandles::enabled() &&
+ name == vmSymbolHandles::invoke_name() && klass() == SystemDictionary::MethodHandle_klass()) {
+ methodOop result_oop = SystemDictionary::find_method_handle_invoke(signature,
+ Handle(),
+ Handle(),
+ CHECK);
+ if (result_oop != NULL) {
+ assert(result_oop->is_method_handle_invoke() && result_oop->signature() == signature(), "consistent");
+ result = methodHandle(THREAD, result_oop);
+ }
+ }
+}
+
void LinkResolver::check_method_accessability(KlassHandle ref_klass,
KlassHandle resolved_klass,
KlassHandle sel_klass,
@@ -240,6 +254,11 @@
lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
if (resolved_method.is_null()) {
+ // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...)
+ lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, CHECK);
+ }
+
+ if (resolved_method.is_null()) {
// 4. method lookup failed
ResourceMark rm(THREAD);
THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(),
--- a/hotspot/src/share/vm/interpreter/linkResolver.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/linkResolver.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -103,6 +103,7 @@
static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
+ static void lookup_implicit_method (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
static int vtable_index_of_miranda_method(KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
--- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -168,6 +168,7 @@
address TemplateInterpreter::_throw_ArrayStoreException_entry = NULL;
address TemplateInterpreter::_throw_ArithmeticException_entry = NULL;
address TemplateInterpreter::_throw_ClassCastException_entry = NULL;
+address TemplateInterpreter::_throw_WrongMethodType_entry = NULL;
address TemplateInterpreter::_throw_NullPointerException_entry = NULL;
address TemplateInterpreter::_throw_StackOverflowError_entry = NULL;
address TemplateInterpreter::_throw_exception_entry = NULL;
@@ -341,6 +342,7 @@
Interpreter::_throw_ArrayStoreException_entry = generate_klass_exception_handler("java/lang/ArrayStoreException" );
Interpreter::_throw_ArithmeticException_entry = generate_exception_handler("java/lang/ArithmeticException" , "/ by zero");
Interpreter::_throw_ClassCastException_entry = generate_ClassCastException_handler();
+ Interpreter::_throw_WrongMethodType_entry = generate_WrongMethodType_handler();
Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException" , NULL );
Interpreter::_throw_StackOverflowError_entry = generate_StackOverflowError_handler();
}
@@ -358,6 +360,7 @@
method_entry(empty)
method_entry(accessor)
method_entry(abstract)
+ method_entry(method_handle)
method_entry(java_lang_math_sin )
method_entry(java_lang_math_cos )
method_entry(java_lang_math_tan )
--- a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -77,6 +77,7 @@
friend class VMStructs;
friend class InterpreterMacroAssembler;
friend class TemplateInterpreterGenerator;
+ friend class InterpreterGenerator;
friend class TemplateTable;
// friend class Interpreter;
public:
@@ -93,6 +94,7 @@
static address _throw_ArrayStoreException_entry;
static address _throw_ArithmeticException_entry;
static address _throw_ClassCastException_entry;
+ static address _throw_WrongMethodType_entry;
static address _throw_NullPointerException_entry;
static address _throw_exception_entry;
@@ -137,6 +139,7 @@
static address remove_activation_entry() { return _remove_activation_entry; }
static address throw_exception_entry() { return _throw_exception_entry; }
static address throw_ArithmeticException_entry() { return _throw_ArithmeticException_entry; }
+ static address throw_WrongMethodType_entry() { return _throw_WrongMethodType_entry; }
static address throw_NullPointerException_entry() { return _throw_NullPointerException_entry; }
static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; }
--- a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,6 +48,7 @@
}
address generate_exception_handler_common(const char* name, const char* message, bool pass_oop);
address generate_ClassCastException_handler();
+ address generate_WrongMethodType_handler();
address generate_ArrayIndexOutOfBounds_handler(const char* name);
address generate_continuation_for(TosState state);
address generate_return_entry_for(TosState state, int step);
--- a/hotspot/src/share/vm/memory/dump.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/memory/dump.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -928,6 +928,10 @@
// shared classes at runtime, where constraints were previously created.
guarantee(SystemDictionary::constraints()->number_of_entries() == 0,
"loader constraints are not saved");
+ // Revisit and implement this if we prelink method handle call sites:
+ guarantee(SystemDictionary::invoke_method_table() == NULL ||
+ SystemDictionary::invoke_method_table()->number_of_entries() == 0,
+ "invoke method table is not saved");
GenCollectedHeap* gch = GenCollectedHeap::heap();
// At this point, many classes have been loaded.
--- a/hotspot/src/share/vm/oops/methodKlass.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/oops/methodKlass.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -298,7 +298,11 @@
m->code()->print_value_on(st);
st->cr();
}
- if (m->is_native()) {
+ if (m->is_method_handle_invoke()) {
+ st->print_cr(" - invoke method type: " INTPTR_FORMAT, (address) m->method_handle_type());
+ // m is classified as native, but it does not have an interesting
+ // native_function or signature handler
+ } else if (m->is_native()) {
st->print_cr(" - native function: " INTPTR_FORMAT, m->native_function());
st->print_cr(" - signature handler: " INTPTR_FORMAT, m->signature_handler());
}
--- a/hotspot/src/share/vm/oops/methodOop.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/oops/methodOop.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -304,6 +304,12 @@
}
+int methodOopDesc::extra_stack_words() {
+ // not an inline function, to avoid a header dependency on Interpreter
+ return extra_stack_entries() * Interpreter::stackElementSize();
+}
+
+
void methodOopDesc::compute_size_of_parameters(Thread *thread) {
symbolHandle h_signature(thread, signature());
ArgumentSizeComputer asc(h_signature);
@@ -564,6 +570,11 @@
bool methodOopDesc::is_not_compilable(int comp_level) const {
+ if (is_method_handle_invoke()) {
+ // compilers must recognize this method specially, or not at all
+ return true;
+ }
+
methodDataOop mdo = method_data();
if (mdo != NULL
&& (uint)mdo->decompile_count() > (uint)PerMethodRecompilationCutoff) {
@@ -651,7 +662,7 @@
assert(entry != NULL, "interpreter entry must be non-null");
// Sets both _i2i_entry and _from_interpreted_entry
set_interpreter_entry(entry);
- if (is_native()) {
+ if (is_native() && !is_method_handle_invoke()) {
set_native_function(
SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
!native_bind_event_is_interesting);
@@ -783,6 +794,100 @@
return false;
}
+// Constant pool structure for invoke methods:
+enum {
+ _imcp_invoke_name = 1, // utf8: 'invoke'
+ _imcp_invoke_signature, // utf8: (variable symbolOop)
+ _imcp_method_type_value, // string: (variable java/dyn/MethodType, sic)
+ _imcp_limit
+};
+
+oop methodOopDesc::method_handle_type() const {
+ if (!is_method_handle_invoke()) { assert(false, "caller resp."); return NULL; }
+ oop mt = constants()->resolved_string_at(_imcp_method_type_value);
+ assert(mt->klass() == SystemDictionary::MethodType_klass(), "");
+ return mt;
+}
+
+jint* methodOopDesc::method_type_offsets_chain() {
+ static jint pchase[] = { -1, -1, -1 };
+ if (pchase[0] == -1) {
+ jint step0 = in_bytes(constants_offset());
+ jint step1 = (constantPoolOopDesc::header_size() + _imcp_method_type_value) * HeapWordSize;
+ // do this in reverse to avoid races:
+ OrderAccess::release_store(&pchase[1], step1);
+ OrderAccess::release_store(&pchase[0], step0);
+ }
+ return pchase;
+}
+
+methodHandle methodOopDesc::make_invoke_method(KlassHandle holder,
+ symbolHandle signature,
+ Handle method_type, TRAPS) {
+ methodHandle empty;
+
+ assert(holder() == SystemDictionary::MethodHandle_klass(),
+ "must be a JSR 292 magic type");
+
+ if (TraceMethodHandles) {
+ tty->print("Creating invoke method for ");
+ signature->print_value();
+ tty->cr();
+ }
+
+ constantPoolHandle cp;
+ {
+ constantPoolOop cp_oop = oopFactory::new_constantPool(_imcp_limit, IsSafeConc, CHECK_(empty));
+ cp = constantPoolHandle(THREAD, cp_oop);
+ }
+ cp->symbol_at_put(_imcp_invoke_name, vmSymbols::invoke_name());
+ cp->symbol_at_put(_imcp_invoke_signature, signature());
+ cp->string_at_put(_imcp_method_type_value, vmSymbols::void_signature());
+ cp->set_pool_holder(holder());
+
+ // set up the fancy stuff:
+ cp->pseudo_string_at_put(_imcp_method_type_value, method_type());
+ methodHandle m;
+ {
+ int flags_bits = (JVM_MH_INVOKE_BITS | JVM_ACC_PUBLIC | JVM_ACC_FINAL);
+ methodOop m_oop = oopFactory::new_method(0, accessFlags_from(flags_bits),
+ 0, 0, 0, IsSafeConc, CHECK_(empty));
+ m = methodHandle(THREAD, m_oop);
+ }
+ m->set_constants(cp());
+ m->set_name_index(_imcp_invoke_name);
+ m->set_signature_index(_imcp_invoke_signature);
+ assert(m->name() == vmSymbols::invoke_name(), "");
+ assert(m->signature() == signature(), "");
+#ifdef CC_INTERP
+ ResultTypeFinder rtf(signature());
+ m->set_result_index(rtf.type());
+#endif
+ m->compute_size_of_parameters(THREAD);
+ m->set_exception_table(Universe::the_empty_int_array());
+
+ // Finally, set up its entry points.
+ assert(m->method_handle_type() == method_type(), "");
+ assert(m->can_be_statically_bound(), "");
+ m->set_vtable_index(methodOopDesc::nonvirtual_vtable_index);
+ m->link_method(m, CHECK_(empty));
+
+#ifdef ASSERT
+ // Make sure the pointer chase works.
+ address p = (address) m();
+ for (jint* pchase = method_type_offsets_chain(); (*pchase) != -1; pchase++) {
+ p = *(address*)(p + (*pchase));
+ }
+ assert((oop)p == method_type(), "pointer chase is correct");
+#endif
+
+ if (TraceMethodHandles)
+ m->print_on(tty);
+
+ return m;
+}
+
+
methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length,
u_char* new_compressed_linenumber_table, int new_compressed_linenumber_size, TRAPS) {
--- a/hotspot/src/share/vm/oops/methodOop.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/oops/methodOop.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -320,6 +320,7 @@
enum VtableIndexFlag {
// Valid vtable indexes are non-negative (>= 0).
// These few negative values are used as sentinels.
+ highest_unused_vtable_index_value = -5,
invalid_vtable_index = -4, // distinct from any valid vtable index
garbage_vtable_index = -3, // not yet linked; no vtable layout yet
nonvirtual_vtable_index = -2 // there is no need for vtable dispatch
@@ -523,6 +524,18 @@
// Reflection support
bool is_overridden_in(klassOop k) const;
+ // JSR 292 support
+ bool is_method_handle_invoke() const { return access_flags().is_method_handle_invoke(); }
+ static methodHandle make_invoke_method(KlassHandle holder,
+ symbolHandle signature,
+ Handle method_type,
+ TRAPS);
+ // these operate only on invoke methods:
+ oop method_handle_type() const;
+ static jint* method_type_offsets_chain(); // series of pointer-offsets, terminated by -1
+ // presize interpreter frames for extra interpreter stack entries, if needed
+ static int extra_stack_entries() { return EnableMethodHandles ? (int)MethodHandlePushLimit : 0; }
+ static int extra_stack_words(); // = extra_stack_entries() * Interpreter::stackElementSize()
// RedefineClasses() support:
bool is_old() const { return access_flags().is_old(); }
void set_is_old() { _access_flags.set_is_old(); }
--- a/hotspot/src/share/vm/oops/oop.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/oops/oop.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -263,6 +263,9 @@
jdouble double_field_acquire(int offset) const;
void release_double_field_put(int offset, jdouble contents);
+ address address_field_acquire(int offset) const;
+ void release_address_field_put(int offset, address contents);
+
// printing functions for VM debugging
void print_on(outputStream* st) const; // First level print
void print_value_on(outputStream* st) const; // Second level print.
--- a/hotspot/src/share/vm/oops/oop.inline.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/oops/oop.inline.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -349,6 +349,9 @@
inline jdouble oopDesc::double_field_acquire(int offset) const { return OrderAccess::load_acquire(double_field_addr(offset)); }
inline void oopDesc::release_double_field_put(int offset, jdouble contents) { OrderAccess::release_store(double_field_addr(offset), contents); }
+inline address oopDesc::address_field_acquire(int offset) const { return (address) OrderAccess::load_ptr_acquire(address_field_addr(offset)); }
+inline void oopDesc::release_address_field_put(int offset, address contents) { OrderAccess::release_store_ptr(address_field_addr(offset), contents); }
+
inline int oopDesc::size_given_klass(Klass* klass) {
int lh = klass->layout_helper();
int s = lh >> LogHeapWordSize; // deliver size scaled by wordSize
--- a/hotspot/src/share/vm/opto/callnode.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/opto/callnode.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1043,6 +1043,51 @@
//=============================================================================
uint AllocateArrayNode::size_of() const { return sizeof(*this); }
+Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) {
+ if (remove_dead_region(phase, can_reshape)) return this;
+
+ const Type* type = phase->type(Ideal_length());
+ if (type->isa_int() && type->is_int()->_hi < 0) {
+ if (can_reshape) {
+ PhaseIterGVN *igvn = phase->is_IterGVN();
+ // Unreachable fall through path (negative array length),
+ // the allocation can only throw so disconnect it.
+ Node* proj = proj_out(TypeFunc::Control);
+ Node* catchproj = NULL;
+ if (proj != NULL) {
+ for (DUIterator_Fast imax, i = proj->fast_outs(imax); i < imax; i++) {
+ Node *cn = proj->fast_out(i);
+ if (cn->is_Catch()) {
+ catchproj = cn->as_Multi()->proj_out(CatchProjNode::fall_through_index);
+ break;
+ }
+ }
+ }
+ if (catchproj != NULL && catchproj->outcnt() > 0 &&
+ (catchproj->outcnt() > 1 ||
+ catchproj->unique_out()->Opcode() != Op_Halt)) {
+ assert(catchproj->is_CatchProj(), "must be a CatchProjNode");
+ Node* nproj = catchproj->clone();
+ igvn->register_new_node_with_optimizer(nproj);
+
+ Node *frame = new (phase->C, 1) ParmNode( phase->C->start(), TypeFunc::FramePtr );
+ frame = phase->transform(frame);
+ // Halt & Catch Fire
+ Node *halt = new (phase->C, TypeFunc::Parms) HaltNode( nproj, frame );
+ phase->C->root()->add_req(halt);
+ phase->transform(halt);
+
+ igvn->replace_node(catchproj, phase->C->top());
+ return this;
+ }
+ } else {
+ // Can't correct it during regular GVN so register for IGVN
+ phase->C->record_for_igvn(this);
+ }
+ }
+ return NULL;
+}
+
// Retrieve the length from the AllocateArrayNode. Narrow the type with a
// CastII, if appropriate. If we are not allowed to create new nodes, and
// a CastII is appropriate, return NULL.
--- a/hotspot/src/share/vm/opto/callnode.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/opto/callnode.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -762,6 +762,7 @@
}
virtual int Opcode() const;
virtual uint size_of() const; // Size is bigger
+ virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
// Dig the length operand out of a array allocation site.
Node* Ideal_length() {
--- a/hotspot/src/share/vm/opto/macro.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/opto/macro.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -806,8 +806,7 @@
}
} else if (use->is_AddP()) {
// raw memory addresses used only by the initialization
- _igvn.hash_delete(use);
- _igvn.subsume_node(use, C->top());
+ _igvn.replace_node(use, C->top());
} else {
assert(false, "only Initialize or AddP expected");
}
@@ -1291,8 +1290,7 @@
if (_fallthroughcatchproj != NULL) {
ctrl = _fallthroughcatchproj->clone();
transform_later(ctrl);
- _igvn.hash_delete(_fallthroughcatchproj);
- _igvn.subsume_node(_fallthroughcatchproj, result_region);
+ _igvn.replace_node(_fallthroughcatchproj, result_region);
} else {
ctrl = top();
}
@@ -1303,8 +1301,7 @@
} else {
slow_result = _resproj->clone();
transform_later(slow_result);
- _igvn.hash_delete(_resproj);
- _igvn.subsume_node(_resproj, result_phi_rawoop);
+ _igvn.replace_node(_resproj, result_phi_rawoop);
}
// Plug slow-path into result merge point
@@ -1613,18 +1610,15 @@
assert(membar != NULL && membar->Opcode() == Op_MemBarAcquire, "");
Node* ctrlproj = membar->proj_out(TypeFunc::Control);
Node* memproj = membar->proj_out(TypeFunc::Memory);
- _igvn.hash_delete(ctrlproj);
- _igvn.subsume_node(ctrlproj, fallthroughproj);
- _igvn.hash_delete(memproj);
- _igvn.subsume_node(memproj, memproj_fallthrough);
+ _igvn.replace_node(ctrlproj, fallthroughproj);
+ _igvn.replace_node(memproj, memproj_fallthrough);
// Delete FastLock node also if this Lock node is unique user
// (a loop peeling may clone a Lock node).
Node* flock = alock->as_Lock()->fastlock_node();
if (flock->outcnt() == 1) {
assert(flock->unique_out() == alock, "sanity");
- _igvn.hash_delete(flock);
- _igvn.subsume_node(flock, top());
+ _igvn.replace_node(flock, top());
}
}
@@ -1634,20 +1628,16 @@
MemBarNode* membar = ctrl->in(0)->as_MemBar();
assert(membar->Opcode() == Op_MemBarRelease &&
mem->is_Proj() && membar == mem->in(0), "");
- _igvn.hash_delete(fallthroughproj);
- _igvn.subsume_node(fallthroughproj, ctrl);
- _igvn.hash_delete(memproj_fallthrough);
- _igvn.subsume_node(memproj_fallthrough, mem);
+ _igvn.replace_node(fallthroughproj, ctrl);
+ _igvn.replace_node(memproj_fallthrough, mem);
fallthroughproj = ctrl;
memproj_fallthrough = mem;
ctrl = membar->in(TypeFunc::Control);
mem = membar->in(TypeFunc::Memory);
}
- _igvn.hash_delete(fallthroughproj);
- _igvn.subsume_node(fallthroughproj, ctrl);
- _igvn.hash_delete(memproj_fallthrough);
- _igvn.subsume_node(memproj_fallthrough, mem);
+ _igvn.replace_node(fallthroughproj, ctrl);
+ _igvn.replace_node(memproj_fallthrough, mem);
return true;
}
@@ -1879,13 +1869,12 @@
region->init_req(1, slow_ctrl);
// region inputs are now complete
transform_later(region);
- _igvn.subsume_node(_fallthroughproj, region);
+ _igvn.replace_node(_fallthroughproj, region);
Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) );
mem_phi->init_req(1, memproj );
transform_later(mem_phi);
- _igvn.hash_delete(_memproj_fallthrough);
- _igvn.subsume_node(_memproj_fallthrough, mem_phi);
+ _igvn.replace_node(_memproj_fallthrough, mem_phi);
}
//------------------------------expand_unlock_node----------------------
@@ -1943,14 +1932,13 @@
region->init_req(1, slow_ctrl);
// region inputs are now complete
transform_later(region);
- _igvn.subsume_node(_fallthroughproj, region);
+ _igvn.replace_node(_fallthroughproj, region);
Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) );
mem_phi->init_req(1, memproj );
mem_phi->init_req(2, mem);
transform_later(mem_phi);
- _igvn.hash_delete(_memproj_fallthrough);
- _igvn.subsume_node(_memproj_fallthrough, mem_phi);
+ _igvn.replace_node(_memproj_fallthrough, mem_phi);
}
//------------------------------expand_macro_nodes----------------------
@@ -1969,9 +1957,7 @@
if (n->is_AbstractLock()) {
success = eliminate_locking_node(n->as_AbstractLock());
} else if (n->Opcode() == Op_Opaque1 || n->Opcode() == Op_Opaque2) {
- _igvn.add_users_to_worklist(n);
- _igvn.hash_delete(n);
- _igvn.subsume_node(n, n->in(1));
+ _igvn.replace_node(n, n->in(1));
success = true;
}
assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
--- a/hotspot/src/share/vm/opto/memnode.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/opto/memnode.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -218,6 +218,26 @@
// Don't bother trying to transform a dead node
if( ctl && ctl->is_top() ) return NodeSentinel;
+ PhaseIterGVN *igvn = phase->is_IterGVN();
+ // Wait if control on the worklist.
+ if (ctl && can_reshape && igvn != NULL) {
+ Node* bol = NULL;
+ Node* cmp = NULL;
+ if (ctl->in(0)->is_If()) {
+ assert(ctl->is_IfTrue() || ctl->is_IfFalse(), "sanity");
+ bol = ctl->in(0)->in(1);
+ if (bol->is_Bool())
+ cmp = ctl->in(0)->in(1)->in(1);
+ }
+ if (igvn->_worklist.member(ctl) ||
+ (bol != NULL && igvn->_worklist.member(bol)) ||
+ (cmp != NULL && igvn->_worklist.member(cmp)) ) {
+ // This control path may be dead.
+ // Delay this memory node transformation until the control is processed.
+ phase->is_IterGVN()->_worklist.push(this);
+ return NodeSentinel; // caller will return NULL
+ }
+ }
// Ignore if memory is dead, or self-loop
Node *mem = in(MemNode::Memory);
if( phase->type( mem ) == Type::TOP ) return NodeSentinel; // caller will return NULL
@@ -227,14 +247,22 @@
const Type *t_adr = phase->type( address );
if( t_adr == Type::TOP ) return NodeSentinel; // caller will return NULL
- PhaseIterGVN *igvn = phase->is_IterGVN();
- if( can_reshape && igvn != NULL && igvn->_worklist.member(address) ) {
+ if( can_reshape && igvn != NULL &&
+ (igvn->_worklist.member(address) || phase->type(address) != adr_type()) ) {
// The address's base and type may change when the address is processed.
// Delay this mem node transformation until the address is processed.
phase->is_IterGVN()->_worklist.push(this);
return NodeSentinel; // caller will return NULL
}
+#ifdef ASSERT
+ Node* base = NULL;
+ if (address->is_AddP())
+ base = address->in(AddPNode::Base);
+ assert(base == NULL || t_adr->isa_rawptr() ||
+ !phase->type(base)->higher_equal(TypePtr::NULL_PTR), "NULL+offs not RAW address?");
+#endif
+
// Avoid independent memory operations
Node* old_mem = mem;
@@ -1307,22 +1335,20 @@
set_req(MemNode::Control,ctrl);
}
- // Check for useless control edge in some common special cases
- if (in(MemNode::Control) != NULL) {
- intptr_t ignore = 0;
- Node* base = AddPNode::Ideal_base_and_offset(address, phase, ignore);
- if (base != NULL
+ intptr_t ignore = 0;
+ Node* base = AddPNode::Ideal_base_and_offset(address, phase, ignore);
+ if (base != NULL
+ && phase->C->get_alias_index(phase->type(address)->is_ptr()) != Compile::AliasIdxRaw) {
+ // Check for useless control edge in some common special cases
+ if (in(MemNode::Control) != NULL
&& phase->type(base)->higher_equal(TypePtr::NOTNULL)
- && phase->C->get_alias_index(phase->type(address)->is_ptr()) != Compile::AliasIdxRaw
&& all_controls_dominate(base, phase->C->start())) {
// A method-invariant, non-null address (constant or 'this' argument).
set_req(MemNode::Control, NULL);
}
- }
-
- if (EliminateAutoBox && can_reshape && in(Address)->is_AddP()) {
- Node* base = in(Address)->in(AddPNode::Base);
- if (base != NULL) {
+
+ if (EliminateAutoBox && can_reshape) {
+ assert(!phase->type(base)->higher_equal(TypePtr::NULL_PTR), "the autobox pointer should be non-null");
Compile::AliasType* atp = phase->C->alias_type(adr_type());
if (is_autobox_object(atp)) {
Node* result = eliminate_autobox(phase);
@@ -1455,10 +1481,11 @@
jt = _type;
}
- if (EliminateAutoBox) {
+ if (EliminateAutoBox && adr->is_AddP()) {
// The pointers in the autobox arrays are always non-null
- Node* base = in(Address)->in(AddPNode::Base);
- if (base != NULL) {
+ Node* base = adr->in(AddPNode::Base);
+ if (base != NULL &&
+ !phase->type(base)->higher_equal(TypePtr::NULL_PTR)) {
Compile::AliasType* atp = phase->C->alias_type(base->adr_type());
if (is_autobox_cache(atp)) {
return jt->join(TypePtr::NOTNULL)->is_ptr();
--- a/hotspot/src/share/vm/opto/output.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/opto/output.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -2256,7 +2256,8 @@
// bother scheduling them.
Node *last = bb->_nodes[_bb_end];
if( last->is_Catch() ||
- (last->is_Mach() && last->as_Mach()->ideal_Opcode() == Op_Halt) ) {
+ // Exclude unreachable path case when Halt node is in a separate block.
+ (_bb_end > 1 && last->is_Mach() && last->as_Mach()->ideal_Opcode() == Op_Halt) ) {
// There must be a prior call. Skip it.
while( !bb->_nodes[--_bb_end]->is_Call() ) {
assert( bb->_nodes[_bb_end]->is_Proj(), "skipping projections after expected call" );
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,2347 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+/*
+ * JSR 292 reference implementation: method handles
+ */
+
+#include "incls/_precompiled.incl"
+#include "incls/_methodHandles.cpp.incl"
+
+bool MethodHandles::_enabled = false; // set true after successful native linkage
+
+MethodHandleEntry* MethodHandles::_entries[MethodHandles::_EK_LIMIT] = {NULL};
+const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
+ "check_mtype",
+ "wrong_method_type", // what happens when there is a type mismatch
+ "invokestatic", // how a MH emulates invokestatic
+ "invokespecial", // ditto for the other invokes...
+ "invokevirtual",
+ "invokeinterface",
+ "bound_ref", // these are for BMH...
+ "bound_int",
+ "bound_long",
+ "bound_ref_direct", // (direct versions have a direct methodOop)
+ "bound_int_direct",
+ "bound_long_direct",
+
+ // starting at _adapter_mh_first:
+ "adapter_retype_only", // these are for AMH...
+ "adapter_check_cast",
+ "adapter_prim_to_prim",
+ "adapter_ref_to_prim",
+ "adapter_prim_to_ref",
+ "adapter_swap_args",
+ "adapter_rot_args",
+ "adapter_dup_args",
+ "adapter_drop_args",
+ "adapter_collect_args",
+ "adapter_spread_args",
+ "adapter_flyby",
+ "adapter_ricochet",
+
+ // optimized adapter types:
+ "adapter_swap_args/1",
+ "adapter_swap_args/2",
+ "adapter_rot_args/1,up",
+ "adapter_rot_args/1,down",
+ "adapter_rot_args/2,up",
+ "adapter_rot_args/2,down",
+ "adapter_prim_to_prim/i2i",
+ "adapter_prim_to_prim/l2i",
+ "adapter_prim_to_prim/d2f",
+ "adapter_prim_to_prim/i2l",
+ "adapter_prim_to_prim/f2d",
+ "adapter_ref_to_prim/unboxi",
+ "adapter_ref_to_prim/unboxl",
+ "adapter_spread_args/0",
+ "adapter_spread_args/1",
+ "adapter_spread_args/more",
+
+ NULL
+};
+
+#ifdef ASSERT
+bool MethodHandles::spot_check_entry_names() {
+ assert(!strcmp(entry_name(_invokestatic_mh), "invokestatic"), "");
+ assert(!strcmp(entry_name(_bound_ref_mh), "bound_ref"), "");
+ assert(!strcmp(entry_name(_adapter_retype_only), "adapter_retype_only"), "");
+ assert(!strcmp(entry_name(_adapter_ricochet), "adapter_ricochet"), "");
+ assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), "");
+ return true;
+}
+#endif
+
+void MethodHandles::set_enabled(bool z) {
+ if (_enabled != z) {
+ guarantee(z && EnableMethodHandles, "can only enable once, and only if -XX:+EnableMethodHandles");
+ _enabled = z;
+ }
+}
+
+// Note: A method which does not have a TRAPS argument cannot block in the GC
+// or throw exceptions. Such methods are used in this file to do something quick
+// and local, like parse a data structure. For speed, such methods work on plain
+// oops, not handles. Trapping methods uniformly operate on handles.
+
+methodOop MethodHandles::decode_vmtarget(oop vmtarget, int vmindex, oop mtype,
+ klassOop& receiver_limit_result, int& decode_flags_result) {
+ if (vmtarget == NULL) return NULL;
+ assert(methodOopDesc::nonvirtual_vtable_index < 0, "encoding");
+ if (vmindex < 0) {
+ // this DMH performs no dispatch; it is directly bound to a methodOop
+ // A MemberName may either be directly bound to a methodOop,
+ // or it may use the klass/index form; both forms mean the same thing.
+ methodOop m = decode_methodOop(methodOop(vmtarget), decode_flags_result);
+ if ((decode_flags_result & _dmf_has_receiver) != 0
+ && java_dyn_MethodType::is_instance(mtype)) {
+ // Extract receiver type restriction from mtype.ptypes[0].
+ objArrayOop ptypes = java_dyn_MethodType::ptypes(mtype);
+ oop ptype0 = (ptypes == NULL || ptypes->length() < 1) ? oop(NULL) : ptypes->obj_at(0);
+ if (java_lang_Class::is_instance(ptype0))
+ receiver_limit_result = java_lang_Class::as_klassOop(ptype0);
+ }
+ if (vmindex == methodOopDesc::nonvirtual_vtable_index) {
+ // this DMH can be an "invokespecial" version
+ decode_flags_result &= ~_dmf_does_dispatch;
+ } else {
+ assert(vmindex == methodOopDesc::invalid_vtable_index, "random vmindex?");
+ }
+ return m;
+ } else {
+ decode_flags_result |= MethodHandles::_dmf_does_dispatch;
+ assert(vmtarget->is_klass(), "must be class or interface");
+ receiver_limit_result = (klassOop)vmtarget;
+ Klass* tk = Klass::cast((klassOop)vmtarget);
+ if (tk->is_interface()) {
+ // an itable linkage is <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.)
+
+methodOop MethodHandles::decode_DirectMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) {
+ oop vmtarget = sun_dyn_DirectMethodHandle::vmtarget(mh);
+ int vmindex = sun_dyn_DirectMethodHandle::vmindex(mh);
+ oop mtype = sun_dyn_DirectMethodHandle::type(mh);
+ return decode_vmtarget(vmtarget, vmindex, mtype, receiver_limit_result, decode_flags_result);
+}
+
+methodOop MethodHandles::decode_BoundMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) {
+ assert(mh->klass() == SystemDictionary::BoundMethodHandle_klass(), "");
+ for (oop bmh = mh;;) {
+ // Bound MHs can be stacked to bind several arguments.
+ oop target = java_dyn_MethodHandle::vmtarget(bmh);
+ if (target == NULL) return NULL;
+ decode_flags_result |= MethodHandles::_dmf_binds_argument;
+ klassOop tk = target->klass();
+ if (tk == SystemDictionary::BoundMethodHandle_klass()) {
+ bmh = target;
+ continue;
+ } else {
+ if (java_dyn_MethodHandle::is_subclass(tk)) {
+ //assert(tk == SystemDictionary::DirectMethodHandle_klass(), "end of BMH chain must be DMH");
+ return decode_MethodHandle(target, receiver_limit_result, decode_flags_result);
+ } else {
+ // Optimized case: binding a receiver to a non-dispatched DMH
+ // short-circuits directly to the methodOop.
+ assert(target->is_method(), "must be a simple method");
+ methodOop m = (methodOop) target;
+ DEBUG_ONLY(int argslot = sun_dyn_BoundMethodHandle::vmargslot(bmh));
+ assert(argslot == m->size_of_parameters() - 1, "must be initial argument (receiver)");
+ decode_flags_result |= MethodHandles::_dmf_binds_method;
+ return m;
+ }
+ }
+ }
+}
+
+methodOop MethodHandles::decode_AdapterMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) {
+ assert(mh->klass() == SystemDictionary::AdapterMethodHandle_klass(), "");
+ for (oop amh = mh;;) {
+ // Adapter MHs can be stacked to convert several arguments.
+ int conv_op = adapter_conversion_op(sun_dyn_AdapterMethodHandle::conversion(amh));
+ decode_flags_result |= (_dmf_adapter_lsb << conv_op) & _DMF_ADAPTER_MASK;
+ oop target = java_dyn_MethodHandle::vmtarget(amh);
+ if (target == NULL) return NULL;
+ klassOop tk = target->klass();
+ if (tk == SystemDictionary::AdapterMethodHandle_klass()) {
+ amh = target;
+ continue;
+ } else {
+ // must be a BMH (which will bind some more arguments) or a DMH (for the final call)
+ return MethodHandles::decode_MethodHandle(target, receiver_limit_result, decode_flags_result);
+ }
+ }
+}
+
+methodOop MethodHandles::decode_MethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) {
+ if (mh == NULL) return NULL;
+ klassOop mhk = mh->klass();
+ assert(java_dyn_MethodHandle::is_subclass(mhk), "must be a MethodHandle");
+ if (mhk == SystemDictionary::DirectMethodHandle_klass()) {
+ return decode_DirectMethodHandle(mh, receiver_limit_result, decode_flags_result);
+ } else if (mhk == SystemDictionary::BoundMethodHandle_klass()) {
+ return decode_BoundMethodHandle(mh, receiver_limit_result, decode_flags_result);
+ } else if (mhk == SystemDictionary::AdapterMethodHandle_klass()) {
+ return decode_AdapterMethodHandle(mh, receiver_limit_result, decode_flags_result);
+ } else {
+ assert(false, "cannot parse this MH");
+ return NULL; // random MH?
+ }
+}
+
+methodOop MethodHandles::decode_methodOop(methodOop m, int& decode_flags_result) {
+ assert(m->is_method(), "");
+ if (m->is_static()) {
+ // check that signature begins '(L' or '([' (not '(I', '()', etc.)
+ symbolOop sig = m->signature();
+ BasicType recv_bt = char2type(sig->byte_at(1));
+ // Note: recv_bt might be T_ILLEGAL if byte_at(2) is ')'
+ assert(sig->byte_at(0) == '(', "must be method sig");
+ if (recv_bt == T_OBJECT || recv_bt == T_ARRAY)
+ decode_flags_result |= _dmf_has_receiver;
+ } else {
+ // non-static method
+ decode_flags_result |= _dmf_has_receiver;
+ if (!m->can_be_statically_bound() && !m->is_initializer()) {
+ decode_flags_result |= _dmf_does_dispatch;
+ if (Klass::cast(m->method_holder())->is_interface())
+ decode_flags_result |= _dmf_from_interface;
+ }
+ }
+ return m;
+}
+
+
+// A trusted party is handing us a cookie to determine a method.
+// Let's boil it down to the method oop they really want.
+methodOop MethodHandles::decode_method(oop x, klassOop& receiver_limit_result, int& decode_flags_result) {
+ decode_flags_result = 0;
+ receiver_limit_result = NULL;
+ klassOop xk = x->klass();
+ if (xk == Universe::methodKlassObj()) {
+ return decode_methodOop((methodOop) x, decode_flags_result);
+ } else if (xk == SystemDictionary::MemberName_klass()) {
+ // Note: This only works if the MemberName has already been resolved.
+ return decode_MemberName(x, receiver_limit_result, decode_flags_result);
+ } else if (java_dyn_MethodHandle::is_subclass(xk)) {
+ return decode_MethodHandle(x, receiver_limit_result, decode_flags_result);
+ } else if (xk == SystemDictionary::reflect_method_klass()) {
+ oop clazz = java_lang_reflect_Method::clazz(x);
+ int slot = java_lang_reflect_Method::slot(x);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ if (k != NULL && Klass::cast(k)->oop_is_instance())
+ return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot),
+ decode_flags_result);
+ } else if (xk == SystemDictionary::reflect_constructor_klass()) {
+ oop clazz = java_lang_reflect_Constructor::clazz(x);
+ int slot = java_lang_reflect_Constructor::slot(x);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ if (k != NULL && Klass::cast(k)->oop_is_instance())
+ return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot),
+ decode_flags_result);
+ } else {
+ // unrecognized object
+ assert(!x->is_method(), "already checked");
+ assert(!sun_dyn_MemberName::is_instance(x), "already checked");
+ }
+ return NULL;
+}
+
+
+int MethodHandles::decode_MethodHandle_stack_pushes(oop mh) {
+ if (mh->klass() == SystemDictionary::DirectMethodHandle_klass())
+ return 0; // no push/pop
+ int this_vmslots = java_dyn_MethodHandle::vmslots(mh);
+ int last_vmslots = 0;
+ oop last_mh = mh;
+ for (;;) {
+ oop target = java_dyn_MethodHandle::vmtarget(last_mh);
+ if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) {
+ last_vmslots = java_dyn_MethodHandle::vmslots(target);
+ break;
+ } else if (!java_dyn_MethodHandle::is_instance(target)) {
+ // might be klass or method
+ assert(target->is_method(), "must get here with a direct ref to method");
+ last_vmslots = methodOop(target)->size_of_parameters();
+ break;
+ }
+ last_mh = target;
+ }
+ // If I am called with fewer VM slots than my ultimate callee,
+ // it must be that I push the additionally needed slots.
+ // Likewise if am called with more VM slots, I will pop them.
+ return (last_vmslots - this_vmslots);
+}
+
+
+// MemberName support
+
+// import sun_dyn_MemberName.*
+enum {
+ IS_METHOD = sun_dyn_MemberName::MN_IS_METHOD,
+ IS_CONSTRUCTOR = sun_dyn_MemberName::MN_IS_CONSTRUCTOR,
+ IS_FIELD = sun_dyn_MemberName::MN_IS_FIELD,
+ IS_TYPE = sun_dyn_MemberName::MN_IS_TYPE,
+ SEARCH_SUPERCLASSES = sun_dyn_MemberName::MN_SEARCH_SUPERCLASSES,
+ SEARCH_INTERFACES = sun_dyn_MemberName::MN_SEARCH_INTERFACES,
+ ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE,
+ VM_INDEX_UNINITIALIZED = sun_dyn_MemberName::VM_INDEX_UNINITIALIZED
+};
+
+void MethodHandles::init_MemberName(oop mname_oop, oop target_oop) {
+ if (target_oop->klass() == SystemDictionary::reflect_field_klass()) {
+ oop clazz = java_lang_reflect_Field::clazz(target_oop); // fd.field_holder()
+ int slot = java_lang_reflect_Field::slot(target_oop); // fd.index()
+ int mods = java_lang_reflect_Field::modifiers(target_oop);
+ klassOop k = java_lang_Class::as_klassOop(clazz);
+ int offset = instanceKlass::cast(k)->offset_from_fields(slot);
+ init_MemberName(mname_oop, k, accessFlags_from(mods), offset);
+ } else {
+ int decode_flags = 0; klassOop receiver_limit = NULL;
+ methodOop m = MethodHandles::decode_method(target_oop,
+ receiver_limit, decode_flags);
+ bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0);
+ init_MemberName(mname_oop, m, do_dispatch);
+ }
+}
+
+void MethodHandles::init_MemberName(oop mname_oop, methodOop m, bool do_dispatch) {
+ int flags = ((m->is_initializer() ? IS_CONSTRUCTOR : IS_METHOD)
+ | (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS ));
+ oop vmtarget = m;
+ int vmindex = methodOopDesc::invalid_vtable_index; // implies no info yet
+ if (!do_dispatch || (flags & IS_CONSTRUCTOR) || m->can_be_statically_bound())
+ vmindex = methodOopDesc::nonvirtual_vtable_index; // implies never any dispatch
+ assert(vmindex != VM_INDEX_UNINITIALIZED, "Java sentinel value");
+ sun_dyn_MemberName::set_vmtarget(mname_oop, vmtarget);
+ sun_dyn_MemberName::set_vmindex(mname_oop, vmindex);
+ sun_dyn_MemberName::set_flags(mname_oop, flags);
+}
+
+void MethodHandles::init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset) {
+ int flags = (IS_FIELD | (jushort)( mods.as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS ));
+ oop vmtarget = field_holder;
+ int vmindex = offset; // implies no info yet
+ assert(vmindex != VM_INDEX_UNINITIALIZED, "bad alias on vmindex");
+ sun_dyn_MemberName::set_vmtarget(mname_oop, vmtarget);
+ sun_dyn_MemberName::set_vmindex(mname_oop, vmindex);
+ sun_dyn_MemberName::set_flags(mname_oop, flags);
+}
+
+
+methodOop MethodHandles::decode_MemberName(oop mname, klassOop& receiver_limit_result, int& decode_flags_result) {
+ int flags = sun_dyn_MemberName::flags(mname);
+ if ((flags & (IS_METHOD | IS_CONSTRUCTOR)) == 0) return NULL; // not invocable
+ oop vmtarget = sun_dyn_MemberName::vmtarget(mname);
+ int vmindex = sun_dyn_MemberName::vmindex(mname);
+ if (vmindex == VM_INDEX_UNINITIALIZED) return NULL; // not resolved
+ return decode_vmtarget(vmtarget, vmindex, NULL, receiver_limit_result, decode_flags_result);
+}
+
+// An unresolved member name is a mere symbolic reference.
+// Resolving it plants a vmtarget/vmindex in it,
+// which refers dirctly to JVM internals.
+void MethodHandles::resolve_MemberName(Handle mname, TRAPS) {
+ assert(sun_dyn_MemberName::is_instance(mname()), "");
+#ifdef ASSERT
+ // If this assert throws, renegotiate the sentinel value used by the Java code,
+ // so that it is distinct from any valid vtable index value, and any special
+ // values defined in methodOopDesc::VtableIndexFlag.
+ // The point of the slop is to give the Java code and the JVM some room
+ // to independently specify sentinel values.
+ const int sentinel_slop = 10;
+ const int sentinel_limit = methodOopDesc::highest_unused_vtable_index_value - sentinel_slop;
+ assert(VM_INDEX_UNINITIALIZED < sentinel_limit, "Java sentinel != JVM sentinels");
+#endif
+ if (sun_dyn_MemberName::vmindex(mname()) != VM_INDEX_UNINITIALIZED)
+ return; // already resolved
+ oop defc_oop = sun_dyn_MemberName::clazz(mname());
+ oop name_str = sun_dyn_MemberName::name(mname());
+ oop type_str = sun_dyn_MemberName::type(mname());
+ int flags = sun_dyn_MemberName::flags(mname());
+
+ if (defc_oop == NULL || name_str == NULL || type_str == NULL) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to resolve");
+ }
+ klassOop defc_klassOop = java_lang_Class::as_klassOop(defc_oop);
+ defc_oop = NULL; // safety
+ if (defc_klassOop == NULL) return; // a primitive; no resolution possible
+ if (!Klass::cast(defc_klassOop)->oop_is_instance()) {
+ if (!Klass::cast(defc_klassOop)->oop_is_array()) return;
+ defc_klassOop = SystemDictionary::object_klass();
+ }
+ instanceKlassHandle defc(THREAD, defc_klassOop);
+ defc_klassOop = NULL; // safety
+ if (defc.is_null()) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "primitive class");
+ }
+ defc->link_class(CHECK);
+
+ // convert the external string name to an internal symbol
+ symbolHandle name(THREAD, java_lang_String::as_symbol_or_null(name_str));
+ if (name.is_null()) return; // no such name
+ name_str = NULL; // safety
+
+ // convert the external string or reflective type to an internal signature
+ bool force_signature = (name() == vmSymbols::invoke_name());
+ symbolHandle type; {
+ symbolOop type_sym = NULL;
+ if (java_dyn_MethodType::is_instance(type_str)) {
+ type_sym = java_dyn_MethodType::as_signature(type_str, force_signature, CHECK);
+ } else if (java_lang_Class::is_instance(type_str)) {
+ type_sym = java_lang_Class::as_signature(type_str, force_signature, CHECK);
+ } else if (java_lang_String::is_instance(type_str)) {
+ if (force_signature) {
+ type = java_lang_String::as_symbol(type_str, CHECK);
+ } else {
+ type_sym = java_lang_String::as_symbol_or_null(type_str);
+ }
+ } else {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized type");
+ }
+ if (type_sym != NULL)
+ type = symbolHandle(THREAD, type_sym);
+ }
+ if (type.is_null()) return; // no such signature exists in the VM
+ type_str = NULL; // safety
+
+ // Time to do the lookup.
+ switch (flags & ALL_KINDS) {
+ case IS_METHOD:
+ {
+ CallInfo result;
+ {
+ EXCEPTION_MARK;
+ if ((flags & JVM_ACC_STATIC) != 0) {
+ LinkResolver::resolve_static_call(result,
+ defc, name, type, KlassHandle(), false, false, THREAD);
+ } else if (defc->is_interface()) {
+ LinkResolver::resolve_interface_call(result, Handle(), defc,
+ defc, name, type, KlassHandle(), false, false, THREAD);
+ } else {
+ LinkResolver::resolve_virtual_call(result, Handle(), defc,
+ defc, name, type, KlassHandle(), false, false, THREAD);
+ }
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return;
+ }
+ }
+ methodHandle m = result.resolved_method();
+ oop vmtarget = NULL;
+ int vmindex = methodOopDesc::nonvirtual_vtable_index;
+ if (defc->is_interface()) {
+ vmindex = klassItable::compute_itable_index(m());
+ assert(vmindex >= 0, "");
+ } else if (result.has_vtable_index()) {
+ vmindex = result.vtable_index();
+ assert(vmindex >= 0, "");
+ }
+ assert(vmindex != VM_INDEX_UNINITIALIZED, "");
+ if (vmindex < 0) {
+ assert(result.is_statically_bound(), "");
+ vmtarget = m();
+ } else {
+ vmtarget = result.resolved_klass()->as_klassOop();
+ }
+ int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS);
+ sun_dyn_MemberName::set_vmtarget(mname(), vmtarget);
+ sun_dyn_MemberName::set_vmindex(mname(), vmindex);
+ sun_dyn_MemberName::set_modifiers(mname(), mods);
+ DEBUG_ONLY(int junk; klassOop junk2);
+ assert(decode_MemberName(mname(), junk2, junk) == result.resolved_method()(),
+ "properly stored for later decoding");
+ return;
+ }
+ case IS_CONSTRUCTOR:
+ {
+ CallInfo result;
+ {
+ EXCEPTION_MARK;
+ if (name() == vmSymbols::object_initializer_name()) {
+ LinkResolver::resolve_special_call(result,
+ defc, name, type, KlassHandle(), false, THREAD);
+ } else {
+ break; // will throw after end of switch
+ }
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return;
+ }
+ }
+ assert(result.is_statically_bound(), "");
+ methodHandle m = result.resolved_method();
+ oop vmtarget = m();
+ int vmindex = methodOopDesc::nonvirtual_vtable_index;
+ int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS);
+ sun_dyn_MemberName::set_vmtarget(mname(), vmtarget);
+ sun_dyn_MemberName::set_vmindex(mname(), vmindex);
+ sun_dyn_MemberName::set_modifiers(mname(), mods);
+ DEBUG_ONLY(int junk; klassOop junk2);
+ assert(decode_MemberName(mname(), junk2, junk) == result.resolved_method()(),
+ "properly stored for later decoding");
+ return;
+ }
+ case IS_FIELD:
+ {
+ // This is taken from LinkResolver::resolve_field, sans access checks.
+ fieldDescriptor fd; // find_field initializes fd if found
+ KlassHandle sel_klass(THREAD, instanceKlass::cast(defc())->find_field(name(), type(), &fd));
+ // check if field exists; i.e., if a klass containing the field def has been selected
+ if (sel_klass.is_null()) return;
+ oop vmtarget = sel_klass->as_klassOop();
+ int vmindex = fd.offset();
+ int mods = (fd.access_flags().as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS);
+ if (vmindex == VM_INDEX_UNINITIALIZED) break; // should not happen
+ sun_dyn_MemberName::set_vmtarget(mname(), vmtarget);
+ sun_dyn_MemberName::set_vmindex(mname(), vmindex);
+ sun_dyn_MemberName::set_modifiers(mname(), mods);
+ return;
+ }
+ }
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format");
+}
+
+// Conversely, a member name which is only initialized from JVM internals
+// may have null defc, name, and type fields.
+// Resolving it plants a vmtarget/vmindex in it,
+// which refers directly to JVM internals.
+void MethodHandles::expand_MemberName(Handle mname, int suppress, TRAPS) {
+ assert(sun_dyn_MemberName::is_instance(mname()), "");
+ oop vmtarget = sun_dyn_MemberName::vmtarget(mname());
+ int vmindex = sun_dyn_MemberName::vmindex(mname());
+ if (vmtarget == NULL || vmindex == VM_INDEX_UNINITIALIZED) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to expand");
+ }
+
+ bool have_defc = (sun_dyn_MemberName::clazz(mname()) != NULL);
+ bool have_name = (sun_dyn_MemberName::name(mname()) != NULL);
+ bool have_type = (sun_dyn_MemberName::type(mname()) != NULL);
+ int flags = sun_dyn_MemberName::flags(mname());
+
+ if (suppress != 0) {
+ if (suppress & _suppress_defc) have_defc = true;
+ if (suppress & _suppress_name) have_name = true;
+ if (suppress & _suppress_type) have_type = true;
+ }
+
+ if (have_defc && have_name && have_type) return; // nothing needed
+
+ switch (flags & ALL_KINDS) {
+ case IS_METHOD:
+ case IS_CONSTRUCTOR:
+ {
+ klassOop receiver_limit = NULL;
+ int decode_flags = 0;
+ methodHandle m(THREAD, decode_vmtarget(vmtarget, vmindex, NULL,
+ receiver_limit, decode_flags));
+ if (m.is_null()) break;
+ if (!have_defc) {
+ klassOop defc = m->method_holder();
+ if (receiver_limit != NULL && receiver_limit != defc
+ && Klass::cast(receiver_limit)->is_subtype_of(defc))
+ defc = receiver_limit;
+ sun_dyn_MemberName::set_clazz(mname(), Klass::cast(defc)->java_mirror());
+ }
+ if (!have_name) {
+ //not java_lang_String::create_from_symbol; let's intern member names
+ Handle name = StringTable::intern(m->name(), CHECK);
+ sun_dyn_MemberName::set_name(mname(), name());
+ }
+ if (!have_type) {
+ Handle type = java_lang_String::create_from_symbol(m->signature(), CHECK);
+ sun_dyn_MemberName::set_type(mname(), type());
+ }
+ return;
+ }
+ case IS_FIELD:
+ {
+ // This is taken from LinkResolver::resolve_field, sans access checks.
+ if (!vmtarget->is_klass()) break;
+ if (!Klass::cast((klassOop) vmtarget)->oop_is_instance()) break;
+ instanceKlassHandle defc(THREAD, (klassOop) vmtarget);
+ bool is_static = ((flags & JVM_ACC_STATIC) != 0);
+ fieldDescriptor fd; // find_field initializes fd if found
+ if (!defc->find_field_from_offset(vmindex, is_static, &fd))
+ break; // cannot expand
+ if (!have_defc) {
+ sun_dyn_MemberName::set_clazz(mname(), defc->java_mirror());
+ }
+ if (!have_name) {
+ //not java_lang_String::create_from_symbol; let's intern member names
+ Handle name = StringTable::intern(fd.name(), CHECK);
+ sun_dyn_MemberName::set_name(mname(), name());
+ }
+ if (!have_type) {
+ Handle type = java_lang_String::create_from_symbol(fd.signature(), CHECK);
+ sun_dyn_MemberName::set_type(mname(), type());
+ }
+ return;
+ }
+ }
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format");
+}
+
+int MethodHandles::find_MemberNames(klassOop k,
+ symbolOop name, symbolOop sig,
+ int mflags, klassOop caller,
+ int skip, objArrayOop results) {
+ DEBUG_ONLY(No_Safepoint_Verifier nsv);
+ // this code contains no safepoints!
+
+ // %%% take caller into account!
+
+ if (k == NULL || !Klass::cast(k)->oop_is_instance()) return -1;
+
+ int rfill = 0, rlimit = results->length(), rskip = skip;
+ // overflow measurement:
+ int overflow = 0, overflow_limit = MAX2(1000, rlimit);
+
+ int match_flags = mflags;
+ bool search_superc = ((match_flags & SEARCH_SUPERCLASSES) != 0);
+ bool search_intfc = ((match_flags & SEARCH_INTERFACES) != 0);
+ bool local_only = !(search_superc | search_intfc);
+ bool classes_only = false;
+
+ if (name != NULL) {
+ if (name->utf8_length() == 0) return 0; // a match is not possible
+ }
+ if (sig != NULL) {
+ if (sig->utf8_length() == 0) return 0; // a match is not possible
+ if (sig->byte_at(0) == '(')
+ match_flags &= ~(IS_FIELD | IS_TYPE);
+ else
+ match_flags &= ~(IS_CONSTRUCTOR | IS_METHOD);
+ }
+
+ if ((match_flags & IS_TYPE) != 0) {
+ // NYI, and Core Reflection works quite well for this query
+ }
+
+ if ((match_flags & IS_FIELD) != 0) {
+ for (FieldStream st(k, local_only, !search_intfc); !st.eos(); st.next()) {
+ if (name != NULL && st.name() != name)
+ continue;
+ if (sig != NULL && st.signature() != sig)
+ continue;
+ // passed the filters
+ if (rskip > 0) {
+ --rskip;
+ } else if (rfill < rlimit) {
+ oop result = results->obj_at(rfill++);
+ if (!sun_dyn_MemberName::is_instance(result))
+ return -99; // caller bug!
+ MethodHandles::init_MemberName(result, st.klass()->as_klassOop(), st.access_flags(), st.offset());
+ } else if (++overflow >= overflow_limit) {
+ match_flags = 0; break; // got tired of looking at overflow
+ }
+ }
+ }
+
+ if ((match_flags & (IS_METHOD | IS_CONSTRUCTOR)) != 0) {
+ // watch out for these guys:
+ symbolOop init_name = vmSymbols::object_initializer_name();
+ symbolOop clinit_name = vmSymbols::class_initializer_name();
+ if (name == clinit_name) clinit_name = NULL; // hack for exposing <clinit>
+ bool negate_name_test = false;
+ // fix name so that it captures the intention of IS_CONSTRUCTOR
+ if (!(match_flags & IS_METHOD)) {
+ // constructors only
+ if (name == NULL) {
+ name = init_name;
+ } else if (name != init_name) {
+ return 0; // no constructors of this method name
+ }
+ } else if (!(match_flags & IS_CONSTRUCTOR)) {
+ // methods only
+ if (name == NULL) {
+ name = init_name;
+ negate_name_test = true; // if we see the name, we *omit* the entry
+ } else if (name == init_name) {
+ return 0; // no methods of this constructor name
+ }
+ } else {
+ // caller will accept either sort; no need to adjust name
+ }
+ for (MethodStream st(k, local_only, !search_intfc); !st.eos(); st.next()) {
+ methodOop m = st.method();
+ symbolOop m_name = m->name();
+ if (m_name == clinit_name)
+ continue;
+ if (name != NULL && ((m_name != name) ^ negate_name_test))
+ continue;
+ if (sig != NULL && m->signature() != sig)
+ continue;
+ // passed the filters
+ if (rskip > 0) {
+ --rskip;
+ } else if (rfill < rlimit) {
+ oop result = results->obj_at(rfill++);
+ if (!sun_dyn_MemberName::is_instance(result))
+ return -99; // caller bug!
+ MethodHandles::init_MemberName(result, m, true);
+ } else if (++overflow >= overflow_limit) {
+ match_flags = 0; break; // got tired of looking at overflow
+ }
+ }
+ }
+
+ // return number of elements we at leasted wanted to initialize
+ return rfill + overflow;
+}
+
+
+
+
+// Decode the vmtarget field of a method handle.
+// Sanitize out methodOops, klassOops, and any other non-Java data.
+// This is for debugging and reflection.
+oop MethodHandles::encode_target(Handle mh, int format, TRAPS) {
+ assert(java_dyn_MethodHandle::is_instance(mh()), "must be a MH");
+ if (format == ETF_HANDLE_OR_METHOD_NAME) {
+ oop target = java_dyn_MethodHandle::vmtarget(mh());
+ if (target == NULL) {
+ return NULL; // unformed MH
+ }
+ klassOop tklass = target->klass();
+ if (Klass::cast(tklass)->is_subclass_of(SystemDictionary::object_klass())) {
+ return target; // target is another MH (or something else?)
+ }
+ }
+ if (format == ETF_DIRECT_HANDLE) {
+ oop target = mh();
+ for (;;) {
+ if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) {
+ return target;
+ }
+ if (!java_dyn_MethodHandle::is_instance(target)){
+ return NULL; // unformed MH
+ }
+ target = java_dyn_MethodHandle::vmtarget(target);
+ }
+ }
+ // cases of metadata in MH.vmtarget:
+ // - AMH can have methodOop for static invoke with bound receiver
+ // - DMH can have methodOop for static invoke (on variable receiver)
+ // - DMH can have klassOop for dispatched (non-static) invoke
+ klassOop receiver_limit = NULL;
+ int decode_flags = 0;
+ methodOop m = decode_MethodHandle(mh(), receiver_limit, decode_flags);
+ if (m == NULL) return NULL;
+ switch (format) {
+ case ETF_REFLECT_METHOD:
+ // same as jni_ToReflectedMethod:
+ if (m->is_initializer()) {
+ return Reflection::new_constructor(m, THREAD);
+ } else {
+ return Reflection::new_method(m, UseNewReflection, false, THREAD);
+ }
+
+ case ETF_HANDLE_OR_METHOD_NAME: // method, not handle
+ case ETF_METHOD_NAME:
+ {
+ if (SystemDictionary::MemberName_klass() == NULL) break;
+ instanceKlassHandle mname_klass(THREAD, SystemDictionary::MemberName_klass());
+ mname_klass->initialize(CHECK_NULL);
+ Handle mname = mname_klass->allocate_instance_handle(CHECK_NULL);
+ sun_dyn_MemberName::set_vmindex(mname(), VM_INDEX_UNINITIALIZED);
+ bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0);
+ init_MemberName(mname(), m, do_dispatch);
+ expand_MemberName(mname, 0, CHECK_NULL);
+ return mname();
+ }
+ }
+
+ // Unknown format code.
+ char msg[50];
+ jio_snprintf(msg, sizeof(msg), "unknown getTarget format=%d", format);
+ THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), msg);
+}
+
+bool MethodHandles::class_cast_needed(klassOop src, klassOop dst) {
+ if (src == dst || dst == SystemDictionary::object_klass())
+ return false; // quickest checks
+ Klass* srck = Klass::cast(src);
+ Klass* dstk = Klass::cast(dst);
+ if (dstk->is_interface()) {
+ // interface receivers can safely be viewed as untyped,
+ // because interface calls always include a dynamic check
+ //dstk = Klass::cast(SystemDictionary::object_klass());
+ return false;
+ }
+ if (srck->is_interface()) {
+ // interface arguments must be viewed as untyped
+ //srck = Klass::cast(SystemDictionary::object_klass());
+ return true;
+ }
+ return !srck->is_subclass_of(dstk->as_klassOop());
+}
+
+static oop object_java_mirror() {
+ return Klass::cast(SystemDictionary::object_klass())->java_mirror();
+}
+
+bool MethodHandles::same_basic_type_for_arguments(BasicType src,
+ BasicType dst,
+ bool for_return) {
+ // return values can always be forgotten:
+ if (for_return && dst == T_VOID) return true;
+ assert(src != T_VOID && dst != T_VOID, "should not be here");
+ if (src == dst) return true;
+ if (type2size[src] != type2size[dst]) return false;
+ // allow reinterpretation casts for integral widening
+ if (is_subword_type(src)) { // subwords can fit in int or other subwords
+ if (dst == T_INT) // any subword fits in an int
+ return true;
+ if (src == T_BOOLEAN) // boolean fits in any subword
+ return is_subword_type(dst);
+ if (src == T_BYTE && dst == T_SHORT)
+ return true; // remaining case: byte fits in short
+ }
+ // allow float/fixed reinterpretation casts
+ if (src == T_FLOAT) return dst == T_INT;
+ if (src == T_INT) return dst == T_FLOAT;
+ if (src == T_DOUBLE) return dst == T_LONG;
+ if (src == T_LONG) return dst == T_DOUBLE;
+ return false;
+}
+
+const char* MethodHandles::check_method_receiver(methodOop m,
+ klassOop passed_recv_type) {
+ assert(!m->is_static(), "caller resp.");
+ if (passed_recv_type == NULL)
+ return "receiver type is primitive";
+ if (class_cast_needed(passed_recv_type, m->method_holder())) {
+ Klass* formal = Klass::cast(m->method_holder());
+ return SharedRuntime::generate_class_cast_message("receiver type",
+ formal->external_name());
+ }
+ return NULL; // checks passed
+}
+
+// Verify that m's signature can be called type-safely by a method handle
+// of the given method type 'mtype'.
+// It takes a TRAPS argument because it must perform symbol lookups.
+void MethodHandles::verify_method_signature(methodHandle m,
+ Handle mtype,
+ int first_ptype_pos,
+ KlassHandle insert_ptype,
+ TRAPS) {
+ objArrayHandle ptypes(THREAD, java_dyn_MethodType::ptypes(mtype()));
+ int pnum = first_ptype_pos;
+ int pmax = ptypes->length();
+ int mnum = 0; // method argument
+ const char* err = NULL;
+ for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) {
+ oop ptype_oop = NULL;
+ if (ss.at_return_type()) {
+ if (pnum != pmax)
+ { err = "too many arguments"; break; }
+ ptype_oop = java_dyn_MethodType::rtype(mtype());
+ } else {
+ if (pnum >= pmax)
+ { err = "not enough arguments"; break; }
+ if (pnum >= 0)
+ ptype_oop = ptypes->obj_at(pnum);
+ else if (insert_ptype.is_null())
+ ptype_oop = NULL;
+ else
+ ptype_oop = insert_ptype->java_mirror();
+ pnum += 1;
+ mnum += 1;
+ }
+ klassOop mklass = NULL;
+ BasicType mtype = ss.type();
+ if (mtype == T_ARRAY) mtype = T_OBJECT; // fold all refs to T_OBJECT
+ if (mtype == T_OBJECT) {
+ if (ptype_oop == NULL) {
+ // null matches any reference
+ continue;
+ }
+ // If we fail to resolve types at this point, we will throw an error.
+ symbolOop name_oop = ss.as_symbol(CHECK);
+ symbolHandle name(THREAD, name_oop);
+ instanceKlass* mk = instanceKlass::cast(m->method_holder());
+ Handle loader(THREAD, mk->class_loader());
+ Handle domain(THREAD, mk->protection_domain());
+ mklass = SystemDictionary::resolve_or_fail(name, loader, domain,
+ true, CHECK);
+ }
+ if (ptype_oop == NULL) {
+ // null does not match any non-reference; use Object to report the error
+ ptype_oop = object_java_mirror();
+ }
+ klassOop pklass = NULL;
+ BasicType ptype = java_lang_Class::as_BasicType(ptype_oop, &pklass);
+ if (!ss.at_return_type()) {
+ err = check_argument_type_change(ptype, pklass, mtype, mklass, mnum);
+ } else {
+ err = check_return_type_change(mtype, mklass, ptype, pklass); // note reversal!
+ }
+ if (err != NULL) break;
+ }
+
+ if (err != NULL) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), err);
+ }
+}
+
+// Main routine for verifying the MethodHandle.type of a proposed
+// direct or bound-direct method handle.
+void MethodHandles::verify_method_type(methodHandle m,
+ Handle mtype,
+ bool has_bound_recv,
+ KlassHandle bound_recv_type,
+ TRAPS) {
+ bool m_needs_receiver = !m->is_static();
+
+ const char* err = NULL;
+
+ int first_ptype_pos = m_needs_receiver ? 1 : 0;
+ if (has_bound_recv && err == NULL) {
+ first_ptype_pos -= 1;
+ if (m_needs_receiver && bound_recv_type.is_null())
+ { err = "bound receiver is not an object"; goto die; }
+ }
+
+ if (m_needs_receiver && err == NULL) {
+ objArrayOop ptypes = java_dyn_MethodType::ptypes(mtype());
+ if (ptypes->length() < first_ptype_pos)
+ { err = "receiver argument is missing"; goto die; }
+ if (first_ptype_pos == -1)
+ err = check_method_receiver(m(), bound_recv_type->as_klassOop());
+ else
+ err = check_method_receiver(m(), java_lang_Class::as_klassOop(ptypes->obj_at(0)));
+ if (err != NULL) goto die;
+ }
+
+ // Check the other arguments for mistypes.
+ verify_method_signature(m, mtype, first_ptype_pos, bound_recv_type, CHECK);
+ return;
+
+ die:
+ THROW_MSG(vmSymbols::java_lang_InternalError(), err);
+}
+
+void MethodHandles::verify_vmslots(Handle mh, TRAPS) {
+ // Verify vmslots.
+ int check_slots = argument_slot_count(java_dyn_MethodHandle::type(mh()));
+ if (java_dyn_MethodHandle::vmslots(mh()) != check_slots) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH");
+ }
+}
+
+void MethodHandles::verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS) {
+ // Verify that argslot points at the given argnum.
+ int check_slot = argument_slot(java_dyn_MethodHandle::type(mh()), argnum);
+ if (argslot != check_slot || argslot < 0) {
+ const char* fmt = "for argnum of %d, vmargslot is %d, should be %d";
+ size_t msglen = strlen(fmt) + 3*11 + 1;
+ char* msg = NEW_RESOURCE_ARRAY(char, msglen);
+ jio_snprintf(msg, msglen, fmt, argnum, argslot, check_slot);
+ THROW_MSG(vmSymbols::java_lang_InternalError(), msg);
+ }
+}
+
+// Verify the correspondence between two method types.
+// Apart from the advertised changes, caller method type X must
+// be able to invoke the callee method Y type with no violations
+// of type integrity.
+// Return NULL if all is well, else a short error message.
+const char* MethodHandles::check_method_type_change(oop src_mtype, int src_beg, int src_end,
+ int insert_argnum, oop insert_type,
+ int change_argnum, oop change_type,
+ int delete_argnum,
+ oop dst_mtype, int dst_beg, int dst_end) {
+ objArrayOop src_ptypes = java_dyn_MethodType::ptypes(src_mtype);
+ objArrayOop dst_ptypes = java_dyn_MethodType::ptypes(dst_mtype);
+
+ int src_max = src_ptypes->length();
+ int dst_max = dst_ptypes->length();
+
+ if (src_end == -1) src_end = src_max;
+ if (dst_end == -1) dst_end = dst_max;
+
+ assert(0 <= src_beg && src_beg <= src_end && src_end <= src_max, "oob");
+ assert(0 <= dst_beg && dst_beg <= dst_end && dst_end <= dst_max, "oob");
+
+ // pending actions; set to -1 when done:
+ int ins_idx = insert_argnum, chg_idx = change_argnum, del_idx = delete_argnum;
+
+ const char* err = NULL;
+
+ // Walk along each array of parameter types, including a virtual
+ // NULL end marker at the end of each.
+ for (int src_idx = src_beg, dst_idx = dst_beg;
+ (src_idx <= src_end && dst_idx <= dst_end);
+ src_idx++, dst_idx++) {
+ oop src_type = (src_idx == src_end) ? oop(NULL) : src_ptypes->obj_at(src_idx);
+ oop dst_type = (dst_idx == dst_end) ? oop(NULL) : dst_ptypes->obj_at(dst_idx);
+ bool fix_null_src_type = false;
+
+ // Perform requested edits.
+ if (ins_idx == src_idx) {
+ // note that the inserted guy is never affected by a change or deletion
+ ins_idx = -1;
+ src_type = insert_type;
+ fix_null_src_type = true;
+ --src_idx; // back up to process src type on next loop
+ src_idx = src_end;
+ } else {
+ // note that the changed guy can be immediately deleted
+ if (chg_idx == src_idx) {
+ chg_idx = -1;
+ assert(src_idx < src_end, "oob");
+ src_type = change_type;
+ fix_null_src_type = true;
+ }
+ if (del_idx == src_idx) {
+ del_idx = -1;
+ assert(src_idx < src_end, "oob");
+ --dst_idx;
+ continue; // rerun loop after skipping this position
+ }
+ }
+
+ if (src_type == NULL && fix_null_src_type)
+ // explicit null in this case matches any dest reference
+ src_type = (java_lang_Class::is_primitive(dst_type) ? object_java_mirror() : dst_type);
+
+ // Compare the two argument types.
+ if (src_type != dst_type) {
+ if (src_type == NULL) return "not enough arguments";
+ if (dst_type == NULL) return "too many arguments";
+ err = check_argument_type_change(src_type, dst_type, dst_idx);
+ if (err != NULL) return err;
+ }
+ }
+
+ // Now compare return types also.
+ oop src_rtype = java_dyn_MethodType::rtype(src_mtype);
+ oop dst_rtype = java_dyn_MethodType::rtype(dst_mtype);
+ if (src_rtype != dst_rtype) {
+ err = check_return_type_change(dst_rtype, src_rtype); // note reversal!
+ if (err != NULL) return err;
+ }
+
+ assert(err == NULL, "");
+ return NULL; // all is well
+}
+
+
+const char* MethodHandles::check_argument_type_change(BasicType src_type,
+ klassOop src_klass,
+ BasicType dst_type,
+ klassOop dst_klass,
+ int argnum) {
+ const char* err = NULL;
+
+ // just in case:
+ if (src_type == T_ARRAY) src_type = T_OBJECT;
+ if (dst_type == T_ARRAY) dst_type = T_OBJECT;
+
+ // Produce some nice messages if VerifyMethodHandles is turned on:
+ if (!same_basic_type_for_arguments(src_type, dst_type, (argnum < 0))) {
+ if (src_type == T_OBJECT) {
+ err = ((argnum >= 0)
+ ? "type mismatch: passing a %s for method argument #%d, which expects primitive %s"
+ : "type mismatch: returning a %s, but caller expects primitive %s");
+ } else if (dst_type == T_OBJECT) {
+ err = ((argnum < 0)
+ ? "type mismatch: passing a primitive %s for method argument #%d, which expects %s"
+ : "type mismatch: returning a primitive %s, but caller expects %s");
+ } else {
+ err = ((argnum < 0)
+ ? "type mismatch: passing a %s for method argument #%d, which expects %s"
+ : "type mismatch: returning a %s, but caller expects %s");
+ }
+ } else if (src_type == T_OBJECT && class_cast_needed(src_klass, dst_klass)) {
+ if (!class_cast_needed(dst_klass, src_klass)) {
+ err = ((argnum < 0)
+ ? "cast required: passing a %s for method argument #%d, which expects %s"
+ : "cast required: returning a %s, but caller expects %s");
+ } else {
+ err = ((argnum < 0)
+ ? "reference mismatch: passing a %s for method argument #%d, which expects %s"
+ : "reference mismatch: returning a %s, but caller expects %s");
+ }
+ } else {
+ // passed the obstacle course
+ return NULL;
+ }
+
+ // format, format, format
+ const char* src_name = type2name(src_type);
+ const char* dst_name = type2name(dst_type);
+ if (src_type == T_OBJECT) src_name = Klass::cast(src_klass)->external_name();
+ if (dst_type == T_OBJECT) dst_name = Klass::cast(dst_klass)->external_name();
+ if (src_name == NULL) src_name = "unknown type";
+ if (dst_name == NULL) dst_name = "unknown type";
+
+ size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11);
+ char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1);
+ if (argnum >= 0) {
+ assert(strstr(err, "%d") != NULL, "");
+ jio_snprintf(msg, msglen, err, src_name, argnum, dst_name);
+ } else {
+ assert(strstr(err, "%d") == NULL, "");
+ jio_snprintf(msg, msglen, err, src_name, dst_name);
+ }
+ return msg;
+}
+
+// Compute the depth within the stack of the given argument, i.e.,
+// the combined size of arguments to the right of the given argument.
+// For the last argument (ptypes.length-1) this will be zero.
+// For the first argument (0) this will be the size of all
+// arguments but that one. For the special number -1, this
+// will be the size of all arguments, including the first.
+// If the argument is neither -1 nor a valid argument index,
+// then return a negative number. Otherwise, the result
+// is in the range [0..vmslots] inclusive.
+int MethodHandles::argument_slot(oop method_type, int arg) {
+ objArrayOop ptypes = java_dyn_MethodType::ptypes(method_type);
+ int argslot = 0;
+ int len = ptypes->length();
+ if (arg < -1 || arg >= len) return -99;
+ for (int i = len-1; i > arg; i--) {
+ BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i));
+ argslot += type2size[bt];
+ }
+ assert(argument_slot_to_argnum(method_type, argslot) == arg, "inverse works");
+ return argslot;
+}
+
+// Given a slot number, return the argument number.
+int MethodHandles::argument_slot_to_argnum(oop method_type, int query_argslot) {
+ objArrayOop ptypes = java_dyn_MethodType::ptypes(method_type);
+ int argslot = 0;
+ int len = ptypes->length();
+ for (int i = len-1; i >= 0; i--) {
+ if (query_argslot == argslot) return i;
+ BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i));
+ argslot += type2size[bt];
+ }
+ // return pseudo-arg deepest in stack:
+ if (query_argslot == argslot) return -1;
+ return -99; // oob slot, or splitting a double-slot arg
+}
+
+methodHandle MethodHandles::dispatch_decoded_method(methodHandle m,
+ KlassHandle receiver_limit,
+ int decode_flags,
+ KlassHandle receiver_klass,
+ TRAPS) {
+ assert((decode_flags & ~_DMF_DIRECT_MASK) == 0, "must be direct method reference");
+ assert((decode_flags & _dmf_has_receiver) != 0, "must have a receiver or first reference argument");
+
+ if (!m->is_static() &&
+ (receiver_klass.is_null() || !receiver_klass->is_subtype_of(m->method_holder())))
+ // given type does not match class of method, or receiver is null!
+ // caller should have checked this, but let's be extra careful...
+ return methodHandle();
+
+ if (receiver_limit.not_null() &&
+ (receiver_klass.not_null() && !receiver_klass->is_subtype_of(receiver_limit())))
+ // given type is not limited to the receiver type
+ // note that a null receiver can match any reference value, for a static method
+ return methodHandle();
+
+ if (!(decode_flags & MethodHandles::_dmf_does_dispatch)) {
+ // pre-dispatched or static method (null receiver is OK for static)
+ return m;
+
+ } else if (receiver_klass.is_null()) {
+ // null receiver value; cannot dispatch
+ return methodHandle();
+
+ } else if (!(decode_flags & MethodHandles::_dmf_from_interface)) {
+ // perform virtual dispatch
+ int vtable_index = m->vtable_index();
+ guarantee(vtable_index >= 0, "valid vtable index");
+
+ // receiver_klass might be an arrayKlassOop but all vtables start at
+ // the same place. The cast is to avoid virtual call and assertion.
+ // See also LinkResolver::runtime_resolve_virtual_method.
+ instanceKlass* inst = (instanceKlass*)Klass::cast(receiver_klass());
+ DEBUG_ONLY(inst->verify_vtable_index(vtable_index));
+ methodOop m_oop = inst->method_at_vtable(vtable_index);
+ return methodHandle(THREAD, m_oop);
+
+ } else {
+ // perform interface dispatch
+ int itable_index = klassItable::compute_itable_index(m());
+ guarantee(itable_index >= 0, "valid itable index");
+ instanceKlass* inst = instanceKlass::cast(receiver_klass());
+ methodOop m_oop = inst->method_at_itable(m->method_holder(), itable_index, THREAD);
+ return methodHandle(THREAD, m_oop);
+ }
+}
+
+void MethodHandles::verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS) {
+ // Verify type.
+ Handle mtype(THREAD, java_dyn_MethodHandle::type(mh()));
+ verify_method_type(m, mtype, false, KlassHandle(), CHECK);
+
+ // Verify vmslots.
+ if (java_dyn_MethodHandle::vmslots(mh()) != m->size_of_parameters()) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in DMH");
+ }
+}
+
+void MethodHandles::init_DirectMethodHandle(Handle mh, methodHandle m, bool do_dispatch, TRAPS) {
+ // Check arguments.
+ if (mh.is_null() || m.is_null() ||
+ (!do_dispatch && m->is_abstract())) {
+ THROW(vmSymbols::java_lang_InternalError());
+ }
+
+ java_dyn_MethodHandle::init_vmslots(mh());
+
+ if (VerifyMethodHandles) {
+ // The privileged code which invokes this routine should not make
+ // a mistake about types, but it's better to verify.
+ verify_DirectMethodHandle(mh, m, CHECK);
+ }
+
+ // Finally, after safety checks are done, link to the target method.
+ // We will follow the same path as the latter part of
+ // InterpreterRuntime::resolve_invoke(), which first finds the method
+ // and then decides how to populate the constant pool cache entry
+ // that links the interpreter calls to the method. We need the same
+ // bits, and will use the same calling sequence code.
+
+ int vmindex = methodOopDesc::garbage_vtable_index;
+ oop vmtarget = NULL;
+
+ instanceKlass::cast(m->method_holder())->link_class(CHECK);
+
+ MethodHandleEntry* me = NULL;
+ if (do_dispatch && Klass::cast(m->method_holder())->is_interface()) {
+ // We are simulating an invokeinterface instruction.
+ // (We might also be simulating an invokevirtual on a miranda method,
+ // but it is safe to treat it as an invokeinterface.)
+ assert(!m->can_be_statically_bound(), "no final methods on interfaces");
+ vmindex = klassItable::compute_itable_index(m());
+ assert(vmindex >= 0, "(>=0) == do_dispatch");
+ // Set up same bits as ConstantPoolCacheEntry::set_interface_call().
+ vmtarget = m->method_holder(); // the interface
+ me = MethodHandles::entry(MethodHandles::_invokeinterface_mh);
+ } else if (!do_dispatch || m->can_be_statically_bound()) {
+ // We are simulating an invokestatic or invokespecial instruction.
+ // Set up the method pointer, just like ConstantPoolCacheEntry::set_method().
+ vmtarget = m();
+ // this does not help dispatch, but it will make it possible to parse this MH:
+ vmindex = methodOopDesc::nonvirtual_vtable_index;
+ assert(vmindex < 0, "(>=0) == do_dispatch");
+ if (!m->is_static()) {
+ me = MethodHandles::entry(MethodHandles::_invokespecial_mh);
+ } else {
+ me = MethodHandles::entry(MethodHandles::_invokestatic_mh);
+ // Part of the semantics of a static call is an initialization barrier.
+ // For a DMH, it is done now, when the handle is created.
+ Klass* k = Klass::cast(m->method_holder());
+ if (k->should_be_initialized()) {
+ k->initialize(CHECK);
+ }
+ }
+ } else {
+ // We are simulating an invokevirtual instruction.
+ // Set up the vtable index, just like ConstantPoolCacheEntry::set_method().
+ // The key logic is LinkResolver::runtime_resolve_virtual_method.
+ vmindex = m->vtable_index();
+ vmtarget = m->method_holder();
+ me = MethodHandles::entry(MethodHandles::_invokevirtual_mh);
+ }
+
+ if (me == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+
+ sun_dyn_DirectMethodHandle::set_vmtarget(mh(), vmtarget);
+ sun_dyn_DirectMethodHandle::set_vmindex(mh(), vmindex);
+ DEBUG_ONLY(int flags; klassOop rlimit);
+ assert(MethodHandles::decode_method(mh(), rlimit, flags) == m(),
+ "properly stored for later decoding");
+ DEBUG_ONLY(bool actual_do_dispatch = ((flags & _dmf_does_dispatch) != 0));
+ assert(!(actual_do_dispatch && !do_dispatch),
+ "do not perform dispatch if !do_dispatch specified");
+ assert(actual_do_dispatch == (vmindex >= 0), "proper later decoding of do_dispatch");
+ assert(decode_MethodHandle_stack_pushes(mh()) == 0, "DMH does not move stack");
+
+ // Done!
+ java_dyn_MethodHandle::set_vmentry(mh(), me);
+}
+
+void MethodHandles::verify_BoundMethodHandle_with_receiver(Handle mh,
+ methodHandle m,
+ TRAPS) {
+ // Verify type.
+ oop receiver = sun_dyn_BoundMethodHandle::argument(mh());
+ Handle mtype(THREAD, java_dyn_MethodHandle::type(mh()));
+ KlassHandle bound_recv_type;
+ if (receiver != NULL) bound_recv_type = KlassHandle(THREAD, receiver->klass());
+ verify_method_type(m, mtype, true, bound_recv_type, CHECK);
+
+ int receiver_pos = m->size_of_parameters() - 1;
+
+ // Verify MH.vmargslot, which should point at the bound receiver.
+ verify_vmargslot(mh, -1, sun_dyn_BoundMethodHandle::vmargslot(mh()), CHECK);
+ //verify_vmslots(mh, CHECK);
+
+ // Verify vmslots.
+ if (java_dyn_MethodHandle::vmslots(mh()) != receiver_pos) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH (receiver)");
+ }
+}
+
+// Initialize a BMH with a receiver bound directly to a methodOop.
+void MethodHandles::init_BoundMethodHandle_with_receiver(Handle mh,
+ methodHandle original_m,
+ KlassHandle receiver_limit,
+ int decode_flags,
+ TRAPS) {
+ // Check arguments.
+ if (mh.is_null() || original_m.is_null()) {
+ THROW(vmSymbols::java_lang_InternalError());
+ }
+
+ KlassHandle receiver_klass;
+ {
+ oop receiver_oop = sun_dyn_BoundMethodHandle::argument(mh());
+ if (receiver_oop != NULL)
+ receiver_klass = KlassHandle(THREAD, receiver_oop->klass());
+ }
+ methodHandle m = dispatch_decoded_method(original_m,
+ receiver_limit, decode_flags,
+ receiver_klass,
+ CHECK);
+ if (m.is_null()) { THROW(vmSymbols::java_lang_InternalError()); }
+ if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); }
+
+ java_dyn_MethodHandle::init_vmslots(mh());
+
+ if (VerifyMethodHandles) {
+ verify_BoundMethodHandle_with_receiver(mh, m, CHECK);
+ }
+
+ sun_dyn_BoundMethodHandle::set_vmtarget(mh(), m());
+
+ DEBUG_ONLY(int junk; klassOop junk2);
+ assert(MethodHandles::decode_method(mh(), junk2, junk) == m(), "properly stored for later decoding");
+ assert(decode_MethodHandle_stack_pushes(mh()) == 1, "BMH pushes one stack slot");
+
+ // Done!
+ java_dyn_MethodHandle::set_vmentry(mh(), MethodHandles::entry(MethodHandles::_bound_ref_direct_mh));
+}
+
+void MethodHandles::verify_BoundMethodHandle(Handle mh, Handle target, int argnum,
+ bool direct_to_method, TRAPS) {
+ Handle ptype_handle(THREAD,
+ java_dyn_MethodType::ptype(java_dyn_MethodHandle::type(target()), argnum));
+ KlassHandle ptype_klass;
+ BasicType ptype = java_lang_Class::as_BasicType(ptype_handle(), &ptype_klass);
+ int slots_pushed = type2size[ptype];
+
+ oop argument = sun_dyn_BoundMethodHandle::argument(mh());
+
+ const char* err = NULL;
+
+ switch (ptype) {
+ case T_OBJECT:
+ if (argument != NULL)
+ // we must implicitly convert from the arg type to the outgoing ptype
+ err = check_argument_type_change(T_OBJECT, argument->klass(), ptype, ptype_klass(), argnum);
+ break;
+
+ case T_ARRAY: case T_VOID:
+ assert(false, "array, void do not appear here");
+ default:
+ if (ptype != T_INT && !is_subword_type(ptype)) {
+ err = "unexpected parameter type";
+ break;
+ }
+ // check subrange of Integer.value, if necessary
+ if (argument == NULL || argument->klass() != SystemDictionary::int_klass()) {
+ err = "bound integer argument must be of type java.lang.Integer";
+ break;
+ }
+ if (ptype != T_INT) {
+ int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT);
+ jint value = argument->int_field(value_offset);
+ int vminfo = adapter_subword_vminfo(ptype);
+ jint subword = truncate_subword_from_vminfo(value, vminfo);
+ if (value != subword) {
+ err = "bound subword value does not fit into the subword type";
+ break;
+ }
+ }
+ break;
+ case T_FLOAT:
+ case T_DOUBLE:
+ case T_LONG:
+ {
+ // we must implicitly convert from the unboxed arg type to the outgoing ptype
+ BasicType argbox = java_lang_boxing_object::basic_type(argument);
+ if (argbox != ptype) {
+ err = check_argument_type_change(T_OBJECT, (argument == NULL
+ ? SystemDictionary::object_klass()
+ : argument->klass()),
+ ptype, ptype_klass(), argnum);
+ assert(err != NULL, "this must be an error");
+ }
+ break;
+ }
+ }
+
+ if (err == NULL) {
+ DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
+ if (direct_to_method) {
+ assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots");
+ assert(slots_pushed <= MethodHandlePushLimit, "");
+ } else {
+ int prev_pushes = decode_MethodHandle_stack_pushes(target());
+ assert(this_pushes == slots_pushed + prev_pushes, "BMH stack motion must be correct");
+ // do not blow the stack; use a Java-based adapter if this limit is exceeded
+ if (slots_pushed + prev_pushes > MethodHandlePushLimit)
+ err = "too many bound parameters";
+ }
+ }
+
+ if (err == NULL) {
+ // Verify the rest of the method type.
+ err = check_method_type_insertion(java_dyn_MethodHandle::type(mh()),
+ argnum, ptype_handle(),
+ java_dyn_MethodHandle::type(target()));
+ }
+
+ if (err != NULL) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), err);
+ }
+}
+
+void MethodHandles::init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
+ // Check arguments.
+ if (mh.is_null() || target.is_null() || !java_dyn_MethodHandle::is_instance(target())) {
+ THROW(vmSymbols::java_lang_InternalError());
+ }
+
+ java_dyn_MethodHandle::init_vmslots(mh());
+
+ if (VerifyMethodHandles) {
+ int insert_after = argnum - 1;
+ verify_vmargslot(mh, insert_after, sun_dyn_BoundMethodHandle::vmargslot(mh()), CHECK);
+ verify_vmslots(mh, CHECK);
+ }
+
+ // If (a) the target is a direct non-dispatched method handle,
+ // or (b) the target is a dispatched direct method handle and we
+ // are binding the receiver, cut out the middle-man.
+ // Do this by decoding the DMH and using its methodOop directly as vmtarget.
+ bool direct_to_method = false;
+ if (OptimizeMethodHandles &&
+ target->klass() == SystemDictionary::DirectMethodHandle_klass() &&
+ (argnum == 0 || sun_dyn_DirectMethodHandle::vmindex(target()) < 0)) {
+ int decode_flags = 0; klassOop receiver_limit_oop = NULL;
+ methodHandle m(THREAD, decode_method(target(), receiver_limit_oop, decode_flags));
+ if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "DMH failed to decode"); }
+ DEBUG_ONLY(int m_vmslots = m->size_of_parameters() - 1); // pos. of 1st arg.
+ assert(sun_dyn_BoundMethodHandle::vmslots(mh()) == m_vmslots, "type w/ m sig");
+ if (argnum == 0 && (decode_flags & _dmf_has_receiver) != 0) {
+ KlassHandle receiver_limit(THREAD, receiver_limit_oop);
+ init_BoundMethodHandle_with_receiver(mh, m,
+ receiver_limit, decode_flags,
+ CHECK);
+ return;
+ }
+
+ // Even if it is not a bound receiver, we still might be able
+ // to bind another argument and still invoke the methodOop directly.
+ if (!(decode_flags & _dmf_does_dispatch)) {
+ direct_to_method = true;
+ sun_dyn_BoundMethodHandle::set_vmtarget(mh(), m());
+ }
+ }
+ if (!direct_to_method)
+ sun_dyn_BoundMethodHandle::set_vmtarget(mh(), target());
+
+ if (VerifyMethodHandles) {
+ verify_BoundMethodHandle(mh, target, argnum, direct_to_method, CHECK);
+ }
+
+ // Next question: Is this a ref, int, or long bound value?
+ oop ptype_oop = java_dyn_MethodType::ptype(java_dyn_MethodHandle::type(target()), argnum);
+ BasicType ptype = java_lang_Class::as_BasicType(ptype_oop);
+ int slots_pushed = type2size[ptype];
+
+ MethodHandleEntry* me = NULL;
+ if (ptype == T_OBJECT) {
+ if (direct_to_method) me = MethodHandles::entry(_bound_ref_direct_mh);
+ else me = MethodHandles::entry(_bound_ref_mh);
+ } else if (slots_pushed == 2) {
+ if (direct_to_method) me = MethodHandles::entry(_bound_long_direct_mh);
+ else me = MethodHandles::entry(_bound_long_mh);
+ } else if (slots_pushed == 1) {
+ if (direct_to_method) me = MethodHandles::entry(_bound_int_direct_mh);
+ else me = MethodHandles::entry(_bound_int_mh);
+ } else {
+ assert(false, "");
+ }
+
+ // Done!
+ java_dyn_MethodHandle::set_vmentry(mh(), me);
+}
+
+static void throw_InternalError_for_bad_conversion(int conversion, const char* err, TRAPS) {
+ char msg[200];
+ jio_snprintf(msg, sizeof(msg), "bad adapter (conversion=0x%08x): %s", conversion, err);
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), msg);
+}
+
+void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
+ jint conversion = sun_dyn_AdapterMethodHandle::conversion(mh());
+ int argslot = sun_dyn_AdapterMethodHandle::vmargslot(mh());
+
+ verify_vmargslot(mh, argnum, argslot, CHECK);
+ verify_vmslots(mh, CHECK);
+
+ jint conv_op = adapter_conversion_op(conversion);
+ if (!conv_op_valid(conv_op)) {
+ throw_InternalError_for_bad_conversion(conversion, "unknown conversion op", THREAD);
+ return;
+ }
+ EntryKind ek = adapter_entry_kind(conv_op);
+
+ int stack_move = adapter_conversion_stack_move(conversion);
+ BasicType src = adapter_conversion_src_type(conversion);
+ BasicType dest = adapter_conversion_dest_type(conversion);
+ int vminfo = adapter_conversion_vminfo(conversion); // should be zero
+
+ Handle argument(THREAD, sun_dyn_AdapterMethodHandle::argument(mh()));
+ Handle target(THREAD, sun_dyn_AdapterMethodHandle::vmtarget(mh()));
+ Handle src_mtype(THREAD, java_dyn_MethodHandle::type(mh()));
+ Handle dst_mtype(THREAD, java_dyn_MethodHandle::type(target()));
+
+ const char* err = NULL;
+
+ if (err == NULL) {
+ // Check that the correct argument is supplied, but only if it is required.
+ switch (ek) {
+ case _adapter_check_cast: // target type of cast
+ case _adapter_ref_to_prim: // wrapper type from which to unbox
+ case _adapter_prim_to_ref: // wrapper type to box into
+ case _adapter_collect_args: // array type to collect into
+ case _adapter_spread_args: // array type to spread from
+ if (!java_lang_Class::is_instance(argument())
+ || java_lang_Class::is_primitive(argument()))
+ { err = "adapter requires argument of type java.lang.Class"; break; }
+ if (ek == _adapter_collect_args ||
+ ek == _adapter_spread_args) {
+ // Make sure it is a suitable collection type. (Array, for now.)
+ Klass* ak = Klass::cast(java_lang_Class::as_klassOop(argument()));
+ if (!ak->oop_is_objArray()) {
+ { err = "adapter requires argument of type java.lang.Class<Object[]>"; break; }
+ }
+ }
+ break;
+ case _adapter_flyby:
+ case _adapter_ricochet:
+ if (!java_dyn_MethodHandle::is_instance(argument()))
+ { err = "MethodHandle adapter argument required"; break; }
+ break;
+ default:
+ if (argument.not_null())
+ { err = "adapter has spurious argument"; break; }
+ break;
+ }
+ }
+
+ if (err == NULL) {
+ // Check that the src/dest types are supplied if needed.
+ switch (ek) {
+ case _adapter_prim_to_prim:
+ if (!is_java_primitive(src) || !is_java_primitive(dest) || src == dest) {
+ err = "adapter requires primitive src/dest conversion subfields"; break;
+ }
+ if ( (src == T_FLOAT || src == T_DOUBLE) && !(dest == T_FLOAT || dest == T_DOUBLE) ||
+ !(src == T_FLOAT || src == T_DOUBLE) && (dest == T_FLOAT || dest == T_DOUBLE)) {
+ err = "adapter cannot convert beween floating and fixed-point"; break;
+ }
+ break;
+ case _adapter_ref_to_prim:
+ if (src != T_OBJECT || !is_java_primitive(dest)
+ || argument() != Klass::cast(SystemDictionary::box_klass(dest))->java_mirror()) {
+ err = "adapter requires primitive dest conversion subfield"; break;
+ }
+ break;
+ case _adapter_prim_to_ref:
+ if (!is_java_primitive(src) || dest != T_OBJECT
+ || argument() != Klass::cast(SystemDictionary::box_klass(src))->java_mirror()) {
+ err = "adapter requires primitive src conversion subfield"; break;
+ }
+ break;
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ {
+ if (!src || src != dest) {
+ err = "adapter requires src/dest conversion subfields for swap"; break;
+ }
+ int swap_size = type2size[src];
+ oop src_mtype = sun_dyn_AdapterMethodHandle::type(target());
+ oop dest_mtype = sun_dyn_AdapterMethodHandle::type(mh());
+ int slot_limit = sun_dyn_AdapterMethodHandle::vmslots(src_mtype);
+ int src_slot = argslot;
+ int dest_slot = vminfo;
+ bool rotate_up = (src_slot > dest_slot); // upward rotation
+ int src_arg = argnum;
+ int dest_arg = argument_slot_to_argnum(dest_mtype, dest_slot);
+ verify_vmargslot(mh, dest_arg, dest_slot, CHECK);
+ if (!(dest_slot >= src_slot + swap_size) &&
+ !(src_slot >= dest_slot + swap_size)) {
+ err = "source, destination slots must be distinct";
+ } else if (ek == _adapter_swap_args && !(src_slot > dest_slot)) {
+ err = "source of swap must be deeper in stack";
+ } else if (ek == _adapter_swap_args) {
+ err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, dest_arg),
+ java_dyn_MethodType::ptype(dest_mtype, src_arg),
+ dest_arg);
+ } else if (ek == _adapter_rot_args) {
+ if (rotate_up) {
+ assert((src_slot > dest_slot) && (src_arg < dest_arg), "");
+ // rotate up: [dest_slot..src_slot-ss] --> [dest_slot+ss..src_slot]
+ // that is: [src_arg+1..dest_arg] --> [src_arg..dest_arg-1]
+ for (int i = src_arg+1; i <= dest_arg && err == NULL; i++) {
+ err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, i),
+ java_dyn_MethodType::ptype(dest_mtype, i-1),
+ i);
+ }
+ } else { // rotate down
+ assert((src_slot < dest_slot) && (src_arg > dest_arg), "");
+ // rotate down: [src_slot+ss..dest_slot] --> [src_slot..dest_slot-ss]
+ // that is: [dest_arg..src_arg-1] --> [dst_arg+1..src_arg]
+ for (int i = dest_arg; i <= src_arg-1 && err == NULL; i++) {
+ err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, i),
+ java_dyn_MethodType::ptype(dest_mtype, i+1),
+ i);
+ }
+ }
+ }
+ if (err == NULL)
+ err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, src_arg),
+ java_dyn_MethodType::ptype(dest_mtype, dest_arg),
+ src_arg);
+ }
+ break;
+ case _adapter_collect_args:
+ case _adapter_spread_args:
+ {
+ BasicType coll_type = (ek == _adapter_collect_args) ? dest : src;
+ BasicType elem_type = (ek == _adapter_collect_args) ? src : dest;
+ if (coll_type != T_OBJECT || elem_type != T_OBJECT) {
+ err = "adapter requires src/dest subfields"; break;
+ // later:
+ // - consider making coll be a primitive array
+ // - consider making coll be a heterogeneous collection
+ }
+ }
+ break;
+ default:
+ if (src != 0 || dest != 0) {
+ err = "adapter has spurious src/dest conversion subfields"; break;
+ }
+ break;
+ }
+ }
+
+ if (err == NULL) {
+ // Check the stack_move subfield.
+ // It must always report the net change in stack size, positive or negative.
+ int slots_pushed = stack_move / stack_move_unit();
+ switch (ek) {
+ case _adapter_prim_to_prim:
+ case _adapter_ref_to_prim:
+ case _adapter_prim_to_ref:
+ if (slots_pushed != type2size[dest] - type2size[src]) {
+ err = "wrong stack motion for primitive conversion";
+ }
+ break;
+ case _adapter_dup_args:
+ if (slots_pushed <= 0) {
+ err = "adapter requires conversion subfield slots_pushed > 0";
+ }
+ break;
+ case _adapter_drop_args:
+ if (slots_pushed >= 0) {
+ err = "adapter requires conversion subfield slots_pushed < 0";
+ }
+ break;
+ case _adapter_collect_args:
+ if (slots_pushed > 1) {
+ err = "adapter requires conversion subfield slots_pushed <= 1";
+ }
+ break;
+ case _adapter_spread_args:
+ if (slots_pushed < -1) {
+ err = "adapter requires conversion subfield slots_pushed >= -1";
+ }
+ break;
+ default:
+ if (stack_move != 0) {
+ err = "adapter has spurious stack_move conversion subfield";
+ }
+ break;
+ }
+ if (err == NULL && stack_move != slots_pushed * stack_move_unit()) {
+ err = "stack_move conversion subfield must be multiple of stack_move_unit";
+ }
+ }
+
+ if (err == NULL) {
+ // Make sure this adapter does not push too deeply.
+ int slots_pushed = stack_move / stack_move_unit();
+ int this_vmslots = java_dyn_MethodHandle::vmslots(mh());
+ int prev_vmslots = java_dyn_MethodHandle::vmslots(target());
+ if (slots_pushed != (this_vmslots - prev_vmslots)) {
+ err = "stack_move inconsistent with previous and current MethodType vmslots";
+ } else if (slots_pushed > 0) {
+ // verify stack_move against MethodHandlePushLimit
+ int prev_pushes = decode_MethodHandle_stack_pushes(target());
+ // do not blow the stack; use a Java-based adapter if this limit is exceeded
+ if (slots_pushed + prev_pushes > MethodHandlePushLimit) {
+ err = "adapter pushes too many parameters";
+ }
+ }
+
+ // While we're at it, check that the stack motion decoder works:
+ DEBUG_ONLY(int prev_pushes = decode_MethodHandle_stack_pushes(target()));
+ DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
+ assert(this_pushes == slots_pushed + prev_pushes, "AMH stack motion must be correct");
+ }
+
+ if (err == NULL && vminfo != 0) {
+ switch (ek) {
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ break; // OK
+ default:
+ err = "vminfo subfield is reserved to the JVM";
+ }
+ }
+
+ // Do additional ad hoc checks.
+ if (err == NULL) {
+ switch (ek) {
+ case _adapter_retype_only:
+ err = check_method_type_passthrough(src_mtype(), dst_mtype());
+ break;
+
+ case _adapter_check_cast:
+ {
+ // The actual value being checked must be a reference:
+ err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype(), argnum),
+ object_java_mirror(), argnum);
+ if (err != NULL) break;
+
+ // The output of the cast must fit with the destination argument:
+ Handle cast_class = argument;
+ err = check_method_type_conversion(src_mtype(),
+ argnum, cast_class(),
+ dst_mtype());
+ }
+ break;
+
+ // %%% TO DO: continue in remaining cases to verify src/dst_mtype if VerifyMethodHandles
+ }
+ }
+
+ if (err != NULL) {
+ throw_InternalError_for_bad_conversion(conversion, err, THREAD);
+ return;
+ }
+
+}
+
+void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
+ oop argument = sun_dyn_AdapterMethodHandle::argument(mh());
+ int argslot = sun_dyn_AdapterMethodHandle::vmargslot(mh());
+ jint conversion = sun_dyn_AdapterMethodHandle::conversion(mh());
+ jint conv_op = adapter_conversion_op(conversion);
+
+ // adjust the adapter code to the internal EntryKind enumeration:
+ EntryKind ek_orig = adapter_entry_kind(conv_op);
+ EntryKind ek_opt = ek_orig; // may be optimized
+
+ // Finalize the vmtarget field (Java initialized it to null).
+ if (!java_dyn_MethodHandle::is_instance(target())) {
+ throw_InternalError_for_bad_conversion(conversion, "bad target", THREAD);
+ return;
+ }
+ sun_dyn_AdapterMethodHandle::set_vmtarget(mh(), target());
+
+ if (VerifyMethodHandles) {
+ verify_AdapterMethodHandle(mh, argnum, CHECK);
+ }
+
+ int stack_move = adapter_conversion_stack_move(conversion);
+ BasicType src = adapter_conversion_src_type(conversion);
+ BasicType dest = adapter_conversion_dest_type(conversion);
+ int vminfo = adapter_conversion_vminfo(conversion); // should be zero
+
+ const char* err = NULL;
+
+ // Now it's time to finish the case analysis and pick a MethodHandleEntry.
+ switch (ek_orig) {
+ case _adapter_retype_only:
+ case _adapter_check_cast:
+ case _adapter_dup_args:
+ case _adapter_drop_args:
+ // these work fine via general case code
+ break;
+
+ case _adapter_prim_to_prim:
+ {
+ // Non-subword cases are {int,float,long,double} -> {int,float,long,double}.
+ // And, the {float,double} -> {int,long} cases must be handled by Java.
+ switch (type2size[src] *4+ type2size[dest]) {
+ case 1 *4+ 1:
+ assert(src == T_INT || is_subword_type(src), "source is not float");
+ // Subword-related cases are int -> {boolean,byte,char,short}.
+ ek_opt = _adapter_opt_i2i;
+ vminfo = adapter_subword_vminfo(dest);
+ break;
+ case 2 *4+ 1:
+ if (src == T_LONG && (dest == T_INT || is_subword_type(dest))) {
+ ek_opt = _adapter_opt_l2i;
+ vminfo = adapter_subword_vminfo(dest);
+ } else if (src == T_DOUBLE && dest == T_FLOAT) {
+ ek_opt = _adapter_opt_d2f;
+ } else {
+ assert(false, "");
+ }
+ break;
+ case 1 *4+ 2:
+ if (src == T_INT && dest == T_LONG) {
+ ek_opt = _adapter_opt_i2l;
+ } else if (src == T_FLOAT && dest == T_DOUBLE) {
+ ek_opt = _adapter_opt_f2d;
+ } else {
+ assert(false, "");
+ }
+ break;
+ default:
+ assert(false, "");
+ break;
+ }
+ }
+ break;
+
+ case _adapter_ref_to_prim:
+ {
+ switch (type2size[dest]) {
+ case 1:
+ ek_opt = _adapter_opt_unboxi;
+ vminfo = adapter_subword_vminfo(dest);
+ break;
+ case 2:
+ ek_opt = _adapter_opt_unboxl;
+ break;
+ default:
+ assert(false, "");
+ break;
+ }
+ }
+ break;
+
+ case _adapter_prim_to_ref:
+ goto throw_not_impl; // allocates, hence could block
+
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ {
+ int swap_slots = type2size[src];
+ oop mtype = sun_dyn_AdapterMethodHandle::type(mh());
+ int slot_limit = sun_dyn_AdapterMethodHandle::vmslots(mtype);
+ int src_slot = argslot;
+ int dest_slot = vminfo;
+ int rotate = (ek_orig == _adapter_swap_args) ? 0 : (src_slot > dest_slot) ? 1 : -1;
+ switch (swap_slots) {
+ case 1:
+ ek_opt = (!rotate ? _adapter_opt_swap_1 :
+ rotate > 0 ? _adapter_opt_rot_1_up : _adapter_opt_rot_1_down);
+ break;
+ case 2:
+ ek_opt = (!rotate ? _adapter_opt_swap_2 :
+ rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down);
+ break;
+ default:
+ assert(false, "");
+ break;
+ }
+ }
+ break;
+
+ case _adapter_collect_args:
+ goto throw_not_impl; // allocates, hence could block
+
+ case _adapter_spread_args:
+ {
+ // vminfo will be the required length of the array
+ int slots_pushed = stack_move / stack_move_unit();
+ int array_size = slots_pushed + 1;
+ assert(array_size >= 0, "");
+ vminfo = array_size;
+ switch (array_size) {
+ case 0: ek_opt = _adapter_opt_spread_0; break;
+ case 1: ek_opt = _adapter_opt_spread_1; break;
+ default: ek_opt = _adapter_opt_spread_more; break;
+ }
+ if ((vminfo & CONV_VMINFO_MASK) != vminfo)
+ goto throw_not_impl; // overflow
+ }
+ break;
+
+ case _adapter_flyby:
+ case _adapter_ricochet:
+ goto throw_not_impl; // runs Java code, hence could block
+
+ default:
+ // should have failed much earlier; must be a missing case here
+ assert(false, "incomplete switch");
+ // and fall through:
+
+ throw_not_impl:
+ // FIXME: these adapters are NYI
+ err = "adapter not yet implemented in the JVM";
+ break;
+ }
+
+ if (err != NULL) {
+ throw_InternalError_for_bad_conversion(conversion, err, THREAD);
+ return;
+ }
+
+ // Rebuild the conversion value; maybe parts of it were changed.
+ jint new_conversion = adapter_conversion(conv_op, src, dest, stack_move, vminfo);
+
+ // Finalize the conversion field. (Note that it is final to Java code.)
+ sun_dyn_AdapterMethodHandle::set_conversion(mh(), new_conversion);
+
+ // Done!
+ java_dyn_MethodHandle::set_vmentry(mh(), entry(ek_opt));
+
+ // There should be enough memory barriers on exit from native methods
+ // to ensure that the MH is fully initialized to all threads before
+ // Java code can publish it in global data structures.
+}
+
+//
+// Here are the native methods on sun.dyn.MethodHandleImpl.
+// They are the private interface between this JVM and the HotSpot-specific
+// Java code that implements JSR 292 method handles.
+//
+// Note: We use a JVM_ENTRY macro to define each of these, for this is the way
+// that intrinsic (non-JNI) native methods are defined in HotSpot.
+//
+
+// direct method handles for invokestatic or invokespecial
+// void init(DirectMethodHandle self, MemberName ref, boolean doDispatch, Class<?> caller);
+JVM_ENTRY(void, MHI_init_DMH(JNIEnv *env, jobject igcls, jobject mh_jh,
+ jobject target_jh, jboolean do_dispatch, jobject caller_jh)) {
+ ResourceMark rm; // for error messages
+
+ // This is the guy we are initializing:
+ if (mh_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
+
+ // Early returns out of this method leave the DMH in an unfinished state.
+ assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
+
+ // which method are we really talking about?
+ if (target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ oop target_oop = JNIHandles::resolve_non_null(target_jh);
+ if (sun_dyn_MemberName::is_instance(target_oop) &&
+ sun_dyn_MemberName::vmindex(target_oop) == VM_INDEX_UNINITIALIZED) {
+ Handle mname(THREAD, target_oop);
+ MethodHandles::resolve_MemberName(mname, CHECK);
+ target_oop = mname(); // in case of GC
+ }
+
+ int decode_flags = 0; klassOop receiver_limit = NULL;
+ methodHandle m(THREAD,
+ MethodHandles::decode_method(target_oop,
+ receiver_limit, decode_flags));
+ if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "no such method"); }
+
+ // The trusted Java code that calls this method should already have performed
+ // access checks on behalf of the given caller. But, we can verify this.
+ if (VerifyMethodHandles && caller_jh != NULL) {
+ KlassHandle caller(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(caller_jh)));
+ // If this were a bytecode, the first access check would be against
+ // the "reference class" mentioned in the CONSTANT_Methodref.
+ // For that class, we use the defining class of m,
+ // or a more specific receiver limit if available.
+ klassOop reference_klass = m->method_holder(); // OK approximation
+ if (receiver_limit != NULL && receiver_limit != reference_klass) {
+ if (!Klass::cast(receiver_limit)->is_subtype_of(reference_klass))
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "receiver limit out of bounds"); // Java code bug
+ reference_klass = receiver_limit;
+ }
+ // Emulate LinkResolver::check_klass_accessability.
+ if (!Reflection::verify_class_access(caller->as_klassOop(),
+ reference_klass,
+ true)) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), Klass::cast(m->method_holder())->external_name());
+ }
+ // If there were a bytecode, the next step would be to lookup the method
+ // in the reference class, then then check the method's access bits.
+ // Emulate LinkResolver::check_method_accessability.
+ klassOop resolved_klass = m->method_holder();
+ if (!Reflection::verify_field_access(caller->as_klassOop(),
+ resolved_klass, reference_klass,
+ m->access_flags(),
+ true)) {
+ // %%% following cutout belongs in Reflection::verify_field_access?
+ bool same_pm = Reflection::is_same_package_member(caller->as_klassOop(),
+ reference_klass, THREAD);
+ if (!same_pm) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), m->name_and_sig_as_C_string());
+ }
+ }
+ }
+
+ MethodHandles::init_DirectMethodHandle(mh, m, (do_dispatch != JNI_FALSE), CHECK);
+}
+JVM_END
+
+// bound method handles
+JVM_ENTRY(void, MHI_init_BMH(JNIEnv *env, jobject igcls, jobject mh_jh,
+ jobject target_jh, int argnum)) {
+ ResourceMark rm; // for error messages
+
+ // This is the guy we are initializing:
+ if (mh_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
+
+ // Early returns out of this method leave the BMH in an unfinished state.
+ assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
+
+ if (target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
+
+ if (!java_dyn_MethodHandle::is_instance(target())) {
+ // Target object is a reflective method. (%%% Do we need this alternate path?)
+ Untested("init_BMH of non-MH");
+ if (argnum != 0) { THROW(vmSymbols::java_lang_InternalError()); }
+ int decode_flags = 0; klassOop receiver_limit_oop = NULL;
+ methodHandle m(THREAD,
+ MethodHandles::decode_method(target(),
+ receiver_limit_oop,
+ decode_flags));
+ KlassHandle receiver_limit(THREAD, receiver_limit_oop);
+ MethodHandles::init_BoundMethodHandle_with_receiver(mh, m,
+ receiver_limit,
+ decode_flags,
+ CHECK);
+ return;
+ }
+
+ // Build a BMH on top of a DMH or another BMH:
+ MethodHandles::init_BoundMethodHandle(mh, target, argnum, CHECK);
+}
+JVM_END
+
+// adapter method handles
+JVM_ENTRY(void, MHI_init_AMH(JNIEnv *env, jobject igcls, jobject mh_jh,
+ jobject target_jh, int argnum)) {
+ // This is the guy we are initializing:
+ if (mh_jh == NULL || target_jh == NULL) {
+ THROW(vmSymbols::java_lang_InternalError());
+ }
+ Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh));
+ Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
+
+ // Early returns out of this method leave the AMH in an unfinished state.
+ assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null");
+
+ MethodHandles::init_AdapterMethodHandle(mh, target, argnum, CHECK);
+}
+JVM_END
+
+// method type forms
+JVM_ENTRY(void, MHI_init_MT(JNIEnv *env, jobject igcls, jobject erased_jh)) {
+ if (erased_jh == NULL) return;
+ if (TraceMethodHandles) {
+ tty->print("creating MethodType form ");
+ if (WizardMode || Verbose) { // Warning: this calls Java code on the MH!
+ // call Object.toString()
+ symbolOop name = vmSymbols::toString_name(), sig = vmSymbols::void_string_signature();
+ JavaCallArguments args(Handle(THREAD, JNIHandles::resolve_non_null(erased_jh)));
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_virtual(&result, SystemDictionary::object_klass(), name, sig,
+ &args, CHECK);
+ Handle str(THREAD, (oop)result.get_jobject());
+ java_lang_String::print(str, tty);
+ }
+ tty->cr();
+ }
+}
+JVM_END
+
+// debugging and reflection
+JVM_ENTRY(jobject, MHI_getTarget(JNIEnv *env, jobject igcls, jobject mh_jh, jint format)) {
+ Handle mh(THREAD, JNIHandles::resolve(mh_jh));
+ if (!java_dyn_MethodHandle::is_instance(mh())) {
+ THROW_NULL(vmSymbols::java_lang_IllegalArgumentException());
+ }
+ oop target = MethodHandles::encode_target(mh, format, CHECK_NULL);
+ return JNIHandles::make_local(THREAD, target);
+}
+JVM_END
+
+JVM_ENTRY(jint, MHI_getConstant(JNIEnv *env, jobject igcls, jint which)) {
+ switch (which) {
+ case MethodHandles::GC_JVM_PUSH_LIMIT:
+ guarantee(MethodHandlePushLimit >= 2 && MethodHandlePushLimit <= 0xFF,
+ "MethodHandlePushLimit parameter must be in valid range");
+ return MethodHandlePushLimit;
+ case MethodHandles::GC_JVM_STACK_MOVE_LIMIT:
+ // return number of words per slot, signed according to stack direction
+ return MethodHandles::stack_move_unit();
+ }
+ return 0;
+}
+JVM_END
+
+#ifndef PRODUCT
+#define EACH_NAMED_CON(template) \
+ template(MethodHandles,GC_JVM_PUSH_LIMIT) \
+ template(MethodHandles,GC_JVM_STACK_MOVE_LIMIT) \
+ template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \
+ template(MethodHandles,ETF_DIRECT_HANDLE) \
+ template(MethodHandles,ETF_METHOD_NAME) \
+ template(MethodHandles,ETF_REFLECT_METHOD) \
+ template(sun_dyn_MemberName,MN_IS_METHOD) \
+ template(sun_dyn_MemberName,MN_IS_CONSTRUCTOR) \
+ template(sun_dyn_MemberName,MN_IS_FIELD) \
+ template(sun_dyn_MemberName,MN_IS_TYPE) \
+ template(sun_dyn_MemberName,MN_SEARCH_SUPERCLASSES) \
+ template(sun_dyn_MemberName,MN_SEARCH_INTERFACES) \
+ template(sun_dyn_MemberName,VM_INDEX_UNINITIALIZED) \
+ template(sun_dyn_AdapterMethodHandle,OP_RETYPE_ONLY) \
+ template(sun_dyn_AdapterMethodHandle,OP_CHECK_CAST) \
+ template(sun_dyn_AdapterMethodHandle,OP_PRIM_TO_PRIM) \
+ template(sun_dyn_AdapterMethodHandle,OP_REF_TO_PRIM) \
+ template(sun_dyn_AdapterMethodHandle,OP_PRIM_TO_REF) \
+ template(sun_dyn_AdapterMethodHandle,OP_SWAP_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_ROT_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_DUP_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_DROP_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_COLLECT_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_SPREAD_ARGS) \
+ template(sun_dyn_AdapterMethodHandle,OP_FLYBY) \
+ template(sun_dyn_AdapterMethodHandle,OP_RICOCHET) \
+ template(sun_dyn_AdapterMethodHandle,CONV_OP_LIMIT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_OP_MASK) \
+ template(sun_dyn_AdapterMethodHandle,CONV_VMINFO_MASK) \
+ template(sun_dyn_AdapterMethodHandle,CONV_VMINFO_SHIFT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_OP_SHIFT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_DEST_TYPE_SHIFT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_SRC_TYPE_SHIFT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_STACK_MOVE_SHIFT) \
+ template(sun_dyn_AdapterMethodHandle,CONV_STACK_MOVE_MASK) \
+ /*end*/
+
+#define ONE_PLUS(scope,value) 1+
+static const int con_value_count = EACH_NAMED_CON(ONE_PLUS) 0;
+#define VALUE_COMMA(scope,value) scope::value,
+static const int con_values[con_value_count+1] = { EACH_NAMED_CON(VALUE_COMMA) 0 };
+#define STRING_NULL(scope,value) #value "\0"
+static const char con_names[] = { EACH_NAMED_CON(STRING_NULL) };
+
+#undef ONE_PLUS
+#undef VALUE_COMMA
+#undef STRING_NULL
+#undef EACH_NAMED_CON
+#endif
+
+JVM_ENTRY(jint, MHI_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) {
+#ifndef PRODUCT
+ if (which >= 0 && which < con_value_count) {
+ int con = con_values[which];
+ objArrayOop box = (objArrayOop) JNIHandles::resolve(box_jh);
+ if (box != NULL && box->klass() == Universe::objectArrayKlassObj() && box->length() > 0) {
+ const char* str = &con_names[0];
+ for (int i = 0; i < which; i++)
+ str += strlen(str) + 1; // skip name and null
+ oop name = java_lang_String::create_oop_from_str(str, CHECK_0);
+ box->obj_at_put(0, name);
+ }
+ return con;
+ }
+#endif
+ return 0;
+}
+JVM_END
+
+// void init(MemberName self, AccessibleObject ref)
+JVM_ENTRY(void, MHI_init_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jobject target_jh)) {
+ if (mname_jh == NULL || target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
+ oop target_oop = JNIHandles::resolve_non_null(target_jh);
+ MethodHandles::init_MemberName(mname(), target_oop);
+}
+JVM_END
+
+// void expand(MemberName self)
+JVM_ENTRY(void, MHI_expand_Mem(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+ if (mname_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
+ MethodHandles::expand_MemberName(mname, 0, CHECK);
+}
+JVM_END
+
+// void resolve(MemberName self, Class<?> caller)
+JVM_ENTRY(void, MHI_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh)) {
+ if (mname_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); }
+ Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
+ // %%% take caller into account!
+ MethodHandles::resolve_MemberName(mname, CHECK);
+}
+JVM_END
+
+// static native int getMembers(Class<?> defc, String matchName, String matchSig,
+// int matchFlags, Class<?> caller, int skip, MemberName[] results);
+JVM_ENTRY(jint, MHI_getMembers(JNIEnv *env, jobject igcls,
+ jclass clazz_jh, jstring name_jh, jstring sig_jh,
+ int mflags, jclass caller_jh, jint skip, jobjectArray results_jh)) {
+ if (clazz_jh == NULL || results_jh == NULL) return -1;
+ klassOop k_oop = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz_jh));
+
+ objArrayOop results = (objArrayOop) JNIHandles::resolve(results_jh);
+ if (results == NULL || !results->is_objArray()) return -1;
+
+ symbolOop name = NULL, sig = NULL;
+ if (name_jh != NULL) {
+ name = java_lang_String::as_symbol_or_null(JNIHandles::resolve_non_null(name_jh));
+ if (name == NULL) return 0; // a match is not possible
+ }
+ if (sig_jh != NULL) {
+ sig = java_lang_String::as_symbol_or_null(JNIHandles::resolve_non_null(sig_jh));
+ if (sig == NULL) return 0; // a match is not possible
+ }
+
+ klassOop caller = NULL;
+ if (caller_jh != NULL) {
+ oop caller_oop = JNIHandles::resolve_non_null(caller_jh);
+ if (!java_lang_Class::is_instance(caller_oop)) return -1;
+ caller = java_lang_Class::as_klassOop(caller_oop);
+ }
+
+ if (name != NULL && sig != NULL && results != NULL) {
+ // try a direct resolve
+ // %%% TO DO
+ }
+
+ int res = MethodHandles::find_MemberNames(k_oop, name, sig, mflags,
+ caller, skip, results);
+ // TO DO: expand at least some of the MemberNames, to avoid massive callbacks
+ return res;
+}
+JVM_END
+
+
+/// JVM_RegisterMethodHandleMethods
+
+#define ADR "J"
+
+#define LANG "Ljava/lang/"
+#define JDYN "Ljava/dyn/"
+#define IDYN "Lsun/dyn/"
+
+#define OBJ LANG"Object;"
+#define CLS LANG"Class;"
+#define STRG LANG"String;"
+#define MT JDYN"MethodType;"
+#define MH JDYN"MethodHandle;"
+#define MHI IDYN"MethodHandleImpl;"
+#define MEM IDYN"MemberName;"
+#define AMH IDYN"AdapterMethodHandle;"
+#define BMH IDYN"BoundMethodHandle;"
+#define DMH IDYN"DirectMethodHandle;"
+
+#define CC (char*) /*cast a literal from (const char*)*/
+#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
+
+// These are the native methods on sun.dyn.MethodHandleNatives.
+static JNINativeMethod methods[] = {
+ // void init(MemberName self, AccessibleObject ref)
+ {CC"init", CC"("AMH""MH"I)V", FN_PTR(MHI_init_AMH)},
+ {CC"init", CC"("BMH""OBJ"I)V", FN_PTR(MHI_init_BMH)},
+ {CC"init", CC"("DMH""OBJ"Z"CLS")V", FN_PTR(MHI_init_DMH)},
+ {CC"init", CC"("MT")V", FN_PTR(MHI_init_MT)},
+ {CC"init", CC"("MEM""OBJ")V", FN_PTR(MHI_init_Mem)},
+ {CC"expand", CC"("MEM")V", FN_PTR(MHI_expand_Mem)},
+ {CC"resolve", CC"("MEM""CLS")V", FN_PTR(MHI_resolve_Mem)},
+ {CC"getTarget", CC"("MH"I)"OBJ, FN_PTR(MHI_getTarget)},
+ {CC"getConstant", CC"(I)I", FN_PTR(MHI_getConstant)},
+ // static native int getNamedCon(int which, Object[] name)
+ {CC"getNamedCon", CC"(I["OBJ")I", FN_PTR(MHI_getNamedCon)},
+ // static native int getMembers(Class<?> defc, String matchName, String matchSig,
+ // int matchFlags, Class<?> caller, int skip, MemberName[] results);
+ {CC"getMembers", CC"("CLS""STRG""STRG"I"CLS"I["MEM")I", FN_PTR(MHI_getMembers)}
+};
+
+
+// This one function is exported, used by NativeLookup.
+
+JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
+ assert(MethodHandles::spot_check_entry_names(), "entry enum is OK");
+
+ if (!EnableMethodHandles) {
+ warning("JSR 292 method handles are disabled in this JVM. Use -XX:+EnableMethodHandles to enable.");
+ return; // bind nothing
+ }
+
+ {
+ ThreadToNativeFromVM ttnfv(thread);
+
+ int status = env->RegisterNatives(MHN_class, methods, sizeof(methods)/sizeof(JNINativeMethod));
+ if (env->ExceptionOccurred()) {
+ MethodHandles::set_enabled(false);
+ warning("JSR 292 method handle code is mismatched to this JVM. Disabling support.");
+ env->ExceptionClear();
+ } else {
+ MethodHandles::set_enabled(true);
+ }
+ }
+}
+JVM_END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+class MacroAssembler;
+class Label;
+class MethodHandleEntry;
+
+class MethodHandles: AllStatic {
+ // JVM support for MethodHandle, MethodType, and related types
+ // in java.dyn and java.dyn.hotspot.
+ // See also javaClasses for layouts java_dyn_Method{Handle,Type,Type::Form}.
+ public:
+ enum EntryKind {
+ _check_mtype, // how a caller calls a MH
+ _wrong_method_type, // what happens when there is a type mismatch
+ _invokestatic_mh, // how a MH emulates invokestatic
+ _invokespecial_mh, // ditto for the other invokes...
+ _invokevirtual_mh,
+ _invokeinterface_mh,
+ _bound_ref_mh, // reference argument is bound
+ _bound_int_mh, // int argument is bound (via an Integer or Float)
+ _bound_long_mh, // long argument is bound (via a Long or Double)
+ _bound_ref_direct_mh, // same as above, with direct linkage to methodOop
+ _bound_int_direct_mh,
+ _bound_long_direct_mh,
+
+ _adapter_mh_first, // adapter sequence goes here...
+ _adapter_retype_only = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_RETYPE_ONLY,
+ _adapter_check_cast = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_CHECK_CAST,
+ _adapter_prim_to_prim = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_PRIM_TO_PRIM,
+ _adapter_ref_to_prim = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_REF_TO_PRIM,
+ _adapter_prim_to_ref = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_PRIM_TO_REF,
+ _adapter_swap_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_SWAP_ARGS,
+ _adapter_rot_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_ROT_ARGS,
+ _adapter_dup_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_DUP_ARGS,
+ _adapter_drop_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_DROP_ARGS,
+ _adapter_collect_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_COLLECT_ARGS,
+ _adapter_spread_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_SPREAD_ARGS,
+ _adapter_flyby = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_FLYBY,
+ _adapter_ricochet = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_RICOCHET,
+ _adapter_mh_last = _adapter_mh_first + sun_dyn_AdapterMethodHandle::CONV_OP_LIMIT - 1,
+
+ // Optimized adapter types
+
+ // argument list reordering
+ _adapter_opt_swap_1,
+ _adapter_opt_swap_2,
+ _adapter_opt_rot_1_up,
+ _adapter_opt_rot_1_down,
+ _adapter_opt_rot_2_up,
+ _adapter_opt_rot_2_down,
+ // primitive single to single:
+ _adapter_opt_i2i, // i2c, i2z, i2b, i2s
+ // primitive double to single:
+ _adapter_opt_l2i,
+ _adapter_opt_d2f,
+ // primitive single to double:
+ _adapter_opt_i2l,
+ _adapter_opt_f2d,
+ // conversion between floating point and integer type is handled by Java
+
+ // reference to primitive:
+ _adapter_opt_unboxi,
+ _adapter_opt_unboxl,
+
+ // spreading (array length cases 0, 1, >=2)
+ _adapter_opt_spread_0,
+ _adapter_opt_spread_1,
+ _adapter_opt_spread_more,
+
+ _EK_LIMIT,
+ _EK_FIRST = 0
+ };
+
+ public:
+ static bool enabled() { return _enabled; }
+ static void set_enabled(bool z);
+
+ private:
+ enum { // import sun_dyn_AdapterMethodHandle::CONV_OP_*
+ CONV_OP_LIMIT = sun_dyn_AdapterMethodHandle::CONV_OP_LIMIT,
+ CONV_OP_MASK = sun_dyn_AdapterMethodHandle::CONV_OP_MASK,
+ CONV_VMINFO_MASK = sun_dyn_AdapterMethodHandle::CONV_VMINFO_MASK,
+ CONV_VMINFO_SHIFT = sun_dyn_AdapterMethodHandle::CONV_VMINFO_SHIFT,
+ CONV_OP_SHIFT = sun_dyn_AdapterMethodHandle::CONV_OP_SHIFT,
+ CONV_DEST_TYPE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_DEST_TYPE_SHIFT,
+ CONV_SRC_TYPE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_SRC_TYPE_SHIFT,
+ CONV_STACK_MOVE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_STACK_MOVE_SHIFT,
+ CONV_STACK_MOVE_MASK = sun_dyn_AdapterMethodHandle::CONV_STACK_MOVE_MASK
+ };
+
+ static bool _enabled;
+ static MethodHandleEntry* _entries[_EK_LIMIT];
+ static const char* _entry_names[_EK_LIMIT+1];
+ static bool ek_valid(EntryKind ek) { return (uint)ek < (uint)_EK_LIMIT; }
+ static bool conv_op_valid(int op) { return (uint)op < (uint)CONV_OP_LIMIT; }
+
+ public:
+ static bool have_entry(EntryKind ek) { return ek_valid(ek) && _entries[ek] != NULL; }
+ static MethodHandleEntry* entry(EntryKind ek) { assert(ek_valid(ek), "initialized");
+ return _entries[ek]; }
+ static const char* entry_name(EntryKind ek) { assert(ek_valid(ek), "oob");
+ return _entry_names[ek]; }
+ static EntryKind adapter_entry_kind(int op) { assert(conv_op_valid(op), "oob");
+ return EntryKind(_adapter_mh_first + op); }
+
+ static void init_entry(EntryKind ek, MethodHandleEntry* me) {
+ assert(ek_valid(ek), "oob");
+ assert(_entries[ek] == NULL, "no double initialization");
+ _entries[ek] = me;
+ }
+
+ static jint adapter_conversion(int conv_op, BasicType src, BasicType dest,
+ int stack_move = 0, int vminfo = 0) {
+ assert(conv_op_valid(conv_op), "oob");
+ jint conv = ((conv_op << CONV_OP_SHIFT)
+ | (src << CONV_SRC_TYPE_SHIFT)
+ | (dest << CONV_DEST_TYPE_SHIFT)
+ | (stack_move << CONV_STACK_MOVE_SHIFT)
+ | (vminfo << CONV_VMINFO_SHIFT)
+ );
+ assert(adapter_conversion_op(conv) == conv_op, "decode conv_op");
+ assert(adapter_conversion_src_type(conv) == src, "decode src");
+ assert(adapter_conversion_dest_type(conv) == dest, "decode dest");
+ assert(adapter_conversion_stack_move(conv) == stack_move, "decode stack_move");
+ assert(adapter_conversion_vminfo(conv) == vminfo, "decode vminfo");
+ return conv;
+ }
+ static int adapter_conversion_op(jint conv) {
+ return ((conv >> CONV_OP_SHIFT) & 0xF);
+ }
+ static BasicType adapter_conversion_src_type(jint conv) {
+ return (BasicType)((conv >> CONV_SRC_TYPE_SHIFT) & 0xF);
+ }
+ static BasicType adapter_conversion_dest_type(jint conv) {
+ return (BasicType)((conv >> CONV_DEST_TYPE_SHIFT) & 0xF);
+ }
+ static int adapter_conversion_stack_move(jint conv) {
+ return (conv >> CONV_STACK_MOVE_SHIFT);
+ }
+ static int adapter_conversion_vminfo(jint conv) {
+ return (conv >> CONV_VMINFO_SHIFT) & CONV_VMINFO_MASK;
+ }
+
+ // Offset in words that the interpreter stack pointer moves when an argument is pushed.
+ // The stack_move value must always be a multiple of this.
+ static int stack_move_unit() {
+ return frame::interpreter_frame_expression_stack_direction() * Interpreter::stackElementWords();
+ }
+
+ enum { CONV_VMINFO_SIGN_FLAG = 0x80 };
+ static int adapter_subword_vminfo(BasicType dest) {
+ if (dest == T_BOOLEAN) return (BitsPerInt - 1);
+ if (dest == T_CHAR) return (BitsPerInt - 16);
+ if (dest == T_BYTE) return (BitsPerInt - 8) | CONV_VMINFO_SIGN_FLAG;
+ if (dest == T_SHORT) return (BitsPerInt - 16) | CONV_VMINFO_SIGN_FLAG;
+ return 0; // case T_INT
+ }
+ // Here is the transformation the i2i adapter must perform:
+ static int truncate_subword_from_vminfo(jint value, int vminfo) {
+ jint tem = value << vminfo;
+ if ((vminfo & CONV_VMINFO_SIGN_FLAG) != 0) {
+ return (jint)tem >> vminfo;
+ } else {
+ return (juint)tem >> vminfo;
+ }
+ }
+
+ static inline address from_compiled_entry(EntryKind ek);
+ static inline address from_interpreted_entry(EntryKind ek);
+
+ // helpers for decode_method.
+ static methodOop decode_methodOop(methodOop m, int& decode_flags_result);
+ static methodOop decode_vmtarget(oop vmtarget, int vmindex, oop mtype, klassOop& receiver_limit_result, int& decode_flags_result);
+ static methodOop decode_MemberName(oop mname, klassOop& receiver_limit_result, int& decode_flags_result);
+ static methodOop decode_MethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result);
+ static methodOop decode_DirectMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result);
+ static methodOop decode_BoundMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result);
+ static methodOop decode_AdapterMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result);
+
+ // Find out how many stack slots an mh pushes or pops.
+ // The result is *not* reported as a multiple of stack_move_unit();
+ // It is a signed net number of pushes (a difference in vmslots).
+ // To compare with a stack_move value, first multiply by stack_move_unit().
+ static int decode_MethodHandle_stack_pushes(oop mh);
+
+ public:
+ // working with member names
+ static void resolve_MemberName(Handle mname, TRAPS); // compute vmtarget/vmindex from name/type
+ static void expand_MemberName(Handle mname, int suppress, TRAPS); // expand defc/name/type if missing
+ static void init_MemberName(oop mname_oop, oop target); // compute vmtarget/vmindex from target
+ static void init_MemberName(oop mname_oop, methodOop m, bool do_dispatch);
+ static void init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset);
+ static int find_MemberNames(klassOop k, symbolOop name, symbolOop sig,
+ int mflags, klassOop caller,
+ int skip, objArrayOop results);
+ // bit values for suppress argument to expand_MemberName:
+ enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
+
+ // called from InterpreterGenerator and StubGenerator
+ static address generate_method_handle_interpreter_entry(MacroAssembler* _masm);
+ static void generate_method_handle_stub(MacroAssembler* _masm, EntryKind ek);
+
+ // argument list parsing
+ static int argument_slot(oop method_type, int arg);
+ static int argument_slot_count(oop method_type) { return argument_slot(method_type, -1); }
+ static int argument_slot_to_argnum(oop method_type, int argslot);
+
+ // Runtime support
+ enum { // bit-encoded flags from decode_method or decode_vmref
+ _dmf_has_receiver = 0x01, // target method has leading reference argument
+ _dmf_does_dispatch = 0x02, // method handle performs virtual or interface dispatch
+ _dmf_from_interface = 0x04, // peforms interface dispatch
+ _DMF_DIRECT_MASK = (_dmf_from_interface*2 - _dmf_has_receiver),
+ _dmf_binds_method = 0x08,
+ _dmf_binds_argument = 0x10,
+ _DMF_BOUND_MASK = (_dmf_binds_argument*2 - _dmf_binds_method),
+ _dmf_adapter_lsb = 0x20,
+ _DMF_ADAPTER_MASK = (_dmf_adapter_lsb << CONV_OP_LIMIT) - _dmf_adapter_lsb
+ };
+ static methodOop decode_method(oop x, klassOop& receiver_limit_result, int& decode_flags_result);
+ enum {
+ // format of query to getConstant:
+ GC_JVM_PUSH_LIMIT = 0,
+ GC_JVM_STACK_MOVE_LIMIT = 1,
+
+ // format of result from getTarget / encode_target:
+ ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
+ ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
+ ETF_METHOD_NAME = 2, // ultimate method as MemberName
+ ETF_REFLECT_METHOD = 3 // ultimate method as java.lang.reflect object (sans refClass)
+ };
+ static int get_named_constant(int which, Handle name_box, TRAPS);
+ static oop encode_target(Handle mh, int format, TRAPS); // report vmtarget (to Java code)
+ static bool class_cast_needed(klassOop src, klassOop dst);
+
+ private:
+ // These checkers operate on a pair of whole MethodTypes:
+ static const char* check_method_type_change(oop src_mtype, int src_beg, int src_end,
+ int insert_argnum, oop insert_type,
+ int change_argnum, oop change_type,
+ int delete_argnum,
+ oop dst_mtype, int dst_beg, int dst_end);
+ static const char* check_method_type_insertion(oop src_mtype,
+ int insert_argnum, oop insert_type,
+ oop dst_mtype) {
+ oop no_ref = NULL;
+ return check_method_type_change(src_mtype, 0, -1,
+ insert_argnum, insert_type,
+ -1, no_ref, -1, dst_mtype, 0, -1);
+ }
+ static const char* check_method_type_conversion(oop src_mtype,
+ int change_argnum, oop change_type,
+ oop dst_mtype) {
+ oop no_ref = NULL;
+ return check_method_type_change(src_mtype, 0, -1, -1, no_ref,
+ change_argnum, change_type,
+ -1, dst_mtype, 0, -1);
+ }
+ static const char* check_method_type_passthrough(oop src_mtype, oop dst_mtype) {
+ oop no_ref = NULL;
+ return check_method_type_change(src_mtype, 0, -1,
+ -1, no_ref, -1, no_ref, -1,
+ dst_mtype, 0, -1);
+ }
+
+ // These checkers operate on pairs of argument or return types:
+ static const char* check_argument_type_change(BasicType src_type, klassOop src_klass,
+ BasicType dst_type, klassOop dst_klass,
+ int argnum);
+
+ static const char* check_argument_type_change(oop src_type, oop dst_type,
+ int argnum) {
+ klassOop src_klass = NULL, dst_klass = NULL;
+ BasicType src_bt = java_lang_Class::as_BasicType(src_type, &src_klass);
+ BasicType dst_bt = java_lang_Class::as_BasicType(dst_type, &dst_klass);
+ return check_argument_type_change(src_bt, src_klass,
+ dst_bt, dst_klass, argnum);
+ }
+
+ static const char* check_return_type_change(oop src_type, oop dst_type) {
+ return check_argument_type_change(src_type, dst_type, -1);
+ }
+
+ static const char* check_return_type_change(BasicType src_type, klassOop src_klass,
+ BasicType dst_type, klassOop dst_klass) {
+ return check_argument_type_change(src_type, src_klass, dst_type, dst_klass, -1);
+ }
+
+ static const char* check_method_receiver(methodOop m, klassOop passed_recv_type);
+
+ // These verifiers can block, and will throw an error if the checking fails:
+ static void verify_vmslots(Handle mh, TRAPS);
+ static void verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS);
+
+ static void verify_method_type(methodHandle m, Handle mtype,
+ bool has_bound_oop,
+ KlassHandle bound_oop_type,
+ TRAPS);
+
+ static void verify_method_signature(methodHandle m, Handle mtype,
+ int first_ptype_pos,
+ KlassHandle insert_ptype, TRAPS);
+
+ static void verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS);
+ static void verify_BoundMethodHandle(Handle mh, Handle target, int argnum,
+ bool direct_to_method, TRAPS);
+ static void verify_BoundMethodHandle_with_receiver(Handle mh, methodHandle m, TRAPS);
+ static void verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS);
+
+ public:
+
+ // Fill in the fields of a DirectMethodHandle mh. (MH.type must be pre-filled.)
+ static void init_DirectMethodHandle(Handle mh, methodHandle method, bool do_dispatch, TRAPS);
+
+ // Fill in the fields of a BoundMethodHandle mh. (MH.type, BMH.argument must be pre-filled.)
+ static void init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
+ static void init_BoundMethodHandle_with_receiver(Handle mh,
+ methodHandle original_m,
+ KlassHandle receiver_limit,
+ int decode_flags,
+ TRAPS);
+
+ // Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.)
+ static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
+
+#ifdef ASSERT
+ static bool spot_check_entry_names();
+#endif
+
+ private:
+ static methodHandle dispatch_decoded_method(methodHandle m,
+ KlassHandle receiver_limit,
+ int decode_flags,
+ KlassHandle receiver_klass,
+ TRAPS);
+
+ static bool same_basic_type_for_arguments(BasicType src, BasicType dst,
+ bool for_return = false);
+ static bool same_basic_type_for_returns(BasicType src, BasicType dst) {
+ return same_basic_type_for_arguments(src, dst, true);
+ }
+
+ enum { // arg_mask values
+ _INSERT_NO_MASK = -1,
+ _INSERT_REF_MASK = 0,
+ _INSERT_INT_MASK = 1,
+ _INSERT_LONG_MASK = 3
+ };
+ static void insert_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ int arg_mask,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp);
+
+ static void remove_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp);
+};
+
+
+// Access methods for the "entry" field of a java.dyn.MethodHandle.
+// The field is primarily a jump target for compiled calls.
+// However, we squirrel away some nice pointers for other uses,
+// just before the jump target.
+// Aspects of a method handle entry:
+// - from_compiled_entry - stub used when compiled code calls the MH
+// - from_interpreted_entry - stub used when the interpreter calls the MH
+// - type_checking_entry - stub for runtime casting between MHForm siblings (NYI)
+class MethodHandleEntry {
+ public:
+ class Data {
+ friend class MethodHandleEntry;
+ size_t _total_size; // size including Data and code stub
+ MethodHandleEntry* _type_checking_entry;
+ address _from_interpreted_entry;
+ MethodHandleEntry* method_entry() { return (MethodHandleEntry*)(this + 1); }
+ };
+
+ Data* data() { return (Data*)this - 1; }
+
+ address start_address() { return (address) data(); }
+ address end_address() { return start_address() + data()->_total_size; }
+
+ address from_compiled_entry() { return (address) this; }
+
+ address from_interpreted_entry() { return data()->_from_interpreted_entry; }
+ void set_from_interpreted_entry(address e) { data()->_from_interpreted_entry = e; }
+
+ MethodHandleEntry* type_checking_entry() { return data()->_type_checking_entry; }
+ void set_type_checking_entry(MethodHandleEntry* e) { data()->_type_checking_entry = e; }
+
+ void set_end_address(address end_addr) {
+ size_t total_size = end_addr - start_address();
+ assert(total_size > 0 && total_size < 0x1000, "reasonable end address");
+ data()->_total_size = total_size;
+ }
+
+ // Compiler support:
+ static int from_interpreted_entry_offset_in_bytes() {
+ return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) );
+ }
+ static int type_checking_entry_offset_in_bytes() {
+ return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) );
+ }
+
+ static address start_compiled_entry(MacroAssembler* _masm,
+ address interpreted_entry = NULL);
+ static MethodHandleEntry* finish_compiled_entry(MacroAssembler* masm, address start_addr);
+};
+
+address MethodHandles::from_compiled_entry(EntryKind ek) { return entry(ek)->from_compiled_entry(); }
+address MethodHandles::from_interpreted_entry(EntryKind ek) { return entry(ek)->from_interpreted_entry(); }
--- a/hotspot/src/share/vm/prims/nativeLookup.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/prims/nativeLookup.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,6 +78,7 @@
extern "C" {
void JNICALL JVM_RegisterUnsafeMethods(JNIEnv *env, jclass unsafecls);
+ void JNICALL JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass unsafecls);
void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass);
}
@@ -97,6 +98,9 @@
if (strstr(jni_name, "Java_sun_misc_Unsafe_registerNatives") != NULL) {
return CAST_FROM_FN_PTR(address, JVM_RegisterUnsafeMethods);
}
+ if (strstr(jni_name, "Java_sun_dyn_MethodHandleNatives_registerNatives") != NULL) {
+ return CAST_FROM_FN_PTR(address, JVM_RegisterMethodHandleMethods);
+ }
if (strstr(jni_name, "Java_sun_misc_Perf_registerNatives") != NULL) {
return CAST_FROM_FN_PTR(address, JVM_RegisterPerfMethods);
}
--- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -2627,6 +2627,13 @@
}
#endif // PRODUCT
+ if (EnableMethodHandles && !AnonymousClasses) {
+ if (!FLAG_IS_DEFAULT(AnonymousClasses)) {
+ warning("forcing AnonymousClasses true to enable EnableMethodHandles");
+ }
+ AnonymousClasses = true;
+ }
+
if (PrintGCDetails) {
// Turn on -verbose:gc options as well
PrintGC = true;
--- a/hotspot/src/share/vm/runtime/globals.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -3301,6 +3301,21 @@
product(bool, AnonymousClasses, false, \
"support sun.misc.Unsafe.defineAnonymousClass") \
\
+ product(bool, EnableMethodHandles, false, \
+ "support method handles (true by default under JSR 292)") \
+ \
+ diagnostic(intx, MethodHandlePushLimit, 3, \
+ "number of additional stack slots a method handle may push") \
+ \
+ develop(bool, TraceMethodHandles, false, \
+ "trace internal method handle operations") \
+ \
+ diagnostic(bool, VerifyMethodHandles, trueInDebug, \
+ "perform extra checks when constructing method handles") \
+ \
+ diagnostic(bool, OptimizeMethodHandles, true, \
+ "when constructing method handles, try to improve them") \
+ \
product(bool, TaggedStackInterpreter, false, \
"Insert tags in interpreter execution stack for oopmap generaion")\
\
--- a/hotspot/src/share/vm/runtime/javaCalls.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/javaCalls.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -37,11 +37,6 @@
guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler");
_result = result;
- // Make sure that that the value of the higest_lock is at least the same as the current stackpointer,
- // since, the Java code is highly likely to use locks.
- // Use '(address)this' to guarantee that highest_lock address is conservative and inside our thread
- thread->update_highest_lock((address)this);
-
// Allocate handle block for Java code. This must be done before we change thread_state to _thread_in_Java_or_stub,
// since it can potentially block.
JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread);
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1471,9 +1471,73 @@
return generate_class_cast_message(objName, targetKlass->external_name());
}
+char* SharedRuntime::generate_wrong_method_type_message(JavaThread* thread,
+ oopDesc* required,
+ oopDesc* actual) {
+ assert(EnableMethodHandles, "");
+ oop singleKlass = wrong_method_type_is_for_single_argument(thread, required);
+ if (singleKlass != NULL) {
+ const char* objName = "argument or return value";
+ if (actual != NULL) {
+ // be flexible about the junk passed in:
+ klassOop ak = (actual->is_klass()
+ ? (klassOop)actual
+ : actual->klass());
+ objName = Klass::cast(ak)->external_name();
+ }
+ Klass* targetKlass = Klass::cast(required->is_klass()
+ ? (klassOop)required
+ : java_lang_Class::as_klassOop(required));
+ return generate_class_cast_message(objName, targetKlass->external_name());
+ } else {
+ // %%% need to get the MethodType string, without messing around too much
+ // Get a signature from the invoke instruction
+ const char* mhName = "method handle";
+ const char* targetType = "the required signature";
+ vframeStream vfst(thread, true);
+ if (!vfst.at_end()) {
+ Bytecode_invoke* call = Bytecode_invoke_at(vfst.method(), vfst.bci());
+ methodHandle target;
+ {
+ EXCEPTION_MARK;
+ target = call->static_target(THREAD);
+ if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; }
+ }
+ if (target.not_null()
+ && target->is_method_handle_invoke()
+ && required == target->method_handle_type()) {
+ targetType = target->signature()->as_C_string();
+ }
+ }
+ klassOop kignore; int fignore;
+ methodOop actual_method = MethodHandles::decode_method(actual,
+ kignore, fignore);
+ if (actual_method != NULL) {
+ if (actual_method->name() == vmSymbols::invoke_name())
+ mhName = "$";
+ else
+ mhName = actual_method->signature()->as_C_string();
+ if (mhName[0] == '$')
+ mhName = actual_method->signature()->as_C_string();
+ }
+ return generate_class_cast_message(mhName, targetType,
+ " cannot be called as ");
+ }
+}
+
+oop SharedRuntime::wrong_method_type_is_for_single_argument(JavaThread* thr,
+ oopDesc* required) {
+ if (required == NULL) return NULL;
+ if (required->klass() == SystemDictionary::class_klass())
+ return required;
+ if (required->is_klass())
+ return Klass::cast(klassOop(required))->java_mirror();
+ return NULL;
+}
+
+
char* SharedRuntime::generate_class_cast_message(
- const char* objName, const char* targetKlassName) {
- const char* desc = " cannot be cast to ";
+ const char* objName, const char* targetKlassName, const char* desc) {
size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1;
char* message = NEW_RESOURCE_ARRAY(char, msglen);
--- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -212,10 +212,32 @@
static char* generate_class_cast_message(JavaThread* thr, const char* name);
/**
+ * Fill in the message for a WrongMethodTypeException
+ *
+ * @param thr the current thread
+ * @param mtype (optional) expected method type (or argument class)
+ * @param mhandle (optional) actual method handle (or argument)
+ * @return the dynamically allocated exception message
+ *
+ * BCP for the frame on top of the stack must refer to an
+ * 'invokevirtual' op for a method handle, or an 'invokedyamic' op.
+ * The caller (or one of its callers) must use a ResourceMark
+ * in order to correctly free the result.
+ */
+ static char* generate_wrong_method_type_message(JavaThread* thr,
+ oopDesc* mtype = NULL,
+ oopDesc* mhandle = NULL);
+
+ /** Return non-null if the mtype is a klass or Class, not a MethodType. */
+ static oop wrong_method_type_is_for_single_argument(JavaThread* thr,
+ oopDesc* mtype);
+
+ /**
* Fill in the "X cannot be cast to a Y" message for ClassCastException
*
* @param name the name of the class of the object attempted to be cast
* @param klass the name of the target klass attempt
+ * @param gripe the specific kind of problem being reported
* @return the dynamically allocated exception message (must be freed
* by the caller using a resource mark)
*
@@ -224,7 +246,8 @@
* The caller (or one of it's callers) must use a ResourceMark
* in order to correctly free the result.
*/
- static char* generate_class_cast_message(const char* name, const char* klass);
+ static char* generate_class_cast_message(const char* name, const char* klass,
+ const char* gripe = " cannot be cast to ");
// Resolves a call site- may patch in the destination of the call into the
// compiled code.
--- a/hotspot/src/share/vm/runtime/synchronizer.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/synchronizer.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -1117,10 +1117,10 @@
// Optimization: if the mark->locker stack address is associated
// with this thread we could simply set m->_owner = Self and
- // m->OwnerIsThread = 1. Note that a thread can inflate an object
+ // m->OwnerIsThread = 1. Note that a thread can inflate an object
// that it has stack-locked -- as might happen in wait() -- directly
// with CAS. That is, we can avoid the xchg-NULL .... ST idiom.
- m->set_owner (mark->locker());
+ m->set_owner(mark->locker());
m->set_object(object);
// TODO-FIXME: assert BasicLock->dhw != 0.
@@ -1214,10 +1214,9 @@
BiasedLocking::revoke_at_safepoint(obj);
}
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
- }
-
- THREAD->update_highest_lock((address)lock);
- slow_enter (obj, lock, THREAD) ;
+ }
+
+ slow_enter (obj, lock, THREAD) ;
}
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
--- a/hotspot/src/share/vm/runtime/thread.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/thread.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -128,7 +128,6 @@
debug_only(_allow_allocation_count = 0;)
NOT_PRODUCT(_allow_safepoint_count = 0;)
CHECK_UNHANDLED_OOPS_ONLY(_gc_locked_out_count = 0;)
- _highest_lock = NULL;
_jvmti_env_iteration_count = 0;
_vm_operation_started_count = 0;
_vm_operation_completed_count = 0;
@@ -790,19 +789,6 @@
}
#endif
-bool Thread::lock_is_in_stack(address adr) const {
- assert(Thread::current() == this, "lock_is_in_stack can only be called from current thread");
- // High limit: highest_lock is set during thread execution
- // Low limit: address of the local variable dummy, rounded to 4K boundary.
- // (The rounding helps finding threads in unsafe mode, even if the particular stack
- // frame has been popped already. Correct as long as stacks are at least 4K long and aligned.)
- address end = os::current_stack_pointer();
- if (_highest_lock >= adr && adr >= end) return true;
-
- return false;
-}
-
-
bool Thread::is_in_stack(address adr) const {
assert(Thread::current() == this, "is_in_stack can only be called from current thread");
address end = os::current_stack_pointer();
@@ -818,8 +804,7 @@
// should be revisited, and they should be removed if possible.
bool Thread::is_lock_owned(address adr) const {
- if (lock_is_in_stack(adr) ) return true;
- return false;
+ return (_stack_base >= adr && adr >= (_stack_base - _stack_size));
}
bool Thread::set_as_starting_thread() {
@@ -1664,7 +1649,7 @@
}
bool JavaThread::is_lock_owned(address adr) const {
- if (lock_is_in_stack(adr)) return true;
+ if (Thread::is_lock_owned(adr)) return true;
for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) {
if (chunk->contains(adr)) return true;
@@ -2443,7 +2428,7 @@
if (thread_oop != NULL && java_lang_Thread::is_daemon(thread_oop)) st->print("daemon ");
Thread::print_on(st);
// print guess for valid stack memory region (assume 4K pages); helps lock debugging
- st->print_cr("[" INTPTR_FORMAT ".." INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12), highest_lock());
+ st->print_cr("[" INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12));
if (thread_oop != NULL && JDK_Version::is_gte_jdk15x_version()) {
st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop));
}
@@ -3733,25 +3718,13 @@
// heavyweight monitors, then the owner is the stack address of the
// Lock Word in the owning Java thread's stack.
//
- // We can't use Thread::is_lock_owned() or Thread::lock_is_in_stack() because
- // those routines rely on the "current" stack pointer. That would be our
- // stack pointer which is not relevant to the question. Instead we use the
- // highest lock ever entered by the thread and find the thread that is
- // higher than and closest to our target stack address.
- //
- address least_diff = 0;
- bool least_diff_initialized = false;
JavaThread* the_owner = NULL;
{
MutexLockerEx ml(doLock ? Threads_lock : NULL);
ALL_JAVA_THREADS(q) {
- address addr = q->highest_lock();
- if (addr == NULL || addr < owner) continue; // thread has entered no monitors or is too low
- address diff = (address)(addr - owner);
- if (!least_diff_initialized || diff < least_diff) {
- least_diff_initialized = true;
- least_diff = diff;
+ if (q->is_lock_owned(owner)) {
the_owner = q;
+ break;
}
}
}
--- a/hotspot/src/share/vm/runtime/thread.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/thread.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -200,14 +200,6 @@
friend class ThreadLocalStorage;
friend class GC_locker;
- // In order for all threads to be able to use fast locking, we need to know the highest stack
- // address of where a lock is on the stack (stacks normally grow towards lower addresses). This
- // variable is initially set to NULL, indicating no locks are used by the thread. During the thread's
- // execution, it will be set whenever locking can happen, i.e., when we call out to Java code or use
- // an ObjectLocker. The value is never decreased, hence, it will over the lifetime of a thread
- // approximate the real stackbase.
- address _highest_lock; // Highest stack address where a JavaLock exist
-
ThreadLocalAllocBuffer _tlab; // Thread-local eden
int _vm_operation_started_count; // VM_Operation support
@@ -400,18 +392,14 @@
// Sweeper support
void nmethods_do();
- // Fast-locking support
- address highest_lock() const { return _highest_lock; }
- void update_highest_lock(address base) { if (base > _highest_lock) _highest_lock = base; }
-
// Tells if adr belong to this thread. This is used
// for checking if a lock is owned by the running thread.
- // Warning: the method can only be used on the running thread
- // Fast lock support uses these methods
- virtual bool lock_is_in_stack(address adr) const;
+
+ // Used by fast lock support
virtual bool is_lock_owned(address adr) const;
// Check if address is in the stack of the thread (not just for locks).
+ // Warning: the method can only be used on the running thread
bool is_in_stack(address adr) const;
// Sets this thread as starting thread. Returns failure if thread
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Wed Jul 05 16:51:32 2017 +0200
@@ -656,7 +656,6 @@
\
volatile_nonstatic_field(Thread, _suspend_flags, uint32_t) \
nonstatic_field(Thread, _active_handles, JNIHandleBlock*) \
- nonstatic_field(Thread, _highest_lock, address) \
nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \
nonstatic_field(Thread, _current_pending_monitor, ObjectMonitor*) \
nonstatic_field(Thread, _current_pending_monitor_is_from_java, bool) \
--- a/hotspot/src/share/vm/utilities/accessFlags.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/utilities/accessFlags.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,6 +47,8 @@
JVM_ACC_IS_OLD = 0x00010000, // RedefineClasses() has replaced this method
JVM_ACC_IS_OBSOLETE = 0x00020000, // RedefineClasses() has made method obsolete
JVM_ACC_IS_PREFIXED_NATIVE = 0x00040000, // JVMTI has prefixed this native method
+ JVM_MH_INVOKE_BITS // = 0x10001100 // MethodHandle.invoke quasi-native
+ = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_MONITOR_MATCH),
// klassOop flags
JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable
@@ -72,6 +74,7 @@
// flags accepted by set_field_flags()
JVM_ACC_FIELD_FLAGS = 0x00008000 | JVM_ACC_WRITTEN_FLAGS
+
};
@@ -114,6 +117,15 @@
bool is_obsolete () const { return (_flags & JVM_ACC_IS_OBSOLETE ) != 0; }
bool is_prefixed_native () const { return (_flags & JVM_ACC_IS_PREFIXED_NATIVE ) != 0; }
+ // JSR 292: A method of the form MethodHandle.invoke(A...)R method is
+ // neither bytecoded nor a JNI native, but rather a fast call through
+ // a lightweight method handle object. Because it is not bytecoded,
+ // it has the native bit set, but the monitor-match bit is also set
+ // to distinguish it from a JNI native (which never has the match bit set).
+ // The synthetic bit is also present, because such a method is never
+ // explicitly defined in Java code.
+ bool is_method_handle_invoke () const { return (_flags & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS; }
+
// klassOop flags
bool has_miranda_methods () const { return (_flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; }
bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; }
@@ -199,6 +211,14 @@
jshort as_short() { return (jshort)_flags; }
jint as_int() { return _flags; }
+ inline friend AccessFlags accessFlags_from(jint flags);
+
// Printing/debugging
void print_on(outputStream* st) const PRODUCT_RETURN;
};
+
+inline AccessFlags accessFlags_from(jint flags) {
+ AccessFlags af;
+ af._flags = flags;
+ return af;
+}
--- a/hotspot/src/share/vm/utilities/exceptions.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/utilities/exceptions.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -237,6 +237,9 @@
#define THROW_ARG_0(name, signature, arg) THROW_ARG_(name, signature, arg, 0)
#define THROW_MSG_CAUSE_0(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, 0)
+#define THROW_NULL(name) THROW_(name, NULL)
+#define THROW_MSG_NULL(name, message) THROW_MSG_(name, message, NULL)
+
// The CATCH macro checks that no exception has been thrown by a function; it is used at
// call sites about which is statically known that the callee cannot throw an exception
// even though it is declared with TRAPS.
--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp Wed Jul 05 16:51:32 2017 +0200
@@ -408,6 +408,15 @@
return T_BOOLEAN <= t && t <= T_LONG;
}
+inline bool is_subword_type(BasicType t) {
+ // these guys are processed exactly like T_INT in calling sequences:
+ return (t == T_BOOLEAN || t == T_CHAR || t == T_BYTE || t == T_SHORT);
+}
+
+inline bool is_signed_subword_type(BasicType t) {
+ return (t == T_BYTE || t == T_SHORT);
+}
+
// Convert a char from a classfile signature to a BasicType
inline BasicType char2type(char c) {
switch( c ) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/6539464/Test.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 6539464
+ * @summary Math.log() produces inconsistent results between successive runs.
+ *
+ * @run main/othervm -Xcomp -XX:CompileOnly=Test.main Test
+ */
+
+public class Test {
+ static double log_value = 17197;
+ static double log_result = Math.log(log_value);
+
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 1000000; i++) {
+ double log_result2 = Math.log(log_value);
+ if (log_result2 != log_result) {
+ throw new InternalError("Math.log produces inconsistent results: " + log_result2 + " != " + log_result);
+ }
+ }
+ }
+}
--- a/hotspot/test/compiler/6636138/Test1.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/test/compiler/6636138/Test1.java Wed Jul 05 16:51:32 2017 +0200
@@ -26,7 +26,7 @@
* @bug 6636138
* @summary SuperWord::co_locate_pack(Node_List* p) generates memory graph that leads to memory order violation.
*
- * @run main/othervm -server -Xbatch -XX:CompileOnly=Test1.init -XX:+UseSuperword Test1
+ * @run main/othervm -server -Xbatch -XX:CompileOnly=Test1.init Test1
*/
class Test1 {
--- a/hotspot/test/compiler/6636138/Test2.java Thu Apr 16 11:23:02 2009 -0700
+++ b/hotspot/test/compiler/6636138/Test2.java Wed Jul 05 16:51:32 2017 +0200
@@ -26,7 +26,7 @@
* @bug 6636138
* @summary SuperWord::co_locate_pack(Node_List* p) generates memory graph that leads to memory order violation.
*
- * @run main/othervm -server -Xbatch -XX:CompileOnly=Test2.shift -XX:+UseSuperword Test2
+ * @run main/othervm -server -Xbatch -XX:CompileOnly=Test2.shift Test2
*/
class Test2 {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/6711117/Test.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,849 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+/*
+ * @test
+ * @bug 6711117
+ * @summary Assertion in 64bit server vm (flat != TypePtr::BOTTOM,"cannot alias-analyze an untyped ptr")
+ * @run main/othervm -Xcomp -XX:+IgnoreUnrecognizedVMOptions -XX:+AggressiveOpts -XX:+UseCompressedOops Test
+ */
+
+final class Test_Class_0 {
+ final static char var_1 = 'E';
+ short var_2 = 16213;
+ final static String var_3 = "jiiibmmsk";
+
+
+ public Test_Class_0()
+ {
+ var_2 ^= 'M';
+ final String var_18 = var_3;
+ var_2--;
+ var_2 |= (byte)('D' / (byte)var_2) - ((byte)1.6680514E38F << + ((byte)'O') & 7320241275829036032L);
+ func_2(((!false & false | false ? true : false) ? true : true | !true) ? var_2 : 834513107);
+ var_2 >>>= var_1;
+ "smiosoebk".codePointCount(true ^ (false ^ ! !false) ? (byte)- ((byte)430513598) : + ((byte)'_'), ~ (true ? (byte)']' : (byte)-2.8272547997066827E307));
+ var_2 -= true ? var_1 : var_1;
+ var_2 ^= var_1;
+ var_2 &= (var_2 |= ~ ((byte)(var_2 *= var_2)));
+ long var_19 = 0L;
+ short var_20 = var_2 += 'P';
+ while (var_19 < 1)
+ {
+ var_2 ^= true ? (byte)- +1.2219539475209E308 : (byte)1.2748408476894178E308;
+ var_19++;
+ var_2 = (byte)((1489358000 == (var_20 | 7816908224315289600L) ? var_1 : var_1) ^ var_19);
+ var_20--;
+ }
+ var_20 -= 'f';
+ var_20 <<= (((new Test_Class_0[(byte)var_20])[(byte)var_2]).var_2 *= false ? 'g' : 'x');
+ }
+
+
+
+
+ static float func_0()
+ {
+ ((new Test_Class_0[(byte)7.774490796987995E307])[(byte)'v']).var_2 <<= false ^ !false ? (short)'v' : "".codePointCount(594464985, 579036736);
+ ((new Test_Class_0[(byte)(((new Test_Class_0[(byte)1361657519])[(byte)2.3703713E38F]).var_2-- - (short)3.5589388134844986E307)])[((true ? !true : false) ^ (!false ? true : !true) ? !false : false) ? (byte)7.047289E37F : (byte)- ((byte)2.6620062118475144E307)]).var_2 *= 3273943364390983680L;
+ --((new Test_Class_0[false ? (byte)(short)1.4965069E36F : (byte)286322022])[(byte)- ((byte)2.742619E38F)]).var_2;
+ long var_4;
+ {
+ double var_5;
+ }
+ var_4 = (byte)1.3509231E38F;
+ ((new Test_Class_0[(byte)'_'])[('g' | 1427123046096105472L) < var_1 >> (byte)(int)(byte)7697616672011068416L ? (byte)var_1 : (byte)1251856579]).var_2--;
+ switch (--((new Test_Class_0[(byte)5.0656327E37F])[(byte)'e']).var_2 != ++((new Test_Class_0[(byte)(int)1.3728667270920175E308])[(byte)+ + -1.6338179407381788E308]).var_2 | !var_3.equalsIgnoreCase("iiwwwln") ? (false ? (byte)1.8291216E38F : (byte)4.778575546584698E307) : (byte)1048254181)
+ {
+ case 99:
+
+ }
+ {
+ byte var_6 = 13;
+ }
+ var_4 = --((new Test_Class_0[!var_3.endsWith("qaoioore") ^ false ? (byte)2.827362738392923E307 : (byte)~4890175967151316992L])[(byte)(short)var_1]).var_2;
+ ++((new Test_Class_0[(byte)(1.0075552E38F + (short)2083553541)])[(byte)(short)(byte)(short)1.6872205E38F]).var_2;
+ return ((new Test_Class_0[(byte)var_1])[(byte)+ +5760973323384750080L]).var_2 - (false ? (byte)'i' : (var_4 = (short)1.2458781351126844E308) + 2.131006E38F);
+ }
+
+ public static long func_1(String arg_0, Object arg_1, final long arg_2)
+ {
+ arg_0 = false ? arg_0 : "fgbrpgsq";
+ ((new Test_Class_0[(byte)- ((byte)']')])[false ? (byte)757239006 : (byte)1866002020]).var_2 ^= (short)(true ? (byte)(((new Test_Class_0[(byte)1416194866])[(byte)1.2309887362692395E308]).var_2 >>= (int)~ ~ ~arg_2) : (byte)5804970709284726784L);
+ final long var_7 = (long)(- + ((long)+ - + - -2.5396583E38F) - - +1.8770165E38F % 2472404173160781824L < --((new Test_Class_0[(byte)5.569360482341752E307])[(byte)(double)(byte)8131142397821553664L]).var_2 ^ true ? (false ? (byte)- -1.163275451591927E308 : (byte)var_1) : (false ? (byte)1843746036 : (byte)1.0209668642291047E308));
+ arg_0 = (arg_0 = arg_0.substring(699480935));
+ switch (((new Test_Class_0[(byte)(5415649243316856832L >> 861936806)])[true | true & !false ? (byte)(short)- -7.785169683394908E307 : (byte)+ ((byte)arg_2)]).var_2++)
+ {
+ case 42:
+
+ case 102:
+
+ }
+ arg_1 = (true || false ? false : true) ? (arg_0 = (arg_0 = "jbfaru")) : arg_0;
+ arg_1 = new byte[(byte)2.669957E38F];
+ boolean var_8 = ! ((false ? (short)1.4259420861834744E308 : (short)7.352115508157158E307) != 1.7635658130722812E308);
+ arg_1 = new Object[(byte)- ((byte)(short)1.8950693E38F)];
+ arg_0 = arg_0;
+ return (byte)1.4762239057269886E308 & 4923938844759802880L;
+ }
+
+ double[][] func_2(final int arg_0)
+ {
+ var_2 >>>= (var_2 >>= var_2++);
+ float var_9 = 0F;
+ var_2 %= var_2;
+ do
+ {
+ ++var_2;
+ var_9++;
+ var_2++;
+ } while (true && (var_9 < 1 && false));
+ double var_10 = 0;
+ final int var_11 = 11903395;
+ do
+ {
+ --var_2;
+ var_10++;
+ ++var_2;
+ } while ((false & true || false) && (var_10 < 2 && ~ ((byte)'[') == (byte)(1.1943192E38F % ('c' << var_1) % (byte)((var_2 |= var_2) + 591679039 / ~5932100696448264192L))));
+ String var_12 = "jkwnk";
+ var_12 = var_3;
+ var_12 = (var_12 = (var_12 = var_3));
+ var_12 = "qrhdwx";
+ var_12 = var_12;
+ short var_13 = (true && true) ^ true | ! (!true || 1646418779 <= (byte)var_1) ? var_2 : var_2;
+ return new double[(byte)var_1][true || false ^ !true ^ true ? (byte)arg_0 : (byte)var_10];
+ }
+
+ private final int func_3()
+ {
+ long var_14 = 's' * (~ ~6656240461354863616L * 3151744928387344384L) << ~ (((var_2 >>>= 6600935261424147456L) % 1798503219359364096L | - ~3832249967647077376L / - ((byte)~1529201870915276800L)) / var_2);
+ {
+ var_14 |= !false | (byte)1078230528 >= (byte)1.3972878565417081E308 | (true | !true & !true & !false) ? var_1 : '_';
+ }
+ long var_15 = 7589204885152164864L;
+ var_2 ^= (var_1 < (byte)'r' ? 475314139 : 'Z') <= 1943074698 ? 'h' : var_1;
+ return 'V' * (false ? (byte)5.498204E37F : (byte)1.0137001669765466E308);
+ }
+
+ protected static boolean func_4(boolean arg_0, byte arg_1, boolean arg_2)
+ {
+ arg_1++;
+ arg_1 &= (((((new Test_Class_0[arg_1][arg_1][arg_1])[arg_1])[arg_1])[arg_1]).var_2 |= arg_2 ? (short)~3038084056596854784L : (short)+ (arg_1 = arg_1));
+ arg_0 |= true;
+ arg_1 %= (arg_1 |= ((new Test_Class_0[arg_1])[arg_1]).var_2--);
+ if (false)
+ {
+ arg_0 |= arg_2;
+ }
+ else
+ {
+ ++(((new Test_Class_0[arg_1][arg_1][arg_1])[arg_1 += var_1])[(!arg_2 | (arg_0 &= false)) ^ (arg_0 | arg_0) ? arg_1 : (arg_1 <<= 3192041751921364992L)][arg_1 /= arg_1]).var_2;
+ }
+ arg_1 &= +(new byte[arg_1])[arg_1];
+ arg_1 <<= 3632133838014908416L;
+ byte[] var_16 = (new byte[arg_1][arg_1--])[arg_1];
+ long var_17;
+ arg_1 ^= ~ arg_1--;
+ arg_0 ^= (arg_2 ^= 1186877294 >= ((new Test_Class_0[arg_1][arg_1])[arg_1][arg_1]).var_2) & arg_2;
+ return var_3.startsWith(var_3);
+ }
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_0.var_2 = "; result += Test.Printer.print(var_2);
+ result += "\n";
+ result += "Test_Class_0.var_1 = "; result += Test.Printer.print(var_1);
+ result += "\n";
+ result += "Test_Class_0.var_3 = "; result += Test.Printer.print(var_3);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+
+class Test_Class_1 {
+ static int var_21 = 670918363;
+ final float var_22 = 8.650798E37F;
+ static int var_23 = 1774228457;
+ final int var_24 = 1282736974;
+ final byte var_25 = !false & false | true ? (byte)7.677121016144275E307 : (byte)'r';
+ static long var_26 = 2939310115459338240L;
+ final long var_27 = var_25 - 7555453173456381952L;
+ double var_28;
+ static String var_29;
+
+
+ public Test_Class_1()
+ {
+ var_29 = Test_Class_0.var_3;
+ ((false ? false || ! !true : ! (! !true & !true)) ? new Test_Class_0() : new Test_Class_0()).var_2++;
+ var_23 -= 2.963694E38F;
+ }
+
+
+
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_1.var_21 = "; result += Test.Printer.print(var_21);
+ result += "\n";
+ result += "Test_Class_1.var_23 = "; result += Test.Printer.print(var_23);
+ result += "\n";
+ result += "Test_Class_1.var_24 = "; result += Test.Printer.print(var_24);
+ result += "\n";
+ result += "Test_Class_1.var_26 = "; result += Test.Printer.print(var_26);
+ result += "\n";
+ result += "Test_Class_1.var_27 = "; result += Test.Printer.print(var_27);
+ result += "\n";
+ result += "Test_Class_1.var_28 = "; result += Test.Printer.print(var_28);
+ result += "\n";
+ result += "Test_Class_1.var_22 = "; result += Test.Printer.print(var_22);
+ result += "\n";
+ result += "Test_Class_1.var_25 = "; result += Test.Printer.print(var_25);
+ result += "\n";
+ result += "Test_Class_1.var_29 = "; result += Test.Printer.print(var_29);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+
+class Test_Class_2 {
+ double var_30;
+ static byte var_31;
+ static char var_32;
+ float var_33;
+ double var_34 = !false & (true ? true : ! !true && false) ? 'q' - 4789231433793305600L - (var_33 = -1.0677024E38F) : 2.65473560313378E307;
+ final double var_35 = ~Test_Class_1.var_26 == 5.145660681364723E307 | false ? 1.4134775E38F : 1.77223030708671E308;
+ final int var_36 = Test_Class_1.var_23 |= Test_Class_1.var_21++;
+
+
+ public Test_Class_2()
+ {
+ Test_Class_0.var_3.replace(Test_Class_0.var_1, 'Q');
+ var_32 = (var_32 = (var_32 = '_'));
+ Test_Class_1.var_26 |= Test_Class_0.var_1;
+ Test_Class_1.var_29 = (Test_Class_1.var_29 = Test_Class_0.var_3);
+ var_32 = Test_Class_0.var_1;
+ var_33 = ((new Test_Class_0[(byte)851412948463452160L])[var_31 = new Test_Class_1().var_25]).var_2;
+ var_33 = ! (((!false | false) & (false || !true) ? false : ! !false) | false) ? new Test_Class_1().var_25 : (var_31 = new Test_Class_1().var_25);
+ float var_38 = 0F;
+ var_34 /= 5336005797857974272L;
+ for ("ccnyq".endsWith((new String[(byte)Test_Class_1.var_26])[var_31 = (var_31 = (var_31 = (byte)4.7927775E37F))]); var_38 < 2; var_32 = '^' <= Test_Class_0.var_1 ^ true ? (var_32 = Test_Class_0.var_1) : (var_32 = 'V'))
+ {
+ var_32 = true ? 'a' : (var_32 = Test_Class_0.var_1);
+ var_38++;
+ var_33 = new Test_Class_1().var_24;
+ var_32 = ! (true || true ? !false : (short)3.2844383E37F < 2.1400662E38F) ? (char)1.2691096999143248E308 : (! !false ^ true ? 's' : 'q');
+ }
+ var_32 = 'B';
+ {
+ var_32 = Test_Class_0.var_1;
+ }
+ var_32 = Test_Class_0.var_1;
+ Test_Class_1.var_29 = "ov";
+ Test_Class_1.var_29 = "smtolghw";
+ }
+
+
+
+
+
+ protected final static String func_0(final long[][] arg_0, byte arg_1, char arg_2)
+ {
+ arg_1 <<= (((new Test_Class_2[arg_1])[arg_1]).var_34 > new Test_Class_0().var_2 | true ? new Test_Class_0() : (new Test_Class_0[arg_1][arg_1])[new Test_Class_1().var_25][new Test_Class_1().var_25]).var_2;
+ Test_Class_1.var_26 >>>= (!true | !true | (new boolean[arg_1])[arg_1] || true ? (new Test_Class_1[arg_1])[arg_1] : new Test_Class_1()).var_27;
+ float var_37 = 0F;
+ arg_2 >>= ((new Test_Class_1[arg_1][arg_1])[arg_1][arg_1]).var_25;
+ do
+ {
+ ((new Test_Class_2[arg_1 /= 2055714081])[arg_1]).var_34 = 'l';
+ var_37++;
+ Test_Class_1.var_29 = Test_Class_0.var_3;
+ } while ((false ? false : false) && var_37 < 7);
+ Test_Class_1.var_29 = Test_Class_0.var_3 + "";
+ ((new Test_Class_2[new Test_Class_1().var_25][new Test_Class_1().var_25])[new Test_Class_1().var_25][arg_1 |= new Test_Class_0().var_2]).var_34 += Test_Class_0.var_1;
+ return "esb";
+ }
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_2.var_32 = "; result += Test.Printer.print(var_32);
+ result += "\n";
+ result += "Test_Class_2.var_36 = "; result += Test.Printer.print(var_36);
+ result += "\n";
+ result += "Test_Class_2.var_30 = "; result += Test.Printer.print(var_30);
+ result += "\n";
+ result += "Test_Class_2.var_34 = "; result += Test.Printer.print(var_34);
+ result += "\n";
+ result += "Test_Class_2.var_35 = "; result += Test.Printer.print(var_35);
+ result += "\n";
+ result += "Test_Class_2.var_33 = "; result += Test.Printer.print(var_33);
+ result += "\n";
+ result += "Test_Class_2.var_31 = "; result += Test.Printer.print(var_31);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+
+final class Test_Class_3 extends Test_Class_2 {
+ byte var_39 = 23;
+ static boolean var_40 = false;
+
+
+ public Test_Class_3()
+ {
+ if (true)
+ {
+ Test_Class_1.var_21 |= new Test_Class_1().var_27;
+ }
+ else
+ {
+ final float var_46 = 7.9266674E37F;
+ ++Test_Class_1.var_26;
+ }
+ {
+ Test_Class_1.var_23++;
+ }
+ var_30 = ((new Test_Class_1[var_39][var_39])[var_39][var_39]).var_25;
+ if (var_40 &= (var_40 |= (var_40 |= var_40)))
+ {
+ Test_Class_0.var_3.indexOf(Test_Class_1.var_29 = "xfgyblg", 'X' >>> ((Test_Class_1)(new Object[var_39])[((new Test_Class_1[var_39])[var_39]).var_25]).var_27);
+ }
+ else
+ {
+ var_40 &= var_40 && var_40;
+ }
+ ((Test_Class_2)(((new boolean[var_39])[var_39++] ? (var_40 &= var_40) : (var_40 &= false)) ? (new Test_Class_2[var_39][var_39])[var_39][var_39] : (new Object[var_39][var_39])[var_39][var_39])).var_33 = (var_40 ? new Test_Class_1() : new Test_Class_1()).var_25;
+ switch (var_39)
+ {
+ case 24:
+
+ }
+ var_39 += (((var_40 ^= true) ? new Test_Class_0() : new Test_Class_0()).var_2 ^= var_40 & (var_40 | false) ? var_39-- : var_36);
+ new Test_Class_0().var_2 %= (new Test_Class_0().var_2 += (var_39 ^= Test_Class_1.var_26));
+ }
+
+
+
+
+ private static String func_0()
+ {
+ --Test_Class_1.var_26;
+ {
+ Test_Class_1.var_29 = var_40 ? Test_Class_0.var_3 : "rahqjhqf";
+ }
+ if (var_40 ^= var_40)
+ {
+ Test_Class_1.var_26 >>= (Test_Class_2.var_32 = Test_Class_0.var_1) / new Test_Class_0().var_2;
+ }
+ else
+ {
+ ++Test_Class_1.var_21;
+ }
+ ++Test_Class_1.var_26;
+ int var_41 = 0;
+ ++Test_Class_1.var_26;
+ do
+ {
+ var_40 = (var_40 = true);
+ var_41++;
+ Test_Class_0 var_42 = new Test_Class_0();
+ } while (var_41 < 1);
+ Test_Class_1.var_29 = "f";
+ Test_Class_1 var_43;
+ var_43 = (var_43 = new Test_Class_1());
+ Test_Class_2.var_32 = 'V';
+ long var_44 = 0L;
+ Test_Class_1.var_23--;
+ while (var_40 && (var_44 < 1 && var_40))
+ {
+ Test_Class_1.var_29 = "bsgewkmk";
+ var_44++;
+ Test_Class_1.var_29 = "ktegattny";
+ var_40 &= var_40 ^ (var_40 |= (short)4.4487427E37F < 'n') & true;
+ }
+ Test_Class_1.var_23 %= (((var_40 |= true & (var_40 &= var_40)) ^ true ? new Test_Class_0() : new Test_Class_0()).var_2 -= 1.6638270827800162E308);
+ float var_45;
+ var_32 = (Test_Class_2.var_32 = Test_Class_0.var_1);
+ return false ? "fluk" : "wt";
+ }
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_3.var_32 = "; result += Test.Printer.print(var_32);
+ result += "\n";
+ result += "Test_Class_3.var_36 = "; result += Test.Printer.print(var_36);
+ result += "\n";
+ result += "Test_Class_3.var_30 = "; result += Test.Printer.print(var_30);
+ result += "\n";
+ result += "Test_Class_3.var_34 = "; result += Test.Printer.print(var_34);
+ result += "\n";
+ result += "Test_Class_3.var_35 = "; result += Test.Printer.print(var_35);
+ result += "\n";
+ result += "Test_Class_3.var_33 = "; result += Test.Printer.print(var_33);
+ result += "\n";
+ result += "Test_Class_3.var_31 = "; result += Test.Printer.print(var_31);
+ result += "\n";
+ result += "Test_Class_3.var_39 = "; result += Test.Printer.print(var_39);
+ result += "\n";
+ result += "Test_Class_3.var_40 = "; result += Test.Printer.print(var_40);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+
+class Test_Class_4 {
+ final float var_47 = 1.9043434E38F;
+ final byte var_48 = 32;
+ final float var_49 = 2.8176504E38F;
+ final char var_50 = 'r';
+ final String var_51 = "uwgmnjpg";
+ static int var_52;
+ short[] var_53;
+ Test_Class_1 var_54;
+
+
+ public Test_Class_4()
+ {
+ final float var_55 = (3.1554042E38F == var_50 ^ (Test_Class_3.var_40 |= true) ? (Test_Class_3.var_40 ^= Test_Class_3.var_40) ^ true : Test_Class_3.var_40) ? new Test_Class_0().var_2 : 2.965321E38F;
+ new Test_Class_0().var_2 = (new Test_Class_0().var_2 >>= +new Test_Class_1().var_25);
+ ((Test_Class_1.var_29 = (Test_Class_1.var_29 = (Test_Class_1.var_29 = "l"))) + "").equalsIgnoreCase(Test_Class_1.var_29 = "garnio");
+ double var_56 = 0;
+ Test_Class_1.var_29 = var_51;
+ while (var_56 < 1)
+ {
+ ((Test_Class_3)(Test_Class_2)(new Object[var_48])[var_48]).var_33 = ++Test_Class_1.var_26;
+ var_56++;
+ Test_Class_1.var_29 = (Test_Class_1.var_29 = "fvyjrih");
+ float[] var_57;
+ }
+ {
+ ((new Test_Class_2[var_48])[((new Test_Class_3[var_48][var_48])[var_48][var_48]).var_39]).var_34 *= 2.2119221943262553E307;
+ Test_Class_2.var_32 = true ? 'q' : 't';
+ ((new Test_Class_3[--((Test_Class_3)new Test_Class_2()).var_39])[var_48]).var_33 = new Test_Class_0().var_2;
+ int var_58 = 'i' >> (var_48 << Test_Class_0.var_1);
+ }
+ Test_Class_3.var_40 &= true && var_51.equalsIgnoreCase(var_51) || new Test_Class_0().var_2 < --((new Test_Class_3[var_48])[var_48]).var_39;
+ ((Test_Class_3)(Test_Class_2)(new Object[var_48][var_48])[var_48][var_48]).var_34 += Test_Class_1.var_26--;
+ var_54 = new Test_Class_1();
+ Test_Class_3.var_40 |= (long)(!true ^ var_47 > ((Test_Class_2)(new Object[var_48])[var_48]).var_34 ? (Test_Class_2.var_31 = (Test_Class_3.var_31 = (Test_Class_3.var_31 = var_48))) : (var_54 = new Test_Class_1()).var_25) <= var_48;
+ (Test_Class_3.var_40 ? (true ? new Test_Class_0() : new Test_Class_0()) : new Test_Class_0()).var_2 &= var_48;
+ (Test_Class_3.var_40 ? (Test_Class_3)new Test_Class_2() : (new Test_Class_3[var_48][var_48])[var_48][var_48]).var_34 += Test_Class_1.var_21;
+ Test_Class_3 var_59;
+ Test_Class_2.var_32 = 'H';
+ --Test_Class_1.var_26;
+ }
+
+
+
+
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_4.var_50 = "; result += Test.Printer.print(var_50);
+ result += "\n";
+ result += "Test_Class_4.var_52 = "; result += Test.Printer.print(var_52);
+ result += "\n";
+ result += "Test_Class_4.var_53 = "; result += Test.Printer.print(var_53);
+ result += "\n";
+ result += "Test_Class_4.var_47 = "; result += Test.Printer.print(var_47);
+ result += "\n";
+ result += "Test_Class_4.var_49 = "; result += Test.Printer.print(var_49);
+ result += "\n";
+ result += "Test_Class_4.var_48 = "; result += Test.Printer.print(var_48);
+ result += "\n";
+ result += "Test_Class_4.var_51 = "; result += Test.Printer.print(var_51);
+ result += "\n";
+ result += "Test_Class_4.var_54 = "; result += Test.Printer.print(var_54);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+
+class Test_Class_5 extends Test_Class_4 {
+ char var_60 = '_';
+ final byte var_61 = 101;
+
+
+ public Test_Class_5()
+ {
+ Test_Class_0.var_3.indexOf(Test_Class_1.var_21, (Test_Class_3.var_40 |= Test_Class_3.var_40) ? new Test_Class_1().var_24 : 'i');
+ }
+
+
+
+
+ final char func_0(Test_Class_1 arg_0, final Test_Class_1 arg_1)
+ {
+ long var_62 = 0L;
+ "aoal".toLowerCase();
+ for (byte var_63 = arg_0.var_25; var_62 < 1 && "ji".startsWith("dikrs".endsWith("va") ? (Test_Class_1.var_29 = "mvp") : Test_Class_0.var_3, Test_Class_1.var_23); ((Test_Class_2)(new Object[arg_0.var_25])[var_63]).var_34 -= new Test_Class_2().var_36)
+ {
+ ((Test_Class_3.var_40 ? false : Test_Class_3.var_40) ? (Test_Class_0)(new Object[arg_1.var_25][arg_1.var_25])[arg_1.var_25][var_63] : (Test_Class_0)(new Object[var_48][var_48])[var_63][var_63]).var_2 += true ^ Test_Class_3.var_40 ^ (((new Test_Class_3[var_63][var_63])[var_63][var_61]).var_35 != 2.1423512E38F | ! !false) ? var_49 + ~var_48 : 3.1549515E38F;
+ var_62++;
+ (!false & ((Test_Class_3.var_40 |= (Test_Class_3.var_40 ^= true)) & true) ? (Test_Class_2)(new Object[var_63])[var_63] : (new Test_Class_2[var_63][var_61])[var_63][arg_0.var_25]).var_33 = (var_60 *= (var_60 *= ((new Test_Class_3[var_48][var_61])[var_61][var_63]).var_35));
+ float var_64;
+ }
+ Test_Class_1.var_29 = "xyenjknu";
+ Test_Class_3.var_40 ^= (Test_Class_3.var_40 = !false & true) ? Test_Class_3.var_40 : Test_Class_3.var_40;
+ ((new Test_Class_2[var_48][arg_1.var_25])[arg_0.var_25][var_48]).var_33 = var_61;
+ Test_Class_1.var_21 |= --(((new Test_Class_3[Test_Class_3.var_31 = arg_0.var_25][var_61])[var_61])[(((new Test_Class_3[var_48][var_61])[var_48])[((Test_Class_3)(new Test_Class_2[var_48][arg_0.var_25])[var_61][var_48]).var_39]).var_39 >>>= var_60]).var_39;
+ var_51.compareToIgnoreCase("hgcaybk");
+ Test_Class_0 var_65 = (Test_Class_1.var_29 = "t").codePointBefore(1602805584) >= (float)((new Test_Class_3[var_48][var_61])[var_48][Test_Class_2.var_31 = arg_1.var_25]).var_39 - 7.256386549028811E307 ? new Test_Class_0() : ((new Test_Class_0[arg_0.var_25][var_48][var_48])[arg_0.var_25])[arg_0.var_25][Test_Class_2.var_31 = arg_1.var_25];
+ return 'U';
+ }
+
+ protected static Test_Class_1 func_1(final short arg_0, long arg_1)
+ {
+ --new Test_Class_0().var_2;
+ "xb".length();
+ if ((Test_Class_3.var_40 ^= (Test_Class_2.var_32 = Test_Class_0.var_1) == 1.2609472E38F) ? (Test_Class_3.var_40 = (Test_Class_3.var_40 = Test_Class_3.var_40)) : true)
+ {
+ --Test_Class_1.var_26;
+ }
+ else
+ {
+ "ybbe".substring(209378562, var_52 = (Test_Class_1.var_21 |= (Test_Class_2.var_31 = (byte)'a')));
+ }
+ Test_Class_3.var_40 &= (Test_Class_3.var_40 &= true) && (Test_Class_1.var_29 = (Test_Class_1.var_29 = Test_Class_0.var_3)).endsWith(Test_Class_0.var_3);
+ (false ? new Test_Class_0() : new Test_Class_0()).var_2 >>= new Test_Class_1().var_25;
+ return 9.430116214455637E307 <= (true ? (Test_Class_3)new Test_Class_2() : (Test_Class_3)new Test_Class_2()).var_34 ? new Test_Class_1() : new Test_Class_1();
+ }
+
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test_Class_5.var_50 = "; result += Test.Printer.print(var_50);
+ result += "\n";
+ result += "Test_Class_5.var_60 = "; result += Test.Printer.print(var_60);
+ result += "\n";
+ result += "Test_Class_5.var_52 = "; result += Test.Printer.print(var_52);
+ result += "\n";
+ result += "Test_Class_5.var_53 = "; result += Test.Printer.print(var_53);
+ result += "\n";
+ result += "Test_Class_5.var_47 = "; result += Test.Printer.print(var_47);
+ result += "\n";
+ result += "Test_Class_5.var_49 = "; result += Test.Printer.print(var_49);
+ result += "\n";
+ result += "Test_Class_5.var_48 = "; result += Test.Printer.print(var_48);
+ result += "\n";
+ result += "Test_Class_5.var_61 = "; result += Test.Printer.print(var_61);
+ result += "\n";
+ result += "Test_Class_5.var_51 = "; result += Test.Printer.print(var_51);
+ result += "\n";
+ result += "Test_Class_5.var_54 = "; result += Test.Printer.print(var_54);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+}
+
+public class Test {
+ Test_Class_4 var_66;
+ Test_Class_3 var_67;
+ Test_Class_5 var_68;
+ Test_Class_2[] var_69;
+ long var_70 = ++Test_Class_1.var_26 & Test_Class_1.var_21++;
+ final static double var_71 = 3.566207721984698E307;
+ static boolean var_72;
+ final static String var_73 = "nmxx";
+
+
+ private final char func_0(Test_Class_3 arg_0, final boolean[] arg_1)
+ {
+ ((Test_Class_5)(arg_1[arg_0.var_39++] ? new Test_Class_2[(var_67 = arg_0).var_39] : (new Object[arg_0.var_39])[arg_0.var_39])).var_54 = new Test_Class_1();
+ new Test_Class_0();
+ (((new Test[arg_0.var_39][arg_0.var_39][arg_0.var_39])[++arg_0.var_39])[arg_0.var_39][arg_0.var_39]).var_66 = (var_68 = (new Test_Class_5[arg_0.var_39][arg_0.var_39])[arg_0.var_39][arg_0.var_39]);
+ ((new Test[arg_0.var_39])[(arg_0 = (var_67 = (arg_0 = arg_0))).var_39]).var_70 = ((new long[arg_0.var_39][arg_0.var_39])[arg_0.var_39])[arg_0.var_39 = ((var_67 = (arg_0 = arg_0)).var_39 -= new Test_Class_0().var_2)] << ']';
+ arg_0 = (new Test_Class_0().var_2 *= ((new Test_Class_2[arg_0.var_39])[arg_0.var_39]).var_34) >= arg_0.var_39 ? (var_67 = arg_0) : (arg_0 = arg_0);
+ Test_Class_1.var_26--;
+ Test_Class_4 var_74 = var_66 = (Test_Class_5)(new Test_Class_4[arg_0.var_39])[arg_0.var_39];
+ Test_Class_3.var_40 ^= ! (Test_Class_3.var_40 &= (Test_Class_3.var_40 ^= Test_Class_3.var_40) | (Test_Class_3.var_40 &= Test_Class_3.var_40));
+ var_72 = (arg_1[(var_67 = arg_0).var_39] | !Test_Class_3.var_40 & !Test_Class_3.var_40 ? (Test_Class_1.var_29 = var_73).endsWith((var_66 = var_74).var_51) && (Test_Class_3.var_40 ^= Test_Class_3.var_40) : (Test_Class_3.var_40 ^= Test_Class_3.var_40)) ^ !Test_Class_3.var_40;
+ Test_Class_3.var_40 &= (Test_Class_3.var_40 &= (Test_Class_3.var_40 = Test_Class_3.var_40) & Test_Class_3.var_40 ^ Test_Class_3.var_40);
+ arg_0.var_39 -= --var_70;
+ int var_75;
+ double var_76;
+ {
+ boolean var_77;
+ var_70 ^= new Test_Class_0().var_2++;
+ }
+ Test_Class_1.var_26 /= Test_Class_0.var_3.lastIndexOf(~new Test_Class_1().var_25, Test_Class_1.var_21);
+ Test_Class_1.var_26 |= Test_Class_1.var_21;
+ (((new Test_Class_3[arg_0.var_39][arg_0.var_39][var_74.var_48])[arg_0.var_39])[arg_0.var_39][arg_0.var_39]).var_34 %= (var_67 = arg_0).var_39;
+ Test_Class_1.var_21 &= arg_0.var_39;
+ var_68 = (var_68 = (Test_Class_5)var_74);
+ var_72 = false;
+ return new Test_Class_5().var_60 ^= 'v';
+ }
+
+ public static Test_Class_2 func_1(byte[][] arg_0, final int arg_1, Test_Class_1 arg_2, final Test_Class_1 arg_3)
+ {
+ ((new Test[arg_3.var_25])[((Test_Class_3)new Test_Class_2()).var_39 *= --Test_Class_1.var_26]).var_67 = (((new Test[arg_2.var_25])[(((new Test[arg_2.var_25][arg_2.var_25])[arg_3.var_25][arg_3.var_25]).var_67 = (new Test_Class_3[arg_2.var_25][arg_2.var_25])[arg_2.var_25][arg_3.var_25]).var_39 %= Test_Class_1.var_26]).var_67 = (((new Test[arg_3.var_25][arg_2.var_25])[arg_3.var_25][arg_2.var_25]).var_67 = (((new Test[arg_3.var_25])[arg_2.var_25]).var_67 = (Test_Class_3)new Test_Class_2())));
+ {
+ --Test_Class_1.var_26;
+ }
+ if (!Test_Class_3.var_40)
+ {
+ "jfqj".replaceAll("ac", Test_Class_0.var_3);
+ }
+ else
+ {
+ arg_2 = (((new Test_Class_5[arg_3.var_25][arg_2.var_25])[((new Test_Class_3[arg_2.var_25])[arg_3.var_25]).var_39][((Test_Class_3)(new Test_Class_2[arg_2.var_25])[arg_3.var_25]).var_39]).var_54 = arg_3);
+ new Test_Class_1();
+ }
+ if (true)
+ {
+ Test_Class_0.func_0();
+ }
+ else
+ {
+ Test_Class_1.var_23 /= Test_Class_1.var_26;
+ }
+ Test_Class_1.var_26--;
+ Test_Class_1.var_23 ^= Test_Class_0.var_1;
+ return new Test_Class_2();
+ }
+
+ public static String execute()
+ {
+ try {
+ Test t = new Test();
+ try { t.test(); }
+ catch(Throwable e) { }
+ try { return t.toString(); }
+ catch (Throwable e) { return "Error during result conversion to String"; }
+ } catch (Throwable e) { return "Error during test execution"; }
+ }
+
+ public static void main(String[] args)
+ {
+ try {
+ Test t = new Test();
+ try { t.test(); }
+ catch(Throwable e) { }
+ try { System.out.println(t); }
+ catch(Throwable e) { }
+ } catch (Throwable e) { }
+ }
+
+ private void test()
+ {
+ double var_78 = 0;
+ --Test_Class_1.var_26;
+ long var_79;
+ for (var_70 /= 8.089457748637276E307; var_78 < 162 && !true & (true ? Test_Class_3.var_40 : (Test_Class_3.var_40 ^= Test_Class_3.var_40)); Test_Class_1.var_26 -= 1.2513521E38F)
+ {
+ short var_80 = 10682;
+ Test_Class_1.var_21--;
+ var_78++;
+ var_72 = (Test_Class_3.var_40 |= (Test_Class_3.var_40 ^= false));
+ ++Test_Class_1.var_26;
+ }
+ Test_Class_2 var_81;
+ new Test_Class_4();
+ int var_82 = 0;
+ ++Test_Class_1.var_23;
+ do
+ {
+ --Test_Class_1.var_26;
+ var_82++;
+ ++Test_Class_1.var_21;
+ } while ((Test_Class_3.var_40 ^= false & false) && var_82 < 256);
+ Test_Class_1.var_23 |= (var_68 = (var_68 = (Test_Class_5)(var_66 = new Test_Class_4()))).var_48 + (Test_Class_1.var_26 >>> new Test_Class_0().var_2);
+ (true ? new Test_Class_5() : (var_68 = (var_68 = new Test_Class_5()))).var_60 *= Test_Class_0.var_1;
+ }
+ public String toString()
+ {
+ String result = "[\n";
+ result += "Test.var_69 = "; result += Printer.print(var_69);
+ result += "\n";
+ result += "Test.var_70 = "; result += Printer.print(var_70);
+ result += "\n";
+ result += "Test.var_71 = "; result += Printer.print(var_71);
+ result += "\n";
+ result += "Test.var_73 = "; result += Printer.print(var_73);
+ result += "\n";
+ result += "Test.var_68 = "; result += Printer.print(var_68);
+ result += "\n";
+ result += "Test.var_66 = "; result += Printer.print(var_66);
+ result += "\n";
+ result += "Test.var_72 = "; result += Printer.print(var_72);
+ result += "\n";
+ result += "Test.var_67 = "; result += Printer.print(var_67);
+ result += "";
+ result += "\n]";
+ return result;
+ }
+ static class Printer
+ {
+ public static String print(boolean arg) { return String.valueOf(arg); }
+ public static String print(byte arg) { return String.valueOf(arg); }
+ public static String print(short arg) { return String.valueOf(arg); }
+ public static String print(char arg) { return String.valueOf((int)arg); }
+ public static String print(int arg) { return String.valueOf(arg); }
+ public static String print(long arg) { return String.valueOf(arg); }
+ public static String print(float arg) { return String.valueOf(arg); }
+ public static String print(double arg) { return String.valueOf(arg); }
+
+
+ public static String print(Object arg)
+ {
+ return print_r(new java.util.Stack(), arg);
+ }
+
+ private static String print_r(java.util.Stack visitedObjects, Object arg)
+ {
+ String result = "";
+ if (arg == null)
+ result += "null";
+ else
+ if (arg.getClass().isArray())
+ {
+ for (int i = 0; i < visitedObjects.size(); i++)
+ if (visitedObjects.elementAt(i) == arg) return "<recursive>";
+
+ visitedObjects.push(arg);
+
+ final String delimiter = ", ";
+ result += "[";
+
+ if (arg instanceof Object[])
+ {
+ Object[] array = (Object[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print_r(visitedObjects, array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof boolean[])
+ {
+ boolean[] array = (boolean[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof byte[])
+ {
+ byte[] array = (byte[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof short[])
+ {
+ short[] array = (short[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof char[])
+ {
+ char[] array = (char[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof int[])
+ {
+ int[] array = (int[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof long[])
+ {
+ long[] array = (long[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof float[])
+ {
+ float[] array = (float[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+ else
+ if (arg instanceof double[])
+ {
+ double[] array = (double[]) arg;
+ for (int i = 0; i < array.length; i++)
+ {
+ result += print(array[i]);
+ if (i < array.length - 1) result += delimiter;
+ }
+ }
+
+ result += "]";
+ visitedObjects.pop();
+
+ } else
+ {
+ result += arg.toString();
+ }
+
+ return result;
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/6823453/Test.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+/*
+ * @test
+ * @bug 6823453
+ * @summary DeoptimizeALot causes fastdebug server jvm to fail with assert(false,"unscheduable graph")
+ * @run main/othervm -Xcomp -XX:CompileOnly=Test -XX:+DeoptimizeALot Test
+ */
+
+public class Test {
+
+ static long vara_1 = 1L;
+
+ static void testa() {
+ short var_2 = (byte) 1.0E10;
+
+ for ( Object temp = new byte[(byte)1.0E10]; true ;
+ var_2 = "1".equals("0") ? ((byte) vara_1) : 1 ) {}
+ }
+
+ static void testb() {
+ long var_1 = -1L;
+
+ short var_2 = (byte) 1.0E10;
+
+ for ( Object temp = new byte[(byte)1.0E10]; true ;
+ var_2 = "1".equals("0") ? ((byte) var_1) : 1 ) {}
+ }
+
+ static void testc() {
+ long var_1 = -1L;
+ if (vara_1 > 0) var_1 = 1L;
+
+ int var_2 = (byte)var_1 - 128;
+
+ for ( Object temp = new byte[var_2]; true ;
+ var_2 = "1".equals("0") ? 2 : 1 ) {}
+ }
+
+ static void testd() {
+ long var_1 = 0L;
+
+ int var_2 = (byte)var_1 + 1;
+ for (int i=0; i<2 ; i++) var_2 = var_2 - 1;
+
+ for ( Object temp = new byte[var_2]; true ;
+ var_2 = "1".equals("0") ? 2 : 1 ) {}
+ }
+
+ public static void main(String[] args) throws Exception {
+ int nex = 0;
+
+ try {
+ testa();
+ }
+ catch (java.lang.NegativeArraySizeException ex) { nex++; }
+ try {
+ testb();
+ }
+ catch (java.lang.NegativeArraySizeException ex) { nex++; }
+ try {
+ testc();
+ }
+ catch (java.lang.NegativeArraySizeException ex) { nex++; }
+ try {
+ testd();
+ }
+ catch (java.lang.NegativeArraySizeException ex) { nex++; }
+
+ if (nex != 4)
+ System.exit(97);
+ }
+}
+
--- a/jaxp/.hgtags Thu Apr 16 11:23:02 2009 -0700
+++ b/jaxp/.hgtags Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
69ad87dc25cbcaaaded4727199395ad0c78bc427 jdk7-b52
e8837366d3fd72f7c7a47ebfdbd5106c16156f12 jdk7-b53
946a9f0c493261fa6a010dc33e61b9b535ba80c1 jdk7-b54
+039945fba683ee6773a721e2bd4e449f6133769a jdk7-b55
--- a/jaxws/.hgtags Thu Apr 16 11:23:02 2009 -0700
+++ b/jaxws/.hgtags Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
e646890d18b770f625f14ed4ad5c50554d8d3d8b jdk7-b52
b250218eb2e534384667ec73e3713e684667fd4c jdk7-b53
50ea00dc5f143fe00025233e704903c37f8464aa jdk7-b54
+e0eebd978b830c09e7862cff3f77a914c15651c9 jdk7-b55
--- a/jdk/.hgtags Thu Apr 16 11:23:02 2009 -0700
+++ b/jdk/.hgtags Wed Jul 05 16:51:32 2017 +0200
@@ -29,3 +29,4 @@
bcbeadb4a5d759b29e876ee2c83401e91ff22f60 jdk7-b52
a2033addca678f9e4c0d92ffa1e389171cc9321d jdk7-b53
d1c43d1f5676a24ba86221ac7cad5694f3a9afda jdk7-b54
+522bb5aa17e0c0cff00b1ed7d1b51bc4db2cfef9 jdk7-b55
--- a/jdk/make/com/sun/Makefile Thu Apr 16 11:23:02 2009 -0700
+++ b/jdk/make/com/sun/Makefile Wed Jul 05 16:51:32 2017 +0200
@@ -41,7 +41,7 @@
# Omit mirror since it's built with the apt tool.
SUBDIRS = $(SCRIPT_SUBDIR) image security crypto/provider jndi jmx \
java inputmethods org xml rowset net/httpserver net/ssl demo \
- tools jarsigner tracing servicetag
+ tools jarsigner tracing servicetag nio
all build clean clobber::
$(SUBDIRS-loop)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/Makefile Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,38 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+
+#
+# Makefile for com.sun.nio packages.
+#
+
+BUILDDIR = ../../..
+include $(BUILDDIR)/common/Defs.gmk
+
+SUBDIRS = sctp
+all build clean clobber::
+ $(SUBDIRS-loop)
+
+clean clobber::
+ $(RM) -r $(CLASSDESTDIR)/com/sun/nio
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/Exportedfiles.gmk Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,39 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+
+#
+# These are the names of Java classes for which we will make .h files.
+#
+
+ifneq ($(PLATFORM), windows)
+FILES_export = \
+ sun/nio/ch/SctpAssocChange.java \
+ sun/nio/ch/SctpChannelImpl.java \
+ sun/nio/ch/SctpNet.java \
+ sun/nio/ch/SctpPeerAddrChange.java \
+ sun/nio/ch/SctpResultContainer.java \
+ sun/nio/ch/SctpServerChannelImpl.java \
+ sun/nio/ch/SctpStdSocketOption.java
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/FILES_c.gmk Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,31 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+
+ifneq ($(PLATFORM),windows)
+FILES_c = \
+ SctpNet.c \
+ SctpChannelImpl.c \
+ SctpServerChannelImpl.c
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/FILES_java.gmk Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,67 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+FILES_java = \
+ com/sun/nio/sctp/AbstractNotificationHandler.java \
+ com/sun/nio/sctp/Association.java \
+ com/sun/nio/sctp/AssociationChangeNotification.java \
+ com/sun/nio/sctp/HandlerResult.java \
+ com/sun/nio/sctp/IllegalReceiveException.java \
+ com/sun/nio/sctp/IllegalUnbindException.java \
+ com/sun/nio/sctp/InvalidStreamException.java \
+ com/sun/nio/sctp/MessageInfo.java \
+ com/sun/nio/sctp/Notification.java \
+ com/sun/nio/sctp/NotificationHandler.java \
+ com/sun/nio/sctp/PeerAddressChangeNotification.java \
+ com/sun/nio/sctp/SctpChannel.java \
+ com/sun/nio/sctp/SctpMultiChannel.java \
+ com/sun/nio/sctp/SctpServerChannel.java \
+ com/sun/nio/sctp/SctpSocketOption.java \
+ com/sun/nio/sctp/SctpStandardSocketOption.java \
+ com/sun/nio/sctp/SendFailedNotification.java \
+ com/sun/nio/sctp/ShutdownNotification.java \
+ \
+ sun/nio/ch/SctpMessageInfoImpl.java \
+ sun/nio/ch/SctpStdSocketOption.java
+
+ifneq ($(PLATFORM), windows)
+FILES_java += \
+ sun/nio/ch/SctpAssocChange.java \
+ sun/nio/ch/SctpAssociationImpl.java \
+ sun/nio/ch/SctpChannelImpl.java \
+ sun/nio/ch/SctpMultiChannelImpl.java \
+ sun/nio/ch/SctpNet.java \
+ sun/nio/ch/SctpNotification.java \
+ sun/nio/ch/SctpPeerAddrChange.java \
+ sun/nio/ch/SctpResultContainer.java \
+ sun/nio/ch/SctpSendFailed.java \
+ sun/nio/ch/SctpServerChannelImpl.java \
+ sun/nio/ch/SctpShutdown.java \
+ sun/nio/ch/SctpSocketDispatcher.java
+else
+FILES_java += \
+ sun/nio/ch/SctpChannelImpl.java \
+ sun/nio/ch/SctpMultiChannelImpl.java \
+ sun/nio/ch/SctpServerChannelImpl.java
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/Makefile Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,80 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+
+#
+# Makefile for com.sun.nio.sctp
+#
+
+BUILDDIR = ../../../..
+PACKAGE = com.sun.nio.sctp
+LIBRARY = sctp
+PRODUCT = sun
+#OTHER_JAVACFLAGS += -Xmaxwarns 1000 -Xlint
+include $(BUILDDIR)/common/Defs.gmk
+
+#
+# Files to compile
+#
+include FILES_c.gmk
+include FILES_java.gmk
+include Exportedfiles.gmk
+
+ifneq ($(PLATFORM), windows)
+include $(BUILDDIR)/common/Mapfile-vers.gmk
+include $(BUILDDIR)/common/Library.gmk
+
+#
+# Find platform-specific C source files
+#
+vpath %.c $(PLATFORM_SRC)/native/sun/nio/ch
+
+#
+# Include nio.h, net_util.h, sun_nio_ch_IOStatus.h, etc
+#
+OTHER_INCLUDES += \
+ -I$(SHARE_SRC)/native/sun/nio/ch \
+ -I$(SHARE_SRC)/native/java/net \
+ -I$(PLATFORM_SRC)/native/java/net \
+ -I$(CLASSHDRDIR)/../../../../java/java.nio/nio/CClassHeaders
+
+ifeq ($(PLATFORM), linux)
+COMPILER_WARNINGS_FATAL=true
+#OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl
+OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -lnio -lnet -lpthread -ldl
+endif
+ifeq ($(PLATFORM), solaris)
+#LIBSCTP = -lsctp
+OTHER_LDLIBS += $(LIBSOCKET) -L$(LIBDIR)/$(LIBARCH) -lnet -lnio
+endif # PLATFORM
+
+else # windows
+include $(BUILDDIR)/common/Classes.gmk
+endif # ifneq windows
+
+
+clean clobber::
+ $(RM) -r $(CLASSDESTDIR)/com/sun/nio/sctp
+ $(RM) -r $(CLASSDESTDIR)/sun/nio/ch
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/mapfile-vers Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,49 @@
+#
+# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. 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.
+#
+
+SUNWprivate_1.1 {
+ global:
+ Java_sun_nio_ch_SctpNet_socket0;
+ Java_sun_nio_ch_SctpNet_bindx;
+ Java_sun_nio_ch_SctpNet_getLocalAddresses0;
+ Java_sun_nio_ch_SctpNet_getRemoteAddresses0;
+ Java_sun_nio_ch_SctpNet_getPrimAddrOption0;
+ Java_sun_nio_ch_SctpNet_setPrimAddrOption0;
+ Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0;
+ Java_sun_nio_ch_SctpNet_getInitMsgOption0;
+ Java_sun_nio_ch_SctpNet_setInitMsgOption0;
+ Java_sun_nio_ch_SctpNet_getIntOption0;
+ Java_sun_nio_ch_SctpNet_setIntOption0;
+ Java_sun_nio_ch_SctpNet_shutdown0;
+ Java_sun_nio_ch_SctpChannelImpl_initIDs;
+ Java_sun_nio_ch_SctpChannelImpl_checkConnect;
+ Java_sun_nio_ch_SctpChannelImpl_receive0;
+ Java_sun_nio_ch_SctpChannelImpl_send0;
+ Java_sun_nio_ch_SctpServerChannelImpl_initIDs;
+ Java_sun_nio_ch_SctpServerChannelImpl_accept0;
+ JNI_OnLoad;
+ local:
+ *;
+};
--- a/jdk/make/docs/NON_CORE_PKGS.gmk Thu Apr 16 11:23:02 2009 -0700
+++ b/jdk/make/docs/NON_CORE_PKGS.gmk Wed Jul 05 16:51:32 2017 +0200
@@ -86,6 +86,8 @@
SMARTCARDIO_PKGS = javax.smartcardio
+SCTPAPI_PKGS = com.sun.nio.sctp
+
TRACING_PKGS = com.sun.tracing \
com.sun.tracing.dtrace
@@ -98,4 +100,6 @@
$(OLD_JSSE_PKGS) \
$(HTTPSERVER_PKGS) \
$(SMARTCARDIO_PKGS) \
- $(TRACING_PKGS)
+ $(TRACING_PKGS) \
+ $(SCTPAPI_PKGS)
+
--- a/jdk/make/java/nio/mapfile-linux Thu Apr 16 11:23:02 2009 -0700
+++ b/jdk/make/java/nio/mapfile-linux Wed Jul 05 16:51:32 2017 +0200
@@ -189,6 +189,7 @@
Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0;
Java_sun_nio_fs_UnixNativeDispatcher_getextmntent;
Java_sun_nio_fs_UnixCopyFile_transfer;
+ handleSocketError;
local:
*;
--- a/jdk/make/java/nio/mapfile-solaris Thu Apr 16 11:23:02 2009 -0700
+++ b/jdk/make/java/nio/mapfile-solaris Wed Jul 05 16:51:32 2017 +0200
@@ -175,6 +175,7 @@
Java_sun_nio_fs_SolarisWatchService_portDissociate;
Java_sun_nio_fs_SolarisWatchService_portSend;
Java_sun_nio_fs_SolarisWatchService_portGetn;
+ handleSocketError;
local:
*;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * A skeletal handler that consumes notifications and continues.
+ *
+ * <P> This class trivially implements the {@code handleNotification} methods to
+ * return {@link HandlerResult#CONTINUE CONTINUE} so that all notifications are
+ * consumed and the channel continues to try and receive a message.
+ *
+ * <P> It also provides overloaded versions of the {@code handleNotification}
+ * methods, one for each of the required supported notification types, {@link
+ * AssociationChangeNotification}, {@link PeerAddressChangeNotification},
+ * {@link SendFailedNotification}, and {@link ShutdownNotification}. The
+ * appropriate method will be invoked when the notification is received.
+ *
+ * @since 1.7
+ */
+public class AbstractNotificationHandler<T>
+ implements NotificationHandler<T>
+{
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AbstractNotificationHandler() {}
+
+ /**
+ * Invoked when an implementation specific notification is received from the
+ * SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the {@code receive} operation when it was
+ * initiated.
+ *
+ * @return The handler result
+ */
+ @Override
+ public HandlerResult handleNotification(Notification notification,
+ T attachment) {
+ return HandlerResult.CONTINUE;
+ }
+
+ /**
+ * Invoked when an {@link AssociationChangeNotification} is received from
+ * the SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the {@code receive} operation when it was
+ * initiated.
+ *
+ * @return The handler result
+ */
+ public HandlerResult handleNotification(AssociationChangeNotification notification,
+ T attachment) {
+ return HandlerResult.CONTINUE;
+ }
+
+ /**
+ * Invoked when an {@link PeerAddressChangeNotification} is received from
+ * the SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the {@code receive} operation when it was
+ * initiated.
+ *
+ * @return The handler result
+ */
+ public HandlerResult handleNotification(PeerAddressChangeNotification notification,
+ T attachment) {
+ return HandlerResult.CONTINUE;
+ }
+
+ /**
+ * Invoked when an {@link SendFailedNotification} is received from
+ * the SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the {@code receive} operation when it was
+ * initiated.
+ *
+ * @return The handler result
+ */
+ public HandlerResult handleNotification(SendFailedNotification notification,
+ T attachment) {
+ return HandlerResult.CONTINUE;
+ }
+
+ /**
+ * Invoked when an {@link ShutdownNotification} is received from
+ * the SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the {@code receive} operation when it was
+ * initiated.
+ *
+ * @return The handler result
+ */
+ public HandlerResult handleNotification(ShutdownNotification notification,
+ T attachment) {
+ return HandlerResult.CONTINUE;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/Association.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * A class that represents an SCTP association.
+ *
+ * <P> An association exists between two SCTP endpoints. Each endpoint is
+ * represented by a list of transport addresses through which that endpoint can
+ * be reached and from which it will originate SCTP messages. The association
+ * spans over all of the possible source/destination combinations which may be
+ * generated from each endpoint's lists of addresses.
+ *
+ * <P> Associations are identified by their Association ID.
+ * Association ID's are guaranteed to be unique for the lifetime of the
+ * association. An association ID may be reused after the association has been
+ * shutdown. An association ID is not unique across multiple SCTP channels.
+ * An Association's local and remote addresses may change if the SCTP
+ * implementation supports <I>Dynamic Address Reconfiguration</I> as defined by
+ * <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>, see the
+ * {@code bindAddress} and {@code unbindAddress} methods of {@link SctpChannel},
+ * {@link SctpServerChannel}, and {@link SctpMultiChannel}.
+ *
+ * <P> An {@code Association} is returned from an {@link
+ * SctpChannel#association SctpChannel} or an {@link
+ * SctpMultiChannel#associations SctpMultiChannel}, as well
+ * as being given as a parameter to {@link NotificationHandler
+ * NotificationHandler} methods.
+ *
+ * @since 1.7
+ */
+public class Association {
+ private final int associationID;
+ private final int maxInStreams;
+ private final int maxOutStreams;
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected Association(int associationID,
+ int maxInStreams,
+ int maxOutStreams) {
+ this.associationID = associationID;
+ this.maxInStreams = maxInStreams;
+ this.maxOutStreams = maxOutStreams;
+ }
+
+ /**
+ * Returns the associationID.
+ *
+ * @return The association ID
+ */
+ public final int associationID() {
+ return associationID;
+ };
+
+ /**
+ * Returns the maximum number of inbound streams that this association
+ * supports.
+ *
+ * <P> Data received on this association will be on stream number
+ * {@code s}, where {@code 0 <= s < maxInboundStreams()}.
+ *
+ * @return The maximum number of inbound streams
+ */
+ public final int maxInboundStreams() {
+ return maxInStreams;
+ };
+
+ /**
+ * Returns the maximum number of outbound streams that this association
+ * supports.
+ *
+ * <P> Data sent on this association must be on stream number
+ * {@code s}, where {@code 0 <= s < maxOutboundStreams()}.
+ *
+ * @return The maximum number of outbound streams
+ */
+ public final int maxOutboundStreams() {
+ return maxOutStreams;
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Notification emitted when an association has either opened or closed.
+ *
+ * @since 1.7
+ */
+public abstract class AssociationChangeNotification
+ implements Notification
+{
+ /**
+ * Defines the type of change event that happened to the association.
+ *
+ * @since 1.7
+ */
+ public enum AssocChangeEvent
+ {
+ /**
+ * A new association is now ready and data may be exchanged with this peer.
+ */
+ COMM_UP,
+
+ /**
+ * The association has failed. A series of SCTP send failed notifications
+ * will follow this notification, one for each outstanding message.
+ */
+ COMM_LOST,
+
+ /**
+ * SCTP has detected that the peer has restarted.
+ */
+ RESTART,
+
+ /**
+ * The association has gracefully closed.
+ */
+ SHUTDOWN,
+
+ /**
+ * The association failed to setup. If a message was sent on a {@link
+ * SctpMultiChannel} in non-blocking mode, an
+ * SCTP send failed notification will follow this notification for the
+ * outstanding message.
+ */
+ CANT_START
+ }
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AssociationChangeNotification() {}
+
+ /**
+ * Returns the association that this notification is applicable to.
+ *
+ * @return The association whose state has changed, or {@code null} if
+ * there is no association, that is {@linkplain
+ * AssocChangeEvent#CANT_START CANT_START}
+ */
+ public abstract Association association();
+
+ /**
+ * Returns the type of change event.
+ *
+ * @return The event
+ */
+ public abstract AssocChangeEvent event();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/HandlerResult.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Defines notification handler results.
+ *
+ * <P> The {@code HandlerResult} is used to determine the behavior of the
+ * channel after it handles a notification from the SCTP stack. Essentially its
+ * value determines if the channel should try to receive another notificaiton or
+ * a message before returning.
+ *
+ * @since 1.7
+ */
+public enum HandlerResult {
+ /**
+ * Try to receieve another message or notification.
+ */
+ CONTINUE,
+
+ /**
+ * Return without trying to receive any more data.
+ */
+ RETURN;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to invoke the
+ * {@code receive} method of {@link SctpChannel} or {@link SctpMultiChannel}
+ * from a notification handler.
+ *
+ * @since 1.7
+ */
+public class IllegalReceiveException extends IllegalStateException {
+ private static final long serialVersionUID = 2296619040988576224L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public IllegalReceiveException() { }
+
+ /**
+ * Constructs an instance of this class with the specified message.
+ */
+ public IllegalReceiveException(String msg) {
+ super(msg);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to remove an
+ * address that is not bound to the channel, or remove an address from a
+ * channel that has only one address bound to it.
+ *
+ * @since 1.7
+ */
+public class IllegalUnbindException extends IllegalStateException {
+ private static final long serialVersionUID = -310540883995532224L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public IllegalUnbindException() { }
+
+ /**
+ * Constructs an instance of this class with the specified detailed message.
+ */
+ public IllegalUnbindException(String msg) {
+ super(msg);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to send a
+ * message to an invalid stream.
+ *
+ * @since 1.7
+ */
+public class InvalidStreamException extends IllegalArgumentException {
+ private static final long serialVersionUID = -9172703378046665558L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public InvalidStreamException() { }
+
+ /**
+ * Constructs an instance of this class with the specified detailed message.
+ */
+ public InvalidStreamException(String msg) {
+ super(msg);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+
+/**
+ * The {@code MessageInfo} class provides additional ancillary information about
+ * messages.
+ *
+ * <P> Received SCTP messages, returned by
+ * {@link SctpChannel#receive SctpChannel.receive} and {@link
+ * SctpMultiChannel#receive SctpMultiChannel.receive},
+ * return a {@code MessageInfo} instance that can be queried to determine
+ * ancillary information about the received message. Messages being sent should
+ * use one of the {@link #createOutgoing(java.net.SocketAddress,int)
+ * createOutgoing} methods to provide ancillary data for the message being
+ * sent, and may use the appropriate setter methods to override the default
+ * values provided for {@link #isUnordered() unordered}, {@link #timeToLive()
+ * timeToLive}, {@link #isComplete() complete} and {@link #payloadProtocolID()
+ * payloadProtocolID}, before sending the message.
+ *
+ * <P> For out going messages the {@code timeToLive} parameter is a time period
+ * that the sending side SCTP stack may expire the message if it has not been
+ * sent. This time period is an indication to the stack that the message is no
+ * longer required to be sent after the time period expires. It is not a hard
+ * timeout and may be influenced by whether the association supports the partial
+ * reliability extension, <a href=http://www.ietf.org/rfc/rfc3758.txt>RFC 3758
+ * <a>
+ *
+ * <P> {@code MessageInfo} instances are not safe for use by multiple concurrent
+ * threads. If a MessageInfo is to be used by more than one thread then access
+ * to the MessageInfo should be controlled by appropriate synchronization.
+ *
+ * @since 1.7
+ */
+public abstract class MessageInfo {
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected MessageInfo() {}
+
+ /**
+ * Creates a {@code MessageInfo} instance suitable for use when
+ * sending a message.
+ *
+ * <P> The returned instance will have its {@link #isUnordered() unordered}
+ * value set to {@code false}, its {@link #timeToLive() timeToLive} value
+ * set to {@code 0}, its {@link #isComplete() complete} value set
+ * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
+ * value set to {@code 0}. These values, if required, can be set through
+ * the appropriate setter method before sending the message.
+ *
+ * @param address
+ * For a connected {@code SctpChannel} the address is the
+ * preferred peer address of the association to send the message
+ * to, or {@code null} to use the peer primary address. For an
+ * {@code SctpMultiChannel} the address is used to determine
+ * the association, or if no association exists with a peer of that
+ * address then one is setup.
+ *
+ * @param streamNumber
+ * The stream number that the message will be sent on
+ *
+ * @return The outgoing message info
+ *
+ * @throws IllegalArgumentException
+ * If the streamNumber is negative or greater than {@code 65536}
+ */
+ public static MessageInfo createOutgoing(SocketAddress address,
+ int streamNumber) {
+ if (streamNumber < 0 || streamNumber > 65536)
+ throw new IllegalArgumentException("Invalid stream number");
+
+ return new sun.nio.ch.SctpMessageInfoImpl(null, address, streamNumber);
+ }
+ /**
+ * Creates a {@code MessageInfo} instance suitable for use when
+ * sending a message to a given association. Typically used for
+ * {@code SctpMultiChannel} when an association has already been setup.
+ *
+ * <P> The returned instance will have its {@link #isUnordered() unordered}
+ * value set to {@code false}, its {@link #timeToLive() timeToLive} value
+ * set to {@code 0}, its {@link #isComplete() complete} value set
+ * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
+ * value set to {@code 0}. These values, if required, can be set through
+ * the appropriate setter method before sending the message.
+ *
+ * @param association
+ * The association to send the message on
+ *
+ * @param address
+ * The preferred peer address of the association to send the message
+ * to, or {@code null} to use the peer primary address
+ *
+ * @param streamNumber
+ * The stream number that the message will be sent on.
+ *
+ * @return The outgoing message info
+ *
+ * @throws IllegalArgumentException
+ * If {@code association} is {@code null}, or the streamNumber is
+ * negative or greater than {@code 65536}
+ */
+ public static MessageInfo createOutgoing(Association association,
+ SocketAddress address,
+ int streamNumber) {
+ if (association == null)
+ throw new IllegalArgumentException("association cannot be null");
+
+ if (streamNumber < 0 || streamNumber > 65536)
+ throw new IllegalArgumentException("Invalid stream number");
+
+ return new sun.nio.ch.SctpMessageInfoImpl(association, address,
+ streamNumber);
+ }
+
+ /**
+ * Returns the source socket address if the message has been received,
+ * otherwise the preferred destination of the message to be sent.
+ *
+ * @return The socket address, or {@code null} if this instance is to be
+ * used for sending a message and has been construced without
+ * specifying a preferred destination address
+ *
+ */
+ public abstract SocketAddress address();
+
+ /**
+ * Returns the association that the message was received on, if the message
+ * has been received, otherwise the association that the message is to be
+ * sent on.
+ *
+ * @return The association, or {@code null} if this instance is to be
+ * used for sending a message and has been construced using the
+ * the {@link #createOutgoing(SocketAddress,int)
+ * createOutgoing(SocketAddress,int)} static factory method
+ */
+ public abstract Association association();
+
+ /**
+ * Returns the number of bytes read for the received message.
+ *
+ * <P> This method is only appicable for received messages, it has no
+ * meaning for messages being sent.
+ *
+ * @return The number of bytes read, {@code -1} if the channel is an {@link
+ * SctpChannel} that has reached end-of-stream, otherwise
+ * {@code 0}
+ */
+ public abstract int bytes();
+
+ /**
+ * Tells whether or not the message is complete.
+ *
+ * <P> For received messages {@code true} indicates that the message was
+ * completely received. For messages being sent {@code true} indicates that
+ * the message is complete, {@code false} indicates that the message is not
+ * complete. How the send channel interprets this value depends on the value
+ * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ * SCTP_EXPLICIT_COMPLETE} socket option.
+ *
+ * @return {@code true} if, and only if, the message is complete
+ */
+ public abstract boolean isComplete();
+
+ /**
+ * Sets whether or not the message is complete.
+ *
+ * <P> For messages being sent {@code true} indicates that
+ * the message is complete, {@code false} indicates that the message is not
+ * complete. How the send channel interprets this value depends on the value
+ * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ * SCTP_EXPLICIT_COMPLETE} socket option.
+ *
+ * @param complete
+ * {@code true} if, and only if, the message is complete
+ *
+ * @return This MessageInfo
+ *
+ * @see MessageInfo#isComplete()
+ */
+ public abstract MessageInfo complete(boolean complete);
+
+ /**
+ * Tells whether or not the message is unordered. For received messages
+ * {@code true} indicates that the message was sent non-ordered. For
+ * messages being sent {@code true} requests the un-ordered delivery of the
+ * message, {@code false} indicates that the message is ordered.
+ *
+ * @return {@code true} if the message is unordered, otherwise
+ * {@code false}
+ */
+ public abstract boolean isUnordered();
+
+ /**
+ * Sets whether or not the message is unordered.
+ *
+ * @param unordered
+ * {@code true} requests the un-ordered delivery of the message,
+ * {@code false} indicates that the message is ordered.
+ *
+ * @return This MessageInfo
+ *
+ * @see MessageInfo#isUnordered()
+ */
+ public abstract MessageInfo unordered(boolean unordered);
+
+ /**
+ * Returns the payload protocol Identifier.
+ *
+ * <P> A value indicating the type of payload protocol data being
+ * transmitted/received. This value is passed as opaque data by SCTP.
+ * {@code 0} indicates an unspecified payload protocol identifier.
+ *
+ * @return The Payload Protocol Identifier
+ */
+ public abstract int payloadProtocolID();
+
+ /**
+ * Sets the payload protocol Identifier.
+ *
+ * <P> A value indicating the type of payload protocol data being
+ * transmitted. This value is passed as opaque data by SCTP.
+ *
+ * @param ppid
+ * The Payload Protocol Identifier, or {@code 0} indicate an
+ * unspecified payload protocol identifier.
+ *
+ * @return This MessageInfo
+ *
+ * @see MessageInfo#payloadProtocolID()
+ */
+ public abstract MessageInfo payloadProtocolID(int ppid);
+
+ /**
+ * Returns the stream number that the message was received on, if the
+ * message has been received, otherwise the stream number that the message
+ * is to be sent on.
+ *
+ * @return The stream number
+ */
+ public abstract int streamNumber();
+
+ /**
+ * Sets the stream number that the message is to be sent on.
+ *
+ * @param streamNumber
+ * The stream number
+ *
+ * @throws IllegalArgumentException
+ * If the streamNumber is negative or greater than {@code 65536}
+ *
+ * @return This MessageInfo
+ */
+ public abstract MessageInfo streamNumber(int streamNumber);
+
+ /**
+ * The time period that the sending side may expire the message if it has
+ * not been sent, or {@code 0} to indicate that no timeout should occur. This
+ * value is only applicable for messages being sent, it has no meaning for
+ * received messages.
+ *
+ * @return The time period in milliseconds, or {@code 0}
+ */
+ public abstract long timeToLive();
+
+ /**
+ * Sets the time period that the sending side may expire the message if it
+ * has not been sent.
+ *
+ * @param millis
+ * The time period in milliseconds, or {@code 0} to indicate that no
+ * timeout should occur
+ *
+ * @return This MessageInfo
+ *
+ * @see MessageInfo#timeToLive()
+ */
+ public abstract MessageInfo timeToLive(long millis);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/Notification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * A notification from the SCTP stack.
+ *
+ * <P> Objects of this type are passed to the {@link NotificationHandler} when
+ * a notification is received.
+ *
+ * <P> An SCTP channel supports the following notifications: {@link
+ * AssociationChangeNotification}, {@link PeerAddressChangeNotification},
+ * {@link SendFailedNotification}, {@link ShutdownNotification}, and may support
+ * additional implementation specific notifications.
+ *
+ * @since 1.7
+ */
+public interface Notification {
+ /**
+ * Returns the association that this notification is applicable to.
+ */
+ public Association association();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/NotificationHandler.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * A handler for consuming notifications from the SCTP stack.
+ *
+ * <P> The SCTP channels defined in this package allow a notification handler to
+ * be specified to consume notifications from the SCTP stack. When a
+ * notification is received the {@linkplain #handleNotification
+ * handleNotification} method of the handler is invoked to handle that
+ * notification.
+ *
+ * <P> Additionally, an attachment object can be attached to the {@code receive}
+ * operation to provide context when consuming the notification. The
+ * attachment is important for cases where a <i>state-less</i> {@code
+ * NotificationHandler} is used to consume the result of many {@code receive}
+ * operations.
+ *
+ * <P> Handler implementations are encouraged to extend the {@link
+ * AbstractNotificationHandler} class which implements this interface and
+ * provide notification specific methods. However, an API should generally use
+ * this handler interface as the type for parameters, return type, etc. rather
+ * than the abstract class.
+ *
+ * @param T The type of the object attached to the receive operation
+ *
+ * @since 1.7
+ */
+public interface NotificationHandler<T> {
+ /**
+ * Invoked when a notification is received from the SCTP stack.
+ *
+ * @param notification
+ * The notification
+ *
+ * @param attachment
+ * The object attached to the receive operation when it was initiated.
+ *
+ * @return The handler result
+ */
+ HandlerResult handleNotification(Notification notification, T attachment);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+
+/**
+ * Notification emitted when a destination address on a multi-homed peer
+ * encounters a change.
+ *
+ * @since 1.7
+ */
+public abstract class PeerAddressChangeNotification
+ implements Notification
+{
+ /**
+ * Defines the type of address change event that occurred to the destination
+ * address on a multi-homed peer when it encounters a change of interface
+ * details.
+ *
+ * <P> Some of these events types are only generated when the association
+ * supports dynamic address reconfiguration, e.g. {@code SCTP_ADDR_ADDED},
+ * {@code SCTP_ADDR_REMOVED}, etc.
+ *
+ * @since 1.7
+ */
+ public enum AddressChangeEvent {
+ /**
+ * This address is now reachable.
+ */
+ ADDR_AVAILABLE,
+
+ /**
+ * The address specified can no longer be reached. Any data sent to this
+ * address is rerouted to an alternate until this address becomes reachable.
+ */
+ ADDR_UNREACHABLE,
+
+ /**
+ * The address is no longer part of the association.
+ */
+ ADDR_REMOVED,
+
+ /**
+ * The address is now part of the association.
+ */
+ ADDR_ADDED,
+
+ /**
+ * This address has now been made to be the primary destination address.
+ */
+ ADDR_MADE_PRIMARY,
+
+ /**
+ * This address has now been confirmed as a valid address.
+ */
+ ADDR_CONFIRMED;
+ }
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected PeerAddressChangeNotification() {}
+
+ /**
+ * Returns the peer address.
+ *
+ * @return The peer address
+ */
+ public abstract SocketAddress address();
+
+ /**
+ * Returns the association that this notification is applicable to.
+ *
+ * @return The association whose peer address changed
+ */
+ public abstract Association association();
+
+ /**
+ * Returns the type of change event.
+ *
+ * @return The event
+ */
+ public abstract AddressChangeEvent event();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,859 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+
+/**
+ * A selectable channel for message-oriented connected SCTP sockets.
+ *
+ * <P> An SCTP channel can only control one SCTP association.
+ * An {@code SCTPChannel} is created by invoking one of the
+ * {@link #open open} methods of this class. A newly-created channel is open but
+ * not yet connected, that is, there is no association setup with a remote peer.
+ * An attempt to invoke an I/O operation upon an unconnected
+ * channel will cause a {@link java.nio.channels.NotYetConnectedException} to be
+ * thrown. An association can be setup by connecting the channel using one of
+ * its {@link #connect connect} methods. Once connected, the channel remains
+ * connected until it is closed. Whether or not a channel is connected may be
+ * determined by invoking {@link #getRemoteAddresses getRemoteAddresses}.
+ *
+ * <p> SCTP channels support <i>non-blocking connection:</i> A
+ * channel may be created and the process of establishing the link to
+ * the remote socket may be initiated via the {@link #connect connect} method
+ * for later completion by the {@link #finishConnect finishConnect} method.
+ * Whether or not a connection operation is in progress may be determined by
+ * invoking the {@link #isConnectionPending isConnectionPending} method.
+ *
+ * <p> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object) setOption} method. An SCTP
+ * channel support the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
+ * SCTP_DISABLE_FRAGMENTS} </td>
+ * <td> Enables or disables message fragmentation </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ * SCTP_EXPLICIT_COMPLETE} </td>
+ * <td> Enables or disables explicit message completion </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ * SCTP_FRAGMENT_INTERLEAVE} </td>
+ * <td> Controls how the presentation of messages occur for the message
+ * receiver </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ * SCTP_INIT_MAXSTREAMS} </td>
+ * <td> The maximum number of streams requested by the local endpoint during
+ * association initialization </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
+ * <td> Enables or disable a Nagle-like algorithm </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
+ * SCTP_PRIMARY_ADDR} </td>
+ * <td> Requests that the local SCTP stack use the given peer address as the
+ * association primary </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
+ * SCTP_SET_PEER_PRIMARY_ADDR} </td>
+ * <td> Requests that the peer mark the enclosed address as the association
+ * primary </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_SNDBUF
+ * SO_SNDBUF} </td>
+ * <td> The size of the socket send buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_RCVBUF
+ * SO_RCVBUF} </td>
+ * <td> The size of the socket receive buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_LINGER
+ * SO_LINGER} </td>
+ * <td> Linger on close if data is present (when configured in blocking mode
+ * only) </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions} method.
+ *
+ * <p> SCTP channels are safe for use by multiple concurrent threads.
+ * They support concurrent reading and writing, though at most one thread may be
+ * reading and at most one thread may be writing at any given time. The
+ * {@link #connect connect} and {@link #finishConnect
+ * finishConnect} methods are mutually synchronized against each other, and
+ * an attempt to initiate a send or receive operation while an invocation of one
+ * of these methods is in progress will block until that invocation is complete.
+ *
+ * @since 1.7
+ */
+public abstract class SctpChannel
+ extends AbstractSelectableChannel
+{
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @param provider
+ * The selector provider for this channel
+ */
+ protected SctpChannel(SelectorProvider provider) {
+ super(provider);
+ }
+
+ /**
+ * Opens an SCTP channel.
+ *
+ * <P> The new channel is unbound and unconnected.
+ *
+ * @return A new SCTP channel
+ *
+ * @throws UnsupportedOperationException
+ * If the SCTP protocol is not supported
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static SctpChannel open() throws
+ IOException {
+ return new sun.nio.ch.SctpChannelImpl((SelectorProvider)null);
+ }
+
+ /**
+ * Opens an SCTP channel and connects it to a remote address.
+ *
+ * <P> This is a convenience method and is equivalent to evaluating the
+ * following expression:
+ * <blockquote><pre>
+ * open().connect(remote, maxOutStreams, maxInStreams);
+ * </pre></blockquote>
+ *
+ * @param remote
+ * The remote address to which the new channel is to be connected
+ *
+ * @param maxOutStreams
+ * The number of streams that the application wishes to be able
+ * to send to. Must be non negative and no larger than {@code 65536}.
+ * {@code 0} to use the endpoints default value.
+ *
+ * @param maxInStreams
+ * The maximum number of inbound streams the application is prepared
+ * to support. Must be non negative and no larger than {@code 65536}.
+ * {@code 0} to use the endpoints default value.
+ *
+ * @return A new SCTP channel connected to the given address
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the connect operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the connect operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote peer
+ *
+ * @throws UnsupportedOperationException
+ * If the SCTP protocol is not supported
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public static SctpChannel open(SocketAddress remote, int maxOutStreams,
+ int maxInStreams) throws IOException {
+ SctpChannel ssc = SctpChannel.open();
+ ssc.connect(remote, maxOutStreams, maxInStreams);
+ return ssc;
+ }
+
+ /**
+ * Returns the association on this channel's socket.
+ *
+ * @return the association, or {@code null} if the channel's socket is not
+ * connected.
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract Association association() throws IOException;
+
+ /**
+ * Binds the channel's socket to a local address.
+ *
+ * <P> This method is used to establish a relationship between the socket
+ * and the local addresses. Once a relationship is established then
+ * the socket remains bound until the channel is closed. This relationship
+ * may not necesssarily be with the address {@code local} as it may be removed
+ * by {@link #unbindAddress unbindAddress}, but there will always be at least
+ * one local address bound to the channel's socket once an invocation of
+ * this method successfully completes.
+ *
+ * <P> Once the channel's socket has been successfully bound to a specific
+ * address, that is not automatically assigned, more addresses
+ * may be bound to it using {@link #bindAddress bindAddress}, or removed
+ * using {@link #unbindAddress unbindAddress}.
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to
+ * bind the socket to an automatically assigned socket address
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.AlreadyConnectedException
+ * If this channel is already connected
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.ConnectionPendingException
+ * If a non-blocking connection operation is already in progress on this channel
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel bind(SocketAddress local)
+ throws IOException;
+
+ /**
+ * Adds the given address to the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise {@link
+ * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
+ * method takes a {@code SocketAddress} as its argument which typically
+ * contains a port number as well as an address. Addresses subquently bound
+ * using this method are simply addresses as the SCTP port number remains
+ * the same for the lifetime of the channel.
+ *
+ * <P> Adding addresses to a connected association is optional functionality.
+ * If the endpoint supports dynamic address reconfiguration then it may
+ * send the appropriate message to the peer to change the peers address
+ * lists.
+ *
+ * @param address
+ * The address to add to the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.ConnectionPendingException
+ * If a non-blocking connection operation is already in progress on
+ * this channel
+ *
+ * @throws java.nio.channels.NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound to the given address
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel bindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Removes the given address from the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise {@link java.nio.channels.NotYetBoundException}
+ * is thrown. If this method is invoked on a channel that does not have
+ * {@code address} as one of its bound addresses or that has only one
+ * local address bound to it, then this method throws
+ * {@link IllegalUnbindException}.
+ * The initial address that the channel's socket is bound to using {@link
+ * #bind bind} may be removed from the bound addresses for the channel's socket.
+ *
+ * <P> Removing addresses from a connected association is optional
+ * functionality. If the endpoint supports dynamic address reconfiguration
+ * then it may send the appropriate message to the peer to change the peers
+ * address lists.
+ *
+ * @param address
+ * The address to remove from the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.ConnectionPendingException
+ * If a non-blocking connection operation is already in progress on
+ * this channel
+ *
+ * @throws java.nio.channels.NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IllegalUnbindException
+ * If {@code address} is not bound to the channel's socket. or
+ * the channel has only one address bound to it
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel unbindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Connects this channel's socket.
+ *
+ * <P> If this channel is in non-blocking mode then an invocation of this
+ * method initiates a non-blocking connection operation. If the connection
+ * is established immediately, as can happen with a local connection, then
+ * this method returns {@code true}. Otherwise this method returns
+ * {@code false} and the connection operation must later be completed by
+ * invoking the {@link #finishConnect finishConnect} method.
+ *
+ * <P> If this channel is in blocking mode then an invocation of this
+ * method will block until the connection is established or an I/O error
+ * occurs.
+ *
+ * <P> If a security manager has been installed then this method verifies
+ * that its {@link java.lang.SecurityManager#checkConnect checkConnect}
+ * method permits connecting to the address and port number of the given
+ * remote peer.
+ *
+ * <p> This method may be invoked at any time. If a {@link #send send} or
+ * {@link #receive receive} operation upon this channel is invoked while an
+ * invocation of this method is in progress then that operation will first
+ * block until this invocation is complete. If a connection attempt is
+ * initiated but fails, that is, if an invocation of this method throws a
+ * checked exception, then the channel will be closed.
+ *
+ * @param remote
+ * The remote peer to which this channel is to be connected
+ *
+ * @return {@code true} if a connection was established, {@code false} if
+ * this channel is in non-blocking mode and the connection
+ * operation is in progress
+ *
+ * @throws java.nio.channels.AlreadyConnectedException
+ * If this channel is already connected
+ *
+ * @throws java.nio.channels.ConnectionPendingException
+ * If a non-blocking connection operation is already in progress on
+ * this channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the connect operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the connect operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote peer
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract boolean connect(SocketAddress remote) throws IOException;
+
+ /**
+ * Connects this channel's socket.
+ *
+ * <P> This is a convience method and is equivalent to evaluating the
+ * following expression:
+ * <blockquote><pre>
+ * setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
+ * .connect(remote);
+ * </pre></blockquote>
+ *
+ * <P> The {@code maxOutStreams} and {@code maxInStreams} parameters
+ * represent the maximum number of streams that the application wishes to be
+ * able to send to and receive from. They are negotiated with the remote
+ * peer and may be limited by the operating system.
+ *
+ * @param remote
+ * The remote peer to which this channel is to be connected
+ *
+ * @param maxOutStreams
+ * Must be non negative and no larger than {@code 65536}.
+ * {@code 0} to use the endpoints default value.
+ *
+ * @param maxInStreams
+ * Must be non negative and no larger than {@code 65536}.
+ * {@code 0} to use the endpoints default value.
+ *
+ * @return {@code true} if a connection was established, {@code false} if
+ * this channel is in non-blocking mode and the connection operation
+ * is in progress
+ *
+ * @throws java.nio.channels.AlreadyConnectedException
+ * If this channel is already connected
+ *
+ * @throws java.nio.channels.ConnectionPendingException
+ * If a non-blocking connection operation is already in progress on
+ * this channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the connect operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the connect operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote peer
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract boolean connect(SocketAddress remote,
+ int maxOutStreams,
+ int maxInStreams)
+ throws IOException;
+
+ /**
+ * Tells whether or not a connection operation is in progress on this channel.
+ *
+ * @return {@code true} if, and only if, a connection operation has been initiated
+ * on this channel but not yet completed by invoking the
+ * {@link #finishConnect} method
+ */
+ public abstract boolean isConnectionPending();
+
+ /**
+ * Finishes the process of connecting an SCTP channel.
+ *
+ * <P> A non-blocking connection operation is initiated by placing a socket
+ * channel in non-blocking mode and then invoking one of its {@link #connect
+ * connect} methods. Once the connection is established, or the attempt has
+ * failed, the channel will become connectable and this method may
+ * be invoked to complete the connection sequence. If the connection
+ * operation failed then invoking this method will cause an appropriate
+ * {@link java.io.IOException} to be thrown.
+ *
+ * <P> If this channel is already connected then this method will not block
+ * and will immediately return <tt>true</tt>. If this channel is in
+ * non-blocking mode then this method will return <tt>false</tt> if the
+ * connection process is not yet complete. If this channel is in blocking
+ * mode then this method will block until the connection either completes
+ * or fails, and will always either return <tt>true</tt> or throw a checked
+ * exception describing the failure.
+ *
+ * <P> This method may be invoked at any time. If a {@link #send send} or {@link #receive receive}
+ * operation upon this channel is invoked while an invocation of this
+ * method is in progress then that operation will first block until this
+ * invocation is complete. If a connection attempt fails, that is, if an
+ * invocation of this method throws a checked exception, then the channel
+ * will be closed.
+ *
+ * @return {@code true} if, and only if, this channel's socket is now
+ * connected
+ *
+ * @throws java.nio.channels.NoConnectionPendingException
+ * If this channel is not connected and a connection operation
+ * has not been initiated
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the connect operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the connect operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract boolean finishConnect() throws IOException;
+
+ /**
+ * Returns all of the socket addresses to which this channel's socket is
+ * bound.
+ *
+ * @return All the socket addresses that this channel's socket is
+ * bound to, or an empty {@code Set} if the channel's socket is not
+ * bound
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract Set<SocketAddress> getAllLocalAddresses()
+ throws IOException;
+
+ /**
+ * Returns all of the remote addresses to which this channel's socket
+ * is connected.
+ *
+ * <P> If the channel is connected to a remote peer that is bound to
+ * multiple addresses then it is these addresses that the channel's socket
+ * is connected.
+ *
+ * @return All of the remote addresses to which this channel's socket
+ * is connected, or an empty {@code Set} if the channel's socket is
+ * not connected
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract Set<SocketAddress> getRemoteAddresses()
+ throws IOException;
+
+ /**
+ * Shutdown a connection without closing the channel.
+ *
+ * <P> Sends a shutdown command to the remote peer, effectively preventing
+ * any new data from being written to the socket by either peer. Further
+ * sends will throw {@link java.nio.channels.ClosedChannelException}. The
+ * channel remains open to allow the for any data (and notifications) to be
+ * received that may have been sent by the peer before it received the
+ * shutdown command. If the channel is already shutdown then invoking this
+ * method has no effect.
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.NotYetConnectedException
+ * If this channel is not yet connected
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel shutdown() throws IOException;
+
+ /**
+ * Returns the value of a socket option.
+ *
+ * @param name
+ * The socket option
+ *
+ * @return The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> T getOption(SctpSocketOption<T> name)
+ throws IOException;
+
+ /**
+ * Sets the value of a socket option.
+ *
+ * @param name
+ * The socket option
+ *
+ * @param value
+ * The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @return This channel
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws IllegalArgumentException
+ * If the value is not a valid value for this socket option
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+ throws IOException;
+
+ /**
+ * Returns a set of the socket options supported by this channel.
+ *
+ * <P> This method will continue to return the set of options even after the
+ * channel has been closed.
+ *
+ * @return A set of the socket options supported by this channel
+ */
+ public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+ /**
+ * Returns an operation set identifying this channel's supported operations.
+ *
+ * <P> SCTP channels support connecting, reading, and writing, so this
+ * method returns <tt>(</tt>{@link SelectionKey#OP_CONNECT}
+ * <tt>|</tt> {@link SelectionKey#OP_READ} <tt>|</tt> {@link
+ * SelectionKey#OP_WRITE}<tt>)</tt>. </p>
+ *
+ * @return The valid-operation set
+ */
+ @Override
+ public final int validOps() {
+ return (SelectionKey.OP_READ |
+ SelectionKey.OP_WRITE |
+ SelectionKey.OP_CONNECT);
+ }
+
+ /**
+ * Receives a message into the given buffer and/or handles a notification.
+ *
+ * <P> If a message or notification is immediately available, or if this
+ * channel is in blocking mode and one eventually becomes available, then
+ * the message or notification is returned or handled, respectively. If this
+ * channel is in non-blocking mode and a message or notification is not
+ * immediately available then this method immediately returns {@code null}.
+ *
+ * <P> If this method receives a message it is copied into the given byte
+ * buffer. The message is transferred into the given byte buffer starting at
+ * its current position and the buffers position is incremented by the
+ * number of bytes read. If there are fewer bytes remaining in the buffer
+ * than are required to hold the message, or the underlying input buffer
+ * does not contain the complete message, then an invocation of {@link
+ * MessageInfo#isComplete isComplete} on the returned {@code
+ * MessageInfo} will return {@code false}, and more invocations of this
+ * method will be necessary to completely consume the messgae. Only
+ * one message at a time will be partially delivered in any stream. The
+ * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
+ * messages occurs.
+ *
+ * <P> If this method receives a notification then the appropriate method of
+ * the given handler, if there is one, is invoked. If the handler returns
+ * {@link HandlerResult#CONTINUE CONTINUE} then this method will try to
+ * receive another message/notification, otherwise, if {@link
+ * HandlerResult#RETURN RETURN} is returned this method will return {@code
+ * null}. If an uncaught exception is thrown by the handler it will be
+ * propagated up the stack through this method.
+ *
+ * <P> This method may be invoked at any time. If another thread has
+ * already initiated a receive operation upon this channel, then an
+ * invocation of this method will block until the first operation is
+ * complete. The given handler is invoked without holding any locks used
+ * to enforce the above synchronization policy, that way handlers
+ * will not stall other threads from receiving. A handler should not invoke
+ * the {@code receive} method of this channel, if it does an
+ * {@link IllegalReceiveException} will be thrown.
+ *
+ * @param dst
+ * The buffer into which message bytes are to be transferred
+ *
+ * @param attachment
+ * The object to attach to the receive operation; can be
+ * {@code null}
+ *
+ * @param handler
+ * A handler to handle notifications from the SCTP stack, or {@code
+ * null} to ignore any notifications.
+ *
+ * @return The {@code MessageInfo}, {@code null} if this channel is in
+ * non-blocking mode and no messages are immediately available or
+ * the notification handler returns {@link HandlerResult#RETURN
+ * RETURN} after handling a notification
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the read operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the read operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.NotYetConnectedException
+ * If this channel is not yet connected
+ *
+ * @throws IllegalReceiveException
+ * If the given handler invokes the {@code receive} method of this
+ * channel
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract <T> MessageInfo receive(ByteBuffer dst,
+ T attachment,
+ NotificationHandler<T> handler)
+ throws IOException;
+
+ /**
+ * Sends a message via this channel.
+ *
+ * <P> If this channel is in non-blocking mode and there is sufficient room
+ * in the underlying output buffer, or if this channel is in blocking mode
+ * and sufficient room becomes available, then the remaining bytes in the
+ * given byte buffer are transmitted as a single message. Sending a message
+ * is atomic unless explicit message completion {@link
+ * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
+ * socket option is enabled on this channel's socket.
+ *
+ * <P> The message is transferred from the byte buffer as if by a regular
+ * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
+ * write} operation.
+ *
+ * <P> The bytes will be written to the stream number that is specified by
+ * {@link MessageInfo#streamNumber streamNumber} in the given {@code
+ * messageInfo}.
+ *
+ * <P> This method may be invoked at any time. If another thread has already
+ * initiated a send operation upon this channel, then an invocation of
+ * this method will block until the first operation is complete.
+ *
+ * @param src
+ * The buffer containing the message to be sent
+ *
+ * @param messageInfo
+ * Ancillary data about the message to be sent
+ *
+ * @return The number of bytes sent, which will be either the number of
+ * bytes that were remaining in the messages buffer when this method
+ * was invoked or, if this channel is non-blocking, may be zero if
+ * there was insufficient room for the message in the underlying
+ * output buffer
+ *
+ * @throws InvalidStreamExcepton
+ * If {@code streamNumner} is negative or greater than or equal to
+ * the maximum number of outgoing streams
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the read operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the read operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.NotYetConnectedException
+ * If this channel is not yet connected
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract int send(ByteBuffer src, MessageInfo messageInfo)
+ throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.SelectionKey;
+
+/**
+ * A selectable channel for message-oriented SCTP sockets.
+ *
+ * <P> An SCTP multi channel supports many associations on a single socket.
+ * An {@code SctpMultiChannel} is created by invoking the
+ * {@link #open open} method of this class. A newly-created channel is open but
+ * not yet bound. An attempt to invoke the {@link #receive receive} method of an
+ * unbound channel will cause the {@link NotYetBoundException}
+ * to be thrown. An attempt to invoke the {@link #send send} method of an
+ * unbound channel will cause it to first invoke the {@link #bind bind} method.
+ * The address(es) that the channel's socket is bound to can be retrieved by
+ * calling {@link #getAllLocalAddresses getAllLocalAddresses}.
+ *
+ * <P> Messages may be sent and received without explicitly setting up an
+ * association with the remote peer. The channel will implicitly setup
+ * a new association whenever it sends or receives a message from a remote
+ * peer if there is not already an association with that peer. Upon successful
+ * association setup, an {@link AssociationChangeNotification
+ * association changed} notification will be put to the SCTP stack with its
+ * {@code event} parameter set to {@link
+ * AssociationChangeNotification.AssocChangeEvent#COMM_UP
+ * COMM_UP}. This notification can be received by invoking {@link #receive
+ * receive}.
+ *
+ * <P> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object,Association) setOption} method. An
+ * {@code SctpMultiChannel} supports the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
+ * SCTP_DISABLE_FRAGMENTS} </td>
+ * <td> Enables or disables message fragmentation </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ * SCTP_EXPLICIT_COMPLETE} </td>
+ * <td> Enables or disables explicit message completion </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ * SCTP_FRAGMENT_INTERLEAVE} </td>
+ * <td> Controls how the presentation of messages occur for the message
+ * receiver </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ * SCTP_INIT_MAXSTREAMS} </td>
+ * <td> The maximum number of streams requested by the local endpoint during
+ * association initialization </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
+ * <td> Enables or disable a Nagle-like algorithm </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
+ * SCTP_PRIMARY_ADDR} </td>
+ * <td> Requests that the local SCTP stack use the given peer address as the
+ * association primary </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
+ * SCTP_SET_PEER_PRIMARY_ADDR} </td>
+ * <td> Requests that the peer mark the enclosed address as the association
+ * primary </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_SNDBUF
+ * SO_SNDBUF} </td>
+ * <td> The size of the socket send buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_RCVBUF
+ * SO_RCVBUF} </td>
+ * <td> The size of the socket receive buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SO_LINGER
+ * SO_LINGER} </td>
+ * <td> Linger on close if data is present (when configured in blocking mode
+ * only) </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions} method.
+ *
+ * <p> SCTP multi channels are safe for use by multiple concurrent threads.
+ * They support concurrent sending and receiving, though at most one thread may be
+ * sending and at most one thread may be receiving at any given time.
+ *
+ * @since 1.7
+ */
+public abstract class SctpMultiChannel
+ extends AbstractSelectableChannel
+{
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @param provider
+ * The selector provider for this channel
+ */
+ protected SctpMultiChannel(SelectorProvider provider) {
+ super(provider);
+ }
+
+ /**
+ * Opens an SCTP multi channel.
+ *
+ * <P> The new channel is unbound.
+ *
+ * @return A new SCTP multi channel
+ *
+ * @throws UnsupportedOperationException
+ * If the SCTP protocol is not supported
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static SctpMultiChannel open() throws
+ IOException {
+ return new sun.nio.ch.SctpMultiChannelImpl((SelectorProvider)null);
+ }
+
+ /**
+ * Returns the open associations on this channel's socket.
+ *
+ * <P> Only associations whose {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP
+ * COMM_UP} association change event has been received are included
+ * in the returned set of associations. Associations for which a
+ * {@link AssociationChangeNotification.AssocChangeEvent#COMM_LOST COMM_LOST} or {@link
+ * AssociationChangeNotification.AssocChangeEvent#SHUTDOWN SHUTDOWN} association change
+ * event have been receive are removed from the set of associations.
+ *
+ * <P> The returned set of associations is a snapshot of the open
+ * associations at the time that this method is invoked.
+ *
+ * @return A {@code Set} containing the open associations, or an empty
+ * {@code Set} if there are none.
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract Set<Association> associations()
+ throws IOException;
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket
+ * to listen for connections.
+ *
+ * <P> This method is used to establish a relationship between the socket
+ * and the local address. Once a relationship is established then
+ * the socket remains bound until the channel is closed. This relationship
+ * may not necesssarily be with the address {@code local} as it may be removed
+ * by {@link #unbindAddress unbindAddress}, but there will always be at least one local
+ * address bound to the channel's socket once an invocation of this method
+ * successfully completes.
+ *
+ * <P> Once the channel's socket has been successfully bound to a specific
+ * address, that is not automatically assigned, more addresses
+ * may be bound to it using {@link #bindAddress bindAddress}, or removed
+ * using {@link #unbindAddress unbindAddress}.
+ *
+ * <P> The backlog parameter is the maximum number of pending connections on
+ * the socket. Its exact semantics are implementation specific. An implementation
+ * may impose an implementation specific maximum length or may choose to ignore
+ * the parameter. If the backlog parameter has the value {@code 0}, or a negative
+ * value, then an implementation specific default is used.
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to
+ * bind the socket to an automatically assigned socket address
+ *
+ * @param backlog
+ * The maximum number number of pending connections
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * java.lang.SecurityManager#checkListen(int) checkListen} method
+ * denies the operation
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpMultiChannel bind(SocketAddress local,
+ int backlog)
+ throws IOException;
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket
+ * to listen for connections.
+ *
+ * <P> This method works as if invoking it were equivalent to evaluating the
+ * expression:
+ * <blockquote><pre>
+ * bind(local, 0);
+ * </pre></blockquote>
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to
+ * bind the socket to an automatically assigned socket address
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * java.lang.SecurityManager#checkListen(int) checkListen} method
+ * denies the operation
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public final SctpMultiChannel bind(SocketAddress local)
+ throws IOException {
+ return bind(local, 0);
+ }
+
+ /**
+ * Adds the given address to the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise {@link NotYetBoundException} is thrown.
+ * The {@link #bind bind} method takes a {@code SocketAddress} as its
+ * argument which typically contains a port number as well as an address.
+ * Addresses subquently bound using this method are simply addresses as the
+ * SCTP port number remains the same for the lifetime of the channel.
+ *
+ * <P> New associations setup after this method successfully completes
+ * will be associated with the given address. Adding addresses to existing
+ * associations is optional functionality. If the endpoint supports
+ * dynamic address reconfiguration then it may send the appropriate message
+ * to the peer to change the peers address lists.
+ *
+ * @param address
+ * The address to add to the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound to the given address
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpMultiChannel bindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Removes the given address from the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise {@link NotYetBoundException} is thrown.
+ *
+ * <P> If this method is invoked on a channel that does
+ * not have {@code address} as one of its bound addresses, or that has only
+ * one local address bound to it, then this method throws
+ * {@link IllegalUnbindException}.
+ *
+ * <P> The initial address that the channel's socket is bound to using
+ * {@link #bind bind} may be removed from the bound addresses for the
+ * channel's socket.
+ *
+ * <P> New associations setup after this method successfully completes
+ * will not be associated with the given address. Removing addresses from
+ * existing associations is optional functionality. If the endpoint supports
+ * dynamic address reconfiguration then it may send the appropriate message
+ * to the peer to change the peers address lists.
+ *
+ * @param address
+ * The address to remove from the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws IllegalUnbindException
+ * {@code address} is not bound to the channel's socket, or the
+ * channel has only one address bound to it
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpMultiChannel unbindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Returns all of the socket addresses to which this channel's socket is
+ * bound.
+ *
+ * @return All the socket addresses that this channel's socket is
+ * bound to, or an empty {@code Set} if the channel's socket is not
+ * bound
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract Set<SocketAddress> getAllLocalAddresses()
+ throws IOException;
+
+ /**
+ * Returns all of the remote addresses to which the given association on
+ * this channel's socket is connected.
+ *
+ * @return All of the remote addresses for the given association, or
+ * an empty {@code Set} if the association has been shutdown
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract Set<SocketAddress> getRemoteAddresses(Association association)
+ throws IOException;
+
+ /**
+ * Shutdown an association without closing the channel.
+ *
+ * @param association
+ * The association to shutdown
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpMultiChannel shutdown(Association association)
+ throws IOException;
+
+ /**
+ * Returns the value of a socket option.
+ *
+ * <P> Note that some options are retrieved on the channel's socket,
+ * therefore the {@code association} parameter is not applicable and will be
+ * ignored if given. However, if the option is association specific then the
+ * association must be given.
+ *
+ * @param name
+ * The socket option
+ *
+ * @param association
+ * The association whose option should be retrieved, or {@code null}
+ * if this option should be retrieved at the channel's socket level.
+ *
+ * @return The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> T getOption(SctpSocketOption<T> name,
+ Association association)
+ throws IOException;
+
+ /**
+ * Sets the value of a socket option.
+ *
+ * <P> Note that some options are retrieved on the channel's socket,
+ * therefore the {@code association} parameter is not applicable and will be
+ * ignored if given. However, if the option is association specific then the
+ * association must be given.
+ *
+ * @param name
+ * The socket option
+ *
+ * @param association
+ * The association whose option should be set, or {@code null}
+ * if this option should be set at the channel's socket level.
+ *
+ * @param value
+ * The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @return This channel
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws IllegalArgumentException
+ * If the value is not a valid value for this socket option
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+ T value,
+ Association association)
+ throws IOException;
+
+ /**
+ * Returns a set of the socket options supported by this channel.
+ *
+ * <P> This method will continue to return the set of options even after the
+ * channel has been closed.
+ *
+ * @return A set of the socket options supported by this channel
+ */
+ public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+ /**
+ * Returns an operation set identifying this channel's supported operations.
+ *
+ * <P> SCTP multi channels support reading, and writing, so this
+ * method returns
+ * {@code (}{@link SelectionKey#OP_READ} {@code |} {@link
+ * SelectionKey#OP_WRITE}{@code )}. </p>
+ *
+ * @return The valid-operation set
+ */
+ @Override
+ public final int validOps() {
+ return (SelectionKey.OP_READ |
+ SelectionKey.OP_WRITE );
+ }
+
+ /**
+ * Receives a message and/or handles a notification via this channel.
+ *
+ * <P> If a message or notification is immediately available, or if this
+ * channel is in blocking mode and one eventually becomes available, then
+ * the message or notification is returned or handled, respectively. If this
+ * channel is in non-blocking mode and a message or notification is not
+ * immediately available then this method immediately returns {@code null}.
+ *
+ * <P> If this method receives a message it is copied into the given byte
+ * buffer and an {@link MessageInfo} is returned.
+ * The message is transferred into the given byte buffer starting at its
+ * current position and the buffers position is incremented by the number of
+ * bytes read. If there are fewer bytes remaining in the buffer than are
+ * required to hold the message, or the underlying input buffer does not
+ * contain the complete message, then an invocation of {@link
+ * MessageInfo#isComplete isComplete} on the returned {@code
+ * MessageInfo} will return {@code false}, and more invocations of this
+ * method will be necessary to completely consume the messgae. Only
+ * one message at a time will be partially delivered in any stream. The
+ * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
+ * messages occurs.
+ *
+ * <P> If this method receives a notification then the appropriate method of
+ * the given handler, if there is one, is invoked. If the handler returns {@link
+ * HandlerResult#CONTINUE CONTINUE} then this method will try to receive another
+ * message/notification, otherwise, if {@link HandlerResult#RETURN RETURN} is returned
+ * this method will return {@code null}. If an uncaught exception is thrown by the
+ * handler it will be propagated up the stack through this method.
+ *
+ * <P> If a security manager has been installed then for each new association
+ * setup this method verifies that the associations source address and port
+ * number are permitted by the security manager's {@link
+ * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
+ *
+ * <P> This method may be invoked at any time. If another thread has
+ * already initiated a receive operation upon this channel, then an
+ * invocation of this method will block until the first operation is
+ * complete. The given handler is invoked without holding any locks used
+ * to enforce the above synchronization policy, that way handlers
+ * will not stall other threads from receiving. A handler should not invoke
+ * the {@code receive} method of this channel, if it does an
+ * {@link IllegalReceiveException} will be thrown.
+ *
+ * @param buffer
+ * The buffer into which bytes are to be transferred
+ *
+ * @param attachment
+ * The object to attach to the receive operation; can be
+ * {@code null}
+ *
+ * @param handler
+ * A handler to handle notifications from the SCTP stack, or
+ * {@code null} to ignore any notifications.
+ *
+ * @return The {@code MessageInfo}, {@code null} if this channel is in
+ * non-blocking mode and no messages are immediately available or
+ * the notification handler returns {@code RETURN} after handling
+ * a notification
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the read operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the read operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws IllegalReceiveException
+ * If the given handler invokes the {@code receive} method of this
+ * channel
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * new associations to be accepted from the message's sender
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract <T> MessageInfo receive(ByteBuffer buffer,
+ T attachment,
+ NotificationHandler<T> handler)
+ throws IOException;
+
+ /**
+ * Sends a message via this channel.
+ *
+ * <P> If this channel is unbound then this method will invoke {@link
+ * #bind(SocketAddress, int) bind(null, 0)} before sending any data.
+ *
+ * <P> If there is no association existing between this channel's socket
+ * and the intended receiver, identified by the address in the given messageInfo, then one
+ * will be automatically setup to the intended receiver. This is considered
+ * to be Implicit Association Setup. Upon successful association setup, an
+ * {@link AssociationChangeNotification association changed}
+ * notification will be put to the SCTP stack with its {@code event} parameter set
+ * to {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
+ * . This notification can be received by invoking {@link #receive
+ * receive}.
+ *
+ * <P> If this channel is in blocking mode, there is sufficient room in the
+ * underlying output buffer, then the remaining bytes in the given byte
+ * buffer are transmitted as a single message. Sending a message
+ * is atomic unless explicit message completion {@link
+ * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
+ * socket option is enabled on this channel's socket.
+ *
+ * <P> If this channel is in non-blocking mode, there is sufficient room
+ * in the underlying output buffer, and an implicit association setup is
+ * required, then the remaining bytes in the given byte buffer are
+ * transmitted as a single message, subject to {@link
+ * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}.
+ * If for any reason the message cannot
+ * be delivered an {@link AssociationChangeNotification association
+ * changed} notification is put on the SCTP stack with its {@code event} parameter set
+ * to {@link AssociationChangeNotification.AssocChangeEvent#CANT_START CANT_START}.
+ *
+ * <P> The message is transferred from the byte buffer as if by a regular
+ * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
+ * write} operation.
+ *
+ * <P> If a security manager has been installed then for each new association
+ * setup this method verifies that the given remote peers address and port
+ * number are permitted by the security manager's {@link
+ * java.lang.SecurityManager#checkConnect(String,int) checkConnect} method.
+ *
+ * <P> This method may be invoked at any time. If another thread has already
+ * initiated a send operation upon this channel, then an invocation of
+ * this method will block until the first operation is complete.
+ *
+ * @param buffer
+ * The buffer containing the message to be sent
+ *
+ * @param messageInfo
+ * Ancillary data about the message to be sent
+ *
+ * @return The number of bytes sent, which will be either the number of
+ * bytes that were remaining in the messages buffer when this method
+ * was invoked or, if this channel is non-blocking, may be zero if
+ * there was insufficient room for the message in the underlying
+ * output buffer
+ *
+ * @throws InvalidStreamExcepton
+ * If {@code streamNumber} is negative, or if an association already
+ * exists and {@code streamNumber} is greater than the maximum number
+ * of outgoing streams
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the read operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the read operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * new associations to be setup with the the messages's address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract int send(ByteBuffer buffer, MessageInfo messageInfo)
+ throws IOException;
+
+ /**
+ * Branches off an association.
+ *
+ * <P> An application can invoke this method to branch off an association
+ * into a separate channel. The new bound and connected {@link SctpChannel}
+ * will be created for the association. The branched off association will no
+ * longer be part of this channel.
+ *
+ * <P> This is particularly useful when, for instance, the application
+ * wishes to have a number of sporadic message senders/receivers remain
+ * under the original SCTP multi channel but branch off those
+ * associations carrying high volume data traffic into their own
+ * separate SCTP channels.
+ *
+ * @param association
+ * The association to branch off
+ *
+ * @return The {@code SctpChannel}
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel branch(Association association)
+ throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.spi.AbstractSelectableChannel;
+
+/**
+ * A selectable channel for message-oriented listening SCTP sockets.
+ *
+ * <p> An {@code SCTPServerChannel} is created by invoking the
+ * {@link #open open} method of this class. A newly-created SCTP server
+ * channel is open but not yet bound. An attempt to invoke the
+ * {@link #accept accept} method of an unbound channel will cause the
+ * {@link java.nio.channels.NotYetBoundException} to be thrown. An SCTP server
+ * channel can be bound by invoking one of the
+ * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class.
+ *
+ * <p> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object) setOption} method. SCTP server socket
+ * channels support the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ * SCTP_INIT_MAXSTREAMS} </td>
+ * <td> The maximum number of streams requested by the local endpoint during
+ * association initialization </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions} method.
+ *
+ * <p>SCTP server channels are safe for use by multiple concurrent threads.
+ *
+ * @since 1.7
+ */
+public abstract class SctpServerChannel
+ extends AbstractSelectableChannel
+{
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @param provider
+ * The selector provider for this channel
+ */
+ protected SctpServerChannel(SelectorProvider provider) {
+ super(provider);
+ }
+
+ /**
+ * Opens an SCTP server channel.
+ *
+ * <P> The new channel's socket is initially unbound; it must be bound
+ * to a specific address via one of its socket's {@link #bind bind}
+ * methods before associations can be accepted.
+ *
+ * @return A new SCTP server channel
+ *
+ * @throws UnsupportedOperationException
+ * If the SCTP protocol is not supported
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static SctpServerChannel open() throws
+ IOException {
+ return new sun.nio.ch.SctpServerChannelImpl((SelectorProvider)null);
+ }
+
+ /**
+ * Accepts an association on this channel's socket.
+ *
+ * <P> If this channel is in non-blocking mode then this method will
+ * immediately return {@code null} if there are no pending associations.
+ * Otherwise it will block indefinitely until a new association is
+ * available or an I/O error occurs.
+ *
+ * <P> The {@code SCTPChannel} returned by this method, if any, will be in
+ * blocking mode regardless of the blocking mode of this channel.
+ *
+ * <P> If a security manager has been installed then for each new
+ * association this method verifies that the address and port number of the
+ * assocaitions's remote peer are permitted by the security manager's {@link
+ * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
+ *
+ * @return The SCTP channel for the new association, or {@code null}
+ * if this channel is in non-blocking mode and no association is
+ * available to be accepted
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AsynchronousCloseException
+ * If another thread closes this channel
+ * while the accept operation is in progress
+ *
+ * @throws java.nio.channels.ClosedByInterruptException
+ * If another thread interrupts the current thread
+ * while the accept operation is in progress, thereby
+ * closing the channel and setting the current thread's
+ * interrupt status
+ *
+ * @throws java.nio.channels.NotYetBoundException
+ * If this channel's socket has not yet been bound
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * access to the remote peer of the new association
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpChannel accept() throws IOException;
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket
+ * to listen for associations.
+ *
+ * <P> This method works as if invoking it were equivalent to evaluating the
+ * expression:
+ * <blockquote><pre>
+ * bind(local, 0);
+ * </pre></blockquote>
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to
+ * bind the socket to an automatically assigned socket address
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * java.lang.SecurityManager#checkListen(int) checkListen} method
+ * denies the operation
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public final SctpServerChannel bind(SocketAddress local)
+ throws IOException {
+ return bind(local, 0);
+ }
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket
+ * to listen for associations.
+ *
+ * <P> This method is used to establish a relationship between the socket
+ * and the local address. Once a relationship is established then
+ * the socket remains bound until the channel is closed. This relationship
+ * may not necesssarily be with the address {@code local} as it may be
+ * removed by {@link #unbindAddress unbindAddress}, but there will always be
+ * at least one local address bound to the channel's socket once an
+ * invocation of this method successfully completes.
+ *
+ * <P> Once the channel's socket has been successfully bound to a specific
+ * address, that is not automatically assigned, more addresses
+ * may be bound to it using {@link #bindAddress bindAddress}, or removed
+ * using {@link #unbindAddress unbindAddress}.
+ *
+ * <P> The backlog parameter is the maximum number of pending associations
+ * on the socket. Its exact semantics are implementation specific. An
+ * implementation may impose an implementation specific maximum length or
+ * may choose to ignore the parameter. If the backlog parameter has the
+ * value {@code 0}, or a negative value, then an implementation specific
+ * default is used.
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to
+ * bind the socket to an automatically assigned socket address
+ *
+ * @param backlog
+ * The maximum number number of pending associations
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound
+ *
+ * @throws java.nio.channels.UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * java.lang.SecurityManager#checkListen(int) checkListen} method
+ * denies the operation
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpServerChannel bind(SocketAddress local,
+ int backlog)
+ throws IOException;
+
+ /**
+ * Adds the given address to the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise {@link
+ * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
+ * method takes a {@code SocketAddress} as its argument which typically
+ * contains a port number as well as an address. Addresses subquently bound
+ * using this method are simply addresses as the SCTP port number remains
+ * the same for the lifetime of the channel.
+ *
+ * <P> New associations accepted after this method successfully completes
+ * will be associated with the given address.
+ *
+ * @param address
+ * The address to add to the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws java.nio.channels.AlreadyBoundException
+ * If this channel is already bound to the given address
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpServerChannel bindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Removes the given address from the bound addresses for the channel's
+ * socket.
+ *
+ * <P> The given address must not be the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+ * The channel must be first bound using {@link #bind bind} before
+ * invoking this method, otherwise
+ * {@link java.nio.channels.NotYetBoundException} is thrown.
+ * If this method is invoked on a channel that does not have
+ * {@code address} as one of its bound addresses, or that has only one
+ * local address bound to it, then this method throws {@link
+ * IllegalUnbindException}.
+ * The initial address that the channel's socket is bound to using
+ * {@link #bind bind} may be removed from the bound addresses for the
+ * channel's socket.
+ *
+ * <P> New associations accepted after this method successfully completes
+ * will not be associated with the given address.
+ *
+ * @param address
+ * The address to remove from the bound addresses for the socket
+ *
+ * @return This channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws java.nio.channels.NotYetBoundException
+ * If this channel is not yet bound
+ *
+ * @throws IllegalArgumentException
+ * If address is {@code null} or the {@link
+ * java.net.InetAddress#isAnyLocalAddress wildcard} address
+ *
+ * @throws IllegalUnbindException
+ * If the implementation does not support removing addresses from a
+ * listening socket, {@code address} is not bound to the channel's
+ * socket, or the channel has only one address bound to it
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract SctpServerChannel unbindAddress(InetAddress address)
+ throws IOException;
+
+ /**
+ * Returns all of the socket addresses to which this channel's socket is
+ * bound.
+ *
+ * @return All the socket addresses that this channel's socket is
+ * bound to, or an empty {@code Set} if the channel's socket is not
+ * bound
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If the channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract Set<SocketAddress> getAllLocalAddresses()
+ throws IOException;
+
+ /**
+ * Returns the value of a socket option.
+ *
+ * @param name
+ * The socket option
+ *
+ * @return The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> T getOption(SctpSocketOption<T> name) throws IOException;
+
+ /**
+ * Sets the value of a socket option.
+ *
+ * @param name
+ * The socket option
+ *
+ * @param value
+ * The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @return This channel
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ *
+ * @throws IllegalArgumentException
+ * If the value is not a valid value for this socket option
+ *
+ * @throws java.nio.channels.ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see SctpStandardSocketOption
+ */
+ public abstract <T> SctpServerChannel setOption(SctpSocketOption<T> name,
+ T value)
+ throws IOException;
+
+ /**
+ * Returns a set of the socket options supported by this channel.
+ *
+ * <P> This method will continue to return the set of options even after the
+ * channel has been closed.
+ *
+ * @return A set of the socket options supported by this channel
+ */
+ public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+ /**
+ * Returns an operation set identifying this channel's supported
+ * operations.
+ *
+ * <P> SCTP server channels only support the accepting of new
+ * associations, so this method returns
+ * {@link java.nio.channels.SelectionKey#OP_ACCEPT}.
+ *
+ * @return The valid-operation set
+ */
+ @Override
+ public final int validOps() {
+ return SelectionKey.OP_ACCEPT;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketOption;
+
+/**
+ * A socket option associated with an SCTP channel.
+ *
+ * @param <T> The type of the socket option value.
+ *
+ * @since 1.7
+ *
+ * @see SctpStandardSocketOption
+ */
+public interface SctpSocketOption<T> extends SocketOption<T> { }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import sun.nio.ch.SctpStdSocketOption;
+
+/**
+ * SCTP channels supports the socket options defined by this class
+ * (as well as those listed in the particular channel class) and may support
+ * additional Implementation specific socket options.
+ *
+ * @since 1.7
+ */
+public class SctpStandardSocketOption {
+ private SctpStandardSocketOption() {}
+ /**
+ * Enables or disables message fragmentation.
+ *
+ * <P> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. If enabled no SCTP message
+ * fragmentation will be performed. Instead if a message being sent
+ * exceeds the current PMTU size, the message will NOT be sent and
+ * an error will be indicated to the user.
+ *
+ * <P> It is implementation specific whether or not this option is
+ * supported.
+ */
+ public static final SctpSocketOption<Boolean> SCTP_DISABLE_FRAGMENTS = new
+ SctpStdSocketOption<Boolean>("SCTP_DISABLE_FRAGMENTS", Boolean.class,
+ sun.nio.ch.SctpStdSocketOption.SCTP_DISABLE_FRAGMENTS);
+
+ /**
+ * Enables or disables explicit message completion.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. When this option is enabled,
+ * the {@code send} method may be invoked multiple times to a send message.
+ * The {@code isComplete} parameter of the {@link MessageInfo} must only
+ * be set to {@code true} for the final send to indicate that the message is
+ * complete. If this option is disabled then each individual {@code send}
+ * invocation is considered complete.
+ *
+ * <P> The default value of the option is {@code false} indicating that the
+ * option is disabled. It is implementation specific whether or not this
+ * option is supported.
+ */
+ public static final SctpSocketOption<Boolean> SCTP_EXPLICIT_COMPLETE = new
+ SctpStdSocketOption<Boolean>("SCTP_EXPLICIT_COMPLETE", Boolean.class,
+ sun.nio.ch.SctpStdSocketOption.SCTP_EXPLICIT_COMPLETE);
+
+ /**
+ * Fragmented interleave controls how the presentation of messages occur
+ * for the message receiver. There are three levels of fragment interleave
+ * defined. Two of the levels effect {@link SctpChannel}, while
+ * {@link SctpMultiChannel} is effected by all three levels.
+ *
+ * <P> This option takes an {@code Integer} value. It can be set to a value
+ * of {@code 0}, {@code 1} or {@code 2}.
+ *
+ * <P> Setting the three levels provides the following receiver
+ * interactions:
+ *
+ * <P> {@code level 0} - Prevents the interleaving of any messages. This
+ * means that when a partial delivery begins, no other messages will be
+ * received except the message being partially delivered. If another message
+ * arrives on a different stream (or association) that could be delivered,
+ * it will be blocked waiting for the user to read all of the partially
+ * delivered message.
+ *
+ * <P> {@code level 1} - Allows interleaving of messages that are from
+ * different associations. For {@code SctpChannel}, level 0 and
+ * level 1 have the same meaning since an {@code SctpChannel} always
+ * receives messages from the same association. Note that setting an {@code
+ * SctpMultiChannel} to this level may cause multiple partial
+ * delivers from different associations but for any given association, only
+ * one message will be delivered until all parts of a message have been
+ * delivered. This means that one large message, being read with an
+ * association identification of "X", will block other messages from
+ * association "X" from being delivered.
+ *
+ * <P> {@code level 2} - Allows complete interleaving of messages. This
+ * level requires that the sender carefully observe not only the peer
+ * {@code Association} but also must pay careful attention to the stream
+ * number. With this option enabled a partially delivered message may begin
+ * being delivered for association "X" stream "Y" and the next subsequent
+ * receive may return a message from association "X" stream "Z". Note that
+ * no other messages would be delivered for association "X" stream "Y"
+ * until all of stream "Y"'s partially delivered message was read.
+ * Note that this option effects both channel types. Also note that
+ * for an {@code SctpMultiChannel} not only may another streams
+ * message from the same association be delivered from the next receive,
+ * some other associations message may be delivered upon the next receive.
+ *
+ * <P> It is implementation specific whether or not this option is
+ * supported.
+ */
+ public static final SctpSocketOption<Integer> SCTP_FRAGMENT_INTERLEAVE =
+ new SctpStdSocketOption<Integer>("SCTP_FRAGMENT_INTERLEAVE",
+ Integer.class,
+ sun.nio.ch.SctpStdSocketOption.SCTP_FRAGMENT_INTERLEAVE);
+
+ /**
+ * The maximum number of streams requested by the local endpoint during
+ * association initialization.
+ *
+ * <P> The value of this socket option is an {@link
+ * SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents
+ * the maximum number of inbound and outbound streams that an association
+ * on the channel is prepared to support.
+ *
+ * <P> For an {@link SctpChannel} this option may only be used to
+ * change the number of inbound/outbound streams prior to connecting.
+ *
+ * <P> For an {@link SctpMultiChannel} this option determines
+ * the maximum number of inbound/outbound streams new associations setup
+ * on the channel will be prepared to support.
+ *
+ * <P> For an {@link SctpServerChannel} this option determines the
+ * maximum number of inbound/outbound streams accepted sockets will
+ * negotiate with their connecting peer.
+ *
+ * <P> In all cases the value set by this option is used in the negotiation
+ * of new associations setup on the channel's socket and the actual
+ * maximum number of inbound/outbound streams that have been negotiated
+ * with the peer can be retrieved from the appropriate {@link
+ * Association}. The {@code Association} can be retrieved from the
+ * {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
+ * {@link AssociationChangeNotification} belonging to that association.
+ *
+ * <p> This value is bounded by the actual implementation. In other
+ * words the user may be able to support more streams than the Operating
+ * System. In such a case, the Operating System limit may override the
+ * value requested by the user. The default value of 0 indicates to use
+ * the endpoints default value.
+ */
+ public static final SctpSocketOption
+ <SctpStandardSocketOption.InitMaxStreams> SCTP_INIT_MAXSTREAMS =
+ new SctpStdSocketOption<SctpStandardSocketOption.InitMaxStreams>(
+ "SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class);
+
+ /**
+ * Enables or disables a Nagle-like algorithm.
+ *
+ * <P> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. SCTP uses an algorithm like
+ * <em>The Nagle Algorithm</em> to coalesce short segments and
+ * improve network efficiency.
+ */
+ public static final SctpSocketOption<Boolean> SCTP_NODELAY =
+ new SctpStdSocketOption<Boolean>("SCTP_NODELAY", Boolean.class,
+ sun.nio.ch.SctpStdSocketOption.SCTP_NODELAY);
+
+ /**
+ * Requests that the local SCTP stack use the given peer address as
+ * the association primary.
+ *
+ * <P> The value of this socket option is a {@code SocketAddress}
+ * that represents the peer address that the local SCTP stack should use as
+ * the association primary. The address must be one of the association
+ * peer's addresses.
+ *
+ * <P> An {@code SctpMultiChannel} can control more than one
+ * association, the association parameter must be given when setting or
+ * retrieving this option.
+ *
+ * <P> Since {@code SctpChannel} only controls one association,
+ * the association parameter is not required and this option can be
+ * set or queried directly.
+ */
+ public static final SctpSocketOption<SocketAddress> SCTP_PRIMARY_ADDR =
+ new SctpStdSocketOption<SocketAddress>
+ ("SCTP_PRIMARY_ADDR", SocketAddress.class);
+
+ /**
+ * Requests that the peer mark the enclosed address as the association
+ * primary.
+ *
+ * <P> The value of this socket option is a {@code SocketAddress}
+ * that represents the local address that the peer should use as its
+ * primary address. The given address must be one of the association's
+ * locally bound addresses.
+ *
+ * <P> An {@code SctpMultiChannel} can control more than one
+ * association, the association parameter must be given when setting or
+ * retrieving this option.
+ *
+ * <P> Since {@code SctpChannel} only controls one association,
+ * the association parameter is not required and this option can be
+ * queried directly.
+ *
+ * <P> Note, this is a set only option and cannot be retrieved by {@code
+ * getOption}. It is implementation specific whether or not this
+ * option is supported.
+ */
+ public static final SctpSocketOption<SocketAddress> SCTP_SET_PEER_PRIMARY_ADDR =
+ new SctpStdSocketOption<SocketAddress>
+ ("SCTP_SET_PEER_PRIMARY_ADDR", SocketAddress.class);
+
+ /**
+ * The size of the socket send buffer.
+ *
+ * <p> The value of this socket option is an {@code Integer} that is the
+ * size of the socket send buffer in bytes. The socket send buffer is an
+ * output buffer used by the networking implementation. It may need to be
+ * increased for high-volume connections. The value of the socket option is
+ * a <em>hint</em> to the implementation to size the buffer and the actual
+ * size may differ. The socket option can be queried to retrieve the actual
+ * size.
+ *
+ * <p> For {@code SctpChannel}, this controls the amount of data
+ * the SCTP stack may have waiting in internal buffers to be sent. This
+ * option therefore bounds the maximum size of data that can be sent in a
+ * single send call.
+ *
+ * <P> For {@code SctpMultiChannel}, the effect is the same as for {@code
+ * SctpChannel}, except that it applies to all associations. The option
+ * applies to each association's window size separately.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Whether an implementation allows the
+ * socket send buffer to be changed after the socket is bound is system
+ * dependent.
+ */
+ public static final SctpSocketOption<Integer> SO_SNDBUF =
+ new SctpStdSocketOption<Integer>("SO_SNDBUF", Integer.class,
+ sun.nio.ch.SctpStdSocketOption.SO_SNDBUF);
+
+ /**
+ * The size of the socket receive buffer.
+ *
+ * <P> The value of this socket option is an {@code Integer} that is the
+ * size of the socket receive buffer in bytes. The socket receive buffer is
+ * an input buffer used by the networking implementation. It may need to be
+ * increased for high-volume connections or decreased to limit the possible
+ * backlog of incoming data. The value of the socket option is a
+ * <em>hint</em> to the implementation to size the buffer and the actual
+ * size may differ.
+ *
+ * <P> For {@code SctpChannel}, this controls the receiver window size.
+ *
+ * <P> For {@code SctpMultiChannel}, the meaning is implementation
+ * dependent. It might control the receive buffer for each association bound
+ * to the socket descriptor or it might control the receive buffer for the
+ * whole socket.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Whether an implementation allows the
+ * socket receive buffer to be changed after the socket is bound is system
+ * dependent.
+ */
+ public static final SctpSocketOption<Integer> SO_RCVBUF =
+ new SctpStdSocketOption<Integer>("SO_RCVBUF", Integer.class,
+ sun.nio.ch.SctpStdSocketOption.SO_RCVBUF);
+
+ /**
+ * Linger on close if data is present.
+ *
+ * <p> The value of this socket option is an {@code Integer} that controls
+ * the action taken when unsent data is queued on the socket and a method
+ * to close the socket is invoked. If the value of the socket option is zero
+ * or greater, then it represents a timeout value, in seconds, known as the
+ * <em>linger interval</em>. The linger interval is the timeout for the
+ * {@code close} method to block while the operating system attempts to
+ * transmit the unsent data or it decides that it is unable to transmit the
+ * data. If the value of the socket option is less than zero then the option
+ * is disabled. In that case the {@code close} method does not wait until
+ * unsent data is transmitted; if possible the operating system will transmit
+ * any unsent data before the connection is closed.
+ *
+ * <p> This socket option is intended for use with sockets that are configured
+ * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode
+ * only. The behavior of the {@code close} method when this option is
+ * enabled on a non-blocking socket is not defined.
+ *
+ * <p> The initial value of this socket option is a negative value, meaning
+ * that the option is disabled. The option may be enabled, or the linger
+ * interval changed, at any time. The maximum value of the linger interval
+ * is system dependent. Setting the linger interval to a value that is
+ * greater than its maximum value causes the linger interval to be set to
+ * its maximum value.
+ */
+ public static final SctpSocketOption<Integer> SO_LINGER =
+ new SctpStdSocketOption<Integer>("SO_LINGER", Integer.class,
+ sun.nio.ch.SctpStdSocketOption.SO_LINGER);
+
+ /**
+ * This class is used to set the maximum number of inbound/outbound streams
+ * used by the local endpoint during association initialization. An
+ * instance of this class is used to set the {@link
+ * SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS}
+ * socket option.
+ *
+ * @since 1.7
+ */
+ public static class InitMaxStreams {
+ private int maxInStreams;
+ private int maxOutStreams;
+
+ private InitMaxStreams(int maxInStreams, int maxOutStreams) {
+ this.maxInStreams = maxInStreams;
+ this.maxOutStreams = maxOutStreams;
+ }
+
+ /**
+ * Creates an InitMaxStreams instance.
+ *
+ * @param maxInStreams
+ * The maximum number of inbound streams, where
+ * {@code 0 <= maxInStreams <= 65536}
+ *
+ * @param maxOutStreams
+ * The maximum number of outbound streams, where
+ * {@code 0 <= maxOutStreams <= 65536}
+ *
+ * @return An {@code InitMaxStreams} instance
+ *
+ * @throws IllegalArgumentException
+ * If an argument is outside of specified bounds
+ */
+ public static InitMaxStreams create
+ (int maxInStreams, int maxOutStreams) {
+ if (maxOutStreams < 0 || maxOutStreams > 65535)
+ throw new IllegalArgumentException(
+ "Invalid maxOutStreams value");
+ if (maxInStreams < 0 || maxInStreams > 65535)
+ throw new IllegalArgumentException(
+ "Invalid maxInStreams value");
+
+ return new InitMaxStreams(maxInStreams, maxOutStreams);
+ }
+
+ /**
+ * Returns the maximum number of inbound streams.
+ *
+ * @return Maximum inbound streams
+ */
+ public int maxInStreams() {
+ return maxInStreams;
+ }
+
+ /**
+ * Returns the maximum number of outbound streams.
+ *
+ * @return Maximum outbound streams
+ */
+ public int maxOutStreams() {
+ return maxOutStreams;
+ }
+
+ /**
+ * Returns a string representation of this init max streams, including
+ * the maximum in and out bound streams.
+ *
+ * @return A string representation of this init max streams
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString()).append(" [");
+ sb.append("maxInStreams:").append(maxInStreams);
+ sb.append("maxOutStreams:").append(maxOutStreams).append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if the specified object is another {@code InitMaxStreams}
+ * instance with the same number of in and out bound streams.
+ *
+ * @param obj
+ * The object to be compared with this init max streams
+ *
+ * @return true if the specified object is another
+ * {@code InitMaxStreams} instance with the same number of in
+ * and out bound streams
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj != null && obj instanceof InitMaxStreams) {
+ InitMaxStreams that = (InitMaxStreams) obj;
+ if (this.maxInStreams == that.maxInStreams &&
+ this.maxOutStreams == that.maxOutStreams)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this init max streams.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 7 ^ maxInStreams ^ maxOutStreams;
+ return hash;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+import java.nio.ByteBuffer;
+import java.net.SocketAddress;
+
+/**
+ * Notification emitted when a send failed notification has been received.
+ *
+ * <P> A send failed notification indicates that a message cannot be delivered.
+ * Typically this is because the association has been shutdown with unsent data
+ * in the socket output buffer, or in the case of a {@link SctpMultiChannel}
+ * the association failed to setup.
+ *
+ * @since 1.7
+ */
+public abstract class SendFailedNotification implements Notification {
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected SendFailedNotification() {}
+
+ /**
+ * Returns the association that this notification is applicable to.
+ *
+ * @return The association that failed to send, or {@code null} if
+ * there is no association, that is, the notification follows a
+ * {@linkplain
+ * com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent#CANT_START}
+ */
+ @Override
+ public abstract Association association();
+
+ /**
+ * Returns the address.
+ *
+ * @return The peer primary address of the association or the address that
+ * the message was sent to
+ */
+ public abstract SocketAddress address();
+
+ /**
+ * Returns the data that was to be sent.
+ *
+ * @return The user data. The buffers position will be {@code 0} and its
+ * limit will be set to the end of the data.
+ */
+ public abstract ByteBuffer buffer();
+
+ /**
+ * Returns the error code.
+ *
+ * <P> The errorCode gives the reason why the send failed, and if set, will
+ * be a SCTP protocol error code as defined in RFC2960 section 3.3.10
+ *
+ * @return The error code
+ */
+ public abstract int errorCode();
+
+ /**
+ * Returns the stream number that the messge was to be sent on.
+ *
+ * @return The stream number
+ */
+ public abstract int streamNumber();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 com.sun.nio.sctp;
+
+/**
+ * Notification emitted when a peers shutdowns an the association.
+ *
+ * <P> When a peer sends a <i>SHUTDOWN</i>, the SCTP stack delivers this
+ * notification to inform the application that it should cease sending data.
+ *
+ * @since 1.7
+ */
+public abstract class ShutdownNotification implements Notification {
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected ShutdownNotification() {}
+
+ /**
+ * Returns the association that this notification is applicable to.
+ *
+ * @return The association that received the shutdown
+ */
+ public abstract Association association();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/package-info.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+/**
+ * A Java API for Stream Control Transport Protocol.
+ *
+ * <P> The Stream Control Transport Protocol (SCTP) is a reliable,
+ * message-oriented, transport protocol existing at an equivalent level with UDP
+ * (User Datagram Protocol) and TCP (Transmission Control Protocol). SCTP is
+ * session oriented and an association between the endpoints must be established
+ * before any data can be transmitted.
+ *
+ * <P> SCTP has direct support for multi-homing, meaning than an endpoint may be
+ * represented by more than one address and each address may be used for sending
+ * and receiving data, thus providing network redundancy. The connection between
+ * two endpoints is referred to as an association between those endpoints.
+ * Endpoints can exchange a list of addresses during association setup. One
+ * address is designated as the primary address, this is the default address that
+ * the peer will use for sending data. A single port number is used across the
+ * entire address list at an endpoint for a specific session.
+ *
+ * <P> SCTP is message based. I/O operations operate upon messages and message
+ * boundaries are preserved. Each association may support multiple independant
+ * logical streams. Each stream represents a sequence of messages within a single
+ * association and streams are independant of one another, meaning that stream
+ * identifiers and sequence numbers are included in the data packet to allow
+ * sequencing of messages on a per-stream basis.
+ *
+ * <P> This package provides two programming model styles. The one-to-one style
+ * supported by {@link com.sun.nio.sctp.SctpChannel} and {@link
+ * com.sun.nio.sctp.SctpServerChannel}, and the one-to-many
+ * style supported by {@link com.sun.nio.sctp.SctpMultiChannel}.
+ * The semantics of the one-to-one style interface are very similar to TCP.
+ * An {@code SctpChannel} can only control one SCTP association. The
+ * semantics of the one-to-many style interface are very similar to UDP. An
+ * {@code SctpMutliChannel} can control multiple SCTP associations.
+ *
+ * <P> Applications can send and receive per-message ancillary information through
+ * {@link com.sun.nio.sctp.MessageInfo}. For example, the stream number that
+ * the message it is to be sent or received from. The SCTP stack is event driven
+ * and applications can receive notifications of certain SCTP events by invoking
+ * the {@code receive} method of the SCTP channel with an appropriate {@link
+ * com.sun.nio.sctp.NotificationHandler notification handler}.
+ *
+ * <P> The SCTP protocol is defined by
+ * <A HREF="http://tools.ietf.org/html/rfc4960">RFC4960</A>, and the optional
+ * extension for <I>Dynamic Address Reconfiguration</I> is defined by
+ * <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>.
+ *
+ * @since 1.7
+ */
+
+package com.sun.nio.sctp;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Association;
+
+/**
+ * An implementation of a MessageInfo.
+ */
+public class SctpMessageInfoImpl extends MessageInfo {
+ private final SocketAddress address;
+ private final int bytes; /* 0 */
+
+ private Association association;
+ private int assocId;
+ private int streamNumber;
+ private boolean complete = true;
+ private boolean unordered; /* false */
+ private long timeToLive; /* 0L */
+ private int ppid; /* 0 */
+
+ public SctpMessageInfoImpl(Association association,
+ SocketAddress address,
+ int streamNumber) {
+ this.association = association;
+ this.address = address;
+ this.streamNumber = streamNumber;
+ bytes = 0;
+ }
+
+ /* Invoked from native */
+ private SctpMessageInfoImpl(int assocId,
+ SocketAddress address,
+ int bytes,
+ int streamNumber,
+ boolean complete,
+ boolean unordered,
+ int ppid) {
+ this.assocId = assocId;
+ this.address = address;
+ this.bytes = bytes;
+ this.streamNumber = streamNumber;
+ this.complete = complete;
+ this.unordered = unordered;
+ this.ppid = ppid;
+ }
+
+ @Override
+ public Association association() {
+ return association;
+ }
+
+ /**
+ * SctpMessageInfoImpl instances created from native will need to have their
+ * association set from the channel.
+ */
+ void setAssociation(Association association) {
+ this.association = association;
+ }
+
+ int associationID() {
+ return assocId;
+ }
+
+ @Override
+ public SocketAddress address() {
+ return address;
+ }
+
+ @Override
+ public int bytes() {
+ return bytes;
+ }
+
+ @Override
+ public int streamNumber() {
+ return streamNumber;
+ }
+
+ @Override
+ public MessageInfo streamNumber(int streamNumber) {
+ if (streamNumber < 0 || streamNumber > 65536)
+ throw new IllegalArgumentException("Invalid stream number");
+
+ this.streamNumber = streamNumber;
+ return this;
+ }
+
+ @Override
+ public int payloadProtocolID() {
+ return ppid;
+ }
+
+ @Override
+ public MessageInfo payloadProtocolID(int ppid) {
+ this.ppid = ppid;
+ return this;
+ }
+
+ @Override
+ public boolean isComplete() {
+ return complete;
+ }
+
+ @Override
+ public MessageInfo complete(boolean complete) {
+ this.complete = complete;
+ return this;
+ }
+
+ @Override
+ public boolean isUnordered() {
+ return unordered;
+ }
+
+ @Override
+ public MessageInfo unordered(boolean unordered) {
+ this.unordered = unordered;
+ return this;
+ }
+
+ @Override
+ public long timeToLive() {
+ return timeToLive;
+ }
+
+ @Override
+ public MessageInfo timeToLive(long millis) {
+ timeToLive = millis;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append( "[Address: ").append(address)
+ .append(", Association: ").append(association)
+ .append(", Assoc ID: ").append(assocId)
+ .append(", Bytes: ").append(bytes)
+ .append(", Stream Number: ").append(streamNumber)
+ .append(", Complete: ").append(complete)
+ .append(", isUnordered: ").append(unordered)
+ .append("]");
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/SctpStdSocketOption.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import com.sun.nio.sctp.SctpSocketOption;
+
+public class SctpStdSocketOption<T>
+ implements SctpSocketOption<T>
+{
+ /* for native mapping of int options */
+ public static final int SCTP_DISABLE_FRAGMENTS = 1;
+ public static final int SCTP_EXPLICIT_COMPLETE = 2;
+ public static final int SCTP_FRAGMENT_INTERLEAVE = 3;
+ public static final int SCTP_NODELAY = 4;
+ public static final int SO_SNDBUF = 5;
+ public static final int SO_RCVBUF = 6;
+ public static final int SO_LINGER = 7;
+
+ private final String name;
+ private final Class<T> type;
+
+ /* for native mapping of int options */
+ private int constValue;
+
+ public SctpStdSocketOption(String name, Class<T> type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public SctpStdSocketOption(String name, Class<T> type, int constValue) {
+ this.name = name;
+ this.type = type;
+ this.constValue = constValue;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Class<T> type() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ int constValue() {
+ return constValue;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpAssocChange.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+
+/**
+ * An implementation of AssociationChangeNotification
+ */
+public class SctpAssocChange extends AssociationChangeNotification
+ implements SctpNotification
+{
+ /* static final ints so that they can be referenced from native */
+ private final static int SCTP_COMM_UP = 1;
+ private final static int SCTP_COMM_LOST = 2;
+ private final static int SCTP_RESTART = 3;
+ private final static int SCTP_SHUTDOWN = 4;
+ private final static int SCTP_CANT_START = 5;
+
+ private Association association;
+
+ /* assocId is used to lookup the association before the notification is
+ * returned to user code */
+ private int assocId;
+ private AssocChangeEvent event;
+ private int maxOutStreams;
+ private int maxInStreams;
+
+ /* Invoked from native */
+ private SctpAssocChange(int assocId,
+ int intEvent,
+ int maxOutStreams,
+ int maxInStreams) {
+ switch (intEvent) {
+ case SCTP_COMM_UP :
+ this.event = AssocChangeEvent.COMM_UP;
+ break;
+ case SCTP_COMM_LOST :
+ this.event = AssocChangeEvent.COMM_LOST;
+ break;
+ case SCTP_RESTART :
+ this.event = AssocChangeEvent.RESTART;
+ break;
+ case SCTP_SHUTDOWN :
+ this.event = AssocChangeEvent.SHUTDOWN;
+ break;
+ case SCTP_CANT_START :
+ this.event = AssocChangeEvent.CANT_START;
+ break;
+ default :
+ throw new AssertionError(
+ "Unknown Association Change Event type: " + intEvent);
+ }
+
+ this.assocId = assocId;
+ this.maxOutStreams = maxOutStreams;
+ this.maxInStreams = maxInStreams;
+ }
+
+ @Override
+ public int assocId() {
+ return assocId;
+ }
+
+ @Override
+ public void setAssociation(Association association) {
+ this.association = association;
+ }
+
+ @Override
+ public Association association() {
+ assert association != null;
+ return association;
+ }
+
+ @Override
+ public AssocChangeEvent event() {
+ return event;
+ }
+
+ int maxOutStreams() {
+ return maxOutStreams;
+ }
+
+ int maxInStreams() {
+ return maxInStreams;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString()).append(" [");
+ sb.append("Association:").append(association);
+ sb.append(", Event: ").append(event).append("]");
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import com.sun.nio.sctp.Association;
+
+/**
+ * An implementation of Association
+ */
+public class SctpAssociationImpl extends Association {
+ public SctpAssociationImpl(int associationID,
+ int maxInStreams,
+ int maxOutStreams) {
+ super(associationID, maxInStreams, maxOutStreams);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(super.toString());
+ return sb.append("[associationID:")
+ .append(associationID())
+ .append(", maxIn:")
+ .append(maxInboundStreams())
+ .append(", maxOut:")
+ .append(maxOutboundStreams())
+ .append("]")
+ .toString();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,1073 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ConnectionPendingException;
+import java.nio.channels.NoConnectionPendingException;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import sun.nio.ch.NativeDispatcher;
+import sun.nio.ch.PollArrayWrapper;
+import sun.nio.ch.SelChImpl;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static sun.nio.ch.SctpResultContainer.SEND_FAILED;
+import static sun.nio.ch.SctpResultContainer.ASSOCIATION_CHANGED;
+import static sun.nio.ch.SctpResultContainer.PEER_ADDRESS_CHANGED;
+import static sun.nio.ch.SctpResultContainer.SHUTDOWN;
+
+/**
+ * An implementation of an SctpChannel
+ */
+public class SctpChannelImpl extends SctpChannel
+ implements SelChImpl
+{
+ /* Used to make native close and preClose calls */
+ private static NativeDispatcher nd;
+
+ private final FileDescriptor fd;
+
+ private final int fdVal;
+
+ /* IDs of native threads doing send and receivess, for signalling */
+ private volatile long receiverThread = 0;
+ private volatile long senderThread = 0;
+
+ /* Lock held by current receiving or connecting thread */
+ private final Object receiveLock = new Object();
+
+ /* Lock held by current sending or connecting thread */
+ private final Object sendLock = new Object();
+
+ private final ThreadLocal<Boolean> receiveInvoked =
+ new ThreadLocal<Boolean>() {
+ @Override protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /* Lock held by any thread that modifies the state fields declared below
+ DO NOT invoke a blocking I/O operation while holding this lock! */
+ private final Object stateLock = new Object();
+
+ private enum ChannelState {
+ UNINITIALIZED,
+ UNCONNECTED,
+ PENDING,
+ CONNECTED,
+ KILLPENDING,
+ KILLED,
+ }
+ /* -- The following fields are protected by stateLock -- */
+ private ChannelState state = ChannelState.UNINITIALIZED;
+
+ /* Binding; Once bound the port will remain constant. */
+ int port = -1;
+ private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+ /* Has the channel been bound to the wildcard address */
+ private boolean wildcard; /* false */
+ //private InetSocketAddress remoteAddress = null;
+
+ /* Input/Output open */
+ private boolean readyToConnect;
+
+ /* Shutdown */
+ private boolean isShutdown;
+
+ private Association association;
+
+ /* -- End of fields protected by stateLock -- */
+
+ private SctpResultContainer commUpResultContainer; /* null */
+
+ /**
+ * Constructor for normal connecting sockets
+ */
+ public SctpChannelImpl(SelectorProvider provider) throws IOException {
+ //TODO: update provider remove public modifier
+ super(provider);
+ this.fd = SctpNet.socket(true);
+ this.fdVal = IOUtil.fdVal(fd);
+ this.state = ChannelState.UNCONNECTED;
+ }
+
+ /**
+ * Constructor for sockets obtained from server sockets
+ */
+ public SctpChannelImpl(SelectorProvider provider, FileDescriptor fd)
+ throws IOException {
+ super(provider);
+ this.fd = fd;
+ this.fdVal = IOUtil.fdVal(fd);
+ this.state = ChannelState.CONNECTED;
+ port = (Net.localAddress(fd)).getPort();
+
+ /* Receive COMM_UP */
+ ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+ try {
+ receive(buf, null, null, true);
+ } finally {
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
+ }
+
+ /**
+ * Binds the channel's socket to a local address.
+ */
+ @Override
+ public SctpChannel bind(SocketAddress local) throws IOException {
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ synchronized (stateLock) {
+ ensureOpenAndUnconnected();
+ if (isBound())
+ throw new AlreadyBoundException();
+ InetSocketAddress isa = (local == null) ?
+ new InetSocketAddress(0) : Net.checkAddress(local);
+ Net.bind(fd, isa.getAddress(), isa.getPort());
+ InetSocketAddress boundIsa = Net.localAddress(fd);
+ port = boundIsa.getPort();
+ localAddresses.add(isa);
+ if (isa.getAddress().isAnyLocalAddress())
+ wildcard = true;
+ }
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public SctpChannel bindAddress(InetAddress address)
+ throws IOException {
+ bindUnbindAddress(address, true);
+ localAddresses.add(new InetSocketAddress(address, port));
+ return this;
+ }
+
+ @Override
+ public SctpChannel unbindAddress(InetAddress address)
+ throws IOException {
+ bindUnbindAddress(address, false);
+ localAddresses.remove(new InetSocketAddress(address, port));
+ return this;
+ }
+
+ private SctpChannel bindUnbindAddress(InetAddress address, boolean add)
+ throws IOException {
+ if (address == null)
+ throw new IllegalArgumentException();
+
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ throw new NotYetBoundException();
+ if (wildcard)
+ throw new IllegalStateException(
+ "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+ if (address.isAnyLocalAddress())
+ throw new IllegalArgumentException(
+ "Cannot add or remove the wildcard address");
+ if (add) {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ throw new AlreadyBoundException();
+ }
+ }
+ } else { /*removing */
+ /* Verify that there is more than one address
+ * and that address is already bound */
+ if (localAddresses.size() <= 1)
+ throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+ boolean foundAddress = false;
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ foundAddress = true;
+ break;
+ }
+ }
+ if (!foundAddress )
+ throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+ }
+
+ SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+ /* Update our internal Set to reflect the addition/removal */
+ if (add)
+ localAddresses.add(new InetSocketAddress(address, port));
+ else {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ localAddresses.remove(addr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ private boolean isBound() {
+ synchronized (stateLock) {
+ return port == -1 ? false : true;
+ }
+ }
+
+ private boolean isConnected() {
+ synchronized (stateLock) {
+ return (state == ChannelState.CONNECTED);
+ }
+ }
+
+ private void ensureOpenAndUnconnected() throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (isConnected())
+ throw new AlreadyConnectedException();
+ if (state == ChannelState.PENDING)
+ throw new ConnectionPendingException();
+ }
+ }
+
+ private boolean ensureReceiveOpen() throws ClosedChannelException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isConnected())
+ throw new NotYetConnectedException();
+ else
+ return true;
+ }
+ }
+
+ private void ensureSendOpen() throws ClosedChannelException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (isShutdown)
+ throw new ClosedChannelException();
+ if (!isConnected())
+ throw new NotYetConnectedException();
+ }
+ }
+
+ private void receiverCleanup() throws IOException {
+ synchronized (stateLock) {
+ receiverThread = 0;
+ if (state == ChannelState.KILLPENDING)
+ kill();
+ }
+ }
+
+ private void senderCleanup() throws IOException {
+ synchronized (stateLock) {
+ senderThread = 0;
+ if (state == ChannelState.KILLPENDING)
+ kill();
+ }
+ }
+
+ @Override
+ public Association association() throws ClosedChannelException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isConnected())
+ return null;
+
+ return association;
+ }
+ }
+
+ @Override
+ public boolean connect(SocketAddress endpoint) throws IOException {
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ ensureOpenAndUnconnected();
+ InetSocketAddress isa = Net.checkAddress(endpoint);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(isa.getAddress().getHostAddress(),
+ isa.getPort());
+ synchronized (blockingLock()) {
+ int n = 0;
+ try {
+ try {
+ begin();
+ synchronized (stateLock) {
+ if (!isOpen()) {
+ return false;
+ }
+ receiverThread = NativeThread.current();
+ }
+ for (;;) {
+ InetAddress ia = isa.getAddress();
+ if (ia.isAnyLocalAddress())
+ ia = InetAddress.getLocalHost();
+ n = Net.connect(fd, ia, isa.getPort());
+ if ( (n == IOStatus.INTERRUPTED)
+ && isOpen())
+ continue;
+ break;
+ }
+ } finally {
+ receiverCleanup();
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+ } catch (IOException x) {
+ /* If an exception was thrown, close the channel after
+ * invoking end() so as to avoid bogus
+ * AsynchronousCloseExceptions */
+ close();
+ throw x;
+ }
+
+ if (n > 0) {
+ synchronized (stateLock) {
+ /* Connection succeeded */
+ state = ChannelState.CONNECTED;
+ if (!isBound()) {
+ InetSocketAddress boundIsa =
+ Net.localAddress(fd);
+ port = boundIsa.getPort();
+ }
+
+ /* Receive COMM_UP */
+ ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+ try {
+ receive(buf, null, null, true);
+ } finally {
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
+ return true;
+ }
+ } else {
+ synchronized (stateLock) {
+ /* If nonblocking and no exception then connection
+ * pending; disallow another invocation */
+ if (!isBlocking())
+ state = ChannelState.PENDING;
+ else
+ assert false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean connect(SocketAddress endpoint,
+ int maxOutStreams,
+ int maxInStreams)
+ throws IOException {
+ return setOption(SCTP_INIT_MAXSTREAMS, InitMaxStreams.
+ create(maxInStreams, maxOutStreams)).connect(endpoint);
+
+ }
+
+ @Override
+ public boolean isConnectionPending() {
+ synchronized (stateLock) {
+ return (state == ChannelState.PENDING);
+ }
+ }
+
+ @Override
+ public boolean finishConnect() throws IOException {
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (isConnected())
+ return true;
+ if (state != ChannelState.PENDING)
+ throw new NoConnectionPendingException();
+ }
+ int n = 0;
+ try {
+ try {
+ begin();
+ synchronized (blockingLock()) {
+ synchronized (stateLock) {
+ if (!isOpen()) {
+ return false;
+ }
+ receiverThread = NativeThread.current();
+ }
+ if (!isBlocking()) {
+ for (;;) {
+ n = checkConnect(fd, false, readyToConnect);
+ if ( (n == IOStatus.INTERRUPTED)
+ && isOpen())
+ continue;
+ break;
+ }
+ } else {
+ for (;;) {
+ n = checkConnect(fd, true, readyToConnect);
+ if (n == 0) {
+ // Loop in case of
+ // spurious notifications
+ continue;
+ }
+ if ( (n == IOStatus.INTERRUPTED)
+ && isOpen())
+ continue;
+ break;
+ }
+ }
+ }
+ } finally {
+ synchronized (stateLock) {
+ receiverThread = 0;
+ if (state == ChannelState.KILLPENDING) {
+ kill();
+ /* poll()/getsockopt() does not report
+ * error (throws exception, with n = 0)
+ * on Linux platform after dup2 and
+ * signal-wakeup. Force n to 0 so the
+ * end() can throw appropriate exception */
+ n = 0;
+ }
+ }
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+ } catch (IOException x) {
+ /* If an exception was thrown, close the channel after
+ * invoking end() so as to avoid bogus
+ * AsynchronousCloseExceptions */
+ close();
+ throw x;
+ }
+
+ if (n > 0) {
+ synchronized (stateLock) {
+ state = ChannelState.CONNECTED;
+ if (!isBound()) {
+ InetSocketAddress boundIsa =
+ Net.localAddress(fd);
+ port = boundIsa.getPort();
+ }
+
+ /* Receive COMM_UP */
+ ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+ try {
+ receive(buf, null, null, true);
+ } finally {
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ IOUtil.configureBlocking(fd, block);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ synchronized (stateLock) {
+ nd.preClose(fd);
+
+ if (receiverThread != 0)
+ NativeThread.signal(receiverThread);
+
+ if (senderThread != 0)
+ NativeThread.signal(senderThread);
+
+ if (!isRegistered())
+ kill();
+ }
+ }
+
+ @Override
+ public FileDescriptor getFD() {
+ return fd;
+ }
+
+ @Override
+ public int getFDVal() {
+ return fdVal;
+ }
+
+ /**
+ * Translates native poll revent ops into a ready operation ops
+ */
+ private boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl sk) {
+ int intOps = sk.nioInterestOps();
+ int oldOps = sk.nioReadyOps();
+ int newOps = initialOps;
+
+ if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+ /* This should only happen if this channel is pre-closed while a
+ * selection operation is in progress
+ * ## Throw an error if this channel has not been pre-closed */
+ return false;
+ }
+
+ if ((ops & (PollArrayWrapper.POLLERR
+ | PollArrayWrapper.POLLHUP)) != 0) {
+ newOps = intOps;
+ sk.nioReadyOps(newOps);
+ /* No need to poll again in checkConnect,
+ * the error will be detected there */
+ readyToConnect = true;
+ return (newOps & ~oldOps) != 0;
+ }
+
+ if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+ ((intOps & SelectionKey.OP_READ) != 0) &&
+ isConnected())
+ newOps |= SelectionKey.OP_READ;
+
+ if (((ops & PollArrayWrapper.POLLCONN) != 0) &&
+ ((intOps & SelectionKey.OP_CONNECT) != 0) &&
+ ((state == ChannelState.UNCONNECTED) || (state == ChannelState.PENDING))) {
+ newOps |= SelectionKey.OP_CONNECT;
+ readyToConnect = true;
+ }
+
+ if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
+ ((intOps & SelectionKey.OP_WRITE) != 0) &&
+ isConnected())
+ newOps |= SelectionKey.OP_WRITE;
+
+ sk.nioReadyOps(newOps);
+ return (newOps & ~oldOps) != 0;
+ }
+
+ @Override
+ public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, sk.nioReadyOps(), sk);
+ }
+
+ @Override
+ @SuppressWarnings("all")
+ public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, 0, sk);
+ }
+
+ @Override
+ public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+ int newOps = 0;
+ if ((ops & SelectionKey.OP_READ) != 0)
+ newOps |= PollArrayWrapper.POLLIN;
+ if ((ops & SelectionKey.OP_WRITE) != 0)
+ newOps |= PollArrayWrapper.POLLOUT;
+ if ((ops & SelectionKey.OP_CONNECT) != 0)
+ newOps |= PollArrayWrapper.POLLCONN;
+ sk.selector.putEventOps(sk, newOps);
+ }
+
+ @Override
+ public void kill() throws IOException {
+ synchronized (stateLock) {
+ if (state == ChannelState.KILLED)
+ return;
+ if (state == ChannelState.UNINITIALIZED) {
+ state = ChannelState.KILLED;
+ return;
+ }
+ assert !isOpen() && !isRegistered();
+
+ /* Postpone the kill if there is a waiting reader
+ * or writer thread. */
+ if (receiverThread == 0 && senderThread == 0) {
+ nd.close(fd);
+ state = ChannelState.KILLED;
+ } else {
+ state = ChannelState.KILLPENDING;
+ }
+ }
+ }
+
+ @Override
+ public <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+ throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/);
+ }
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ return (T)SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/);
+ }
+ }
+
+ private static class DefaultOptionsHolder {
+ static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+ private static Set<SctpSocketOption<?>> defaultOptions() {
+ HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);
+ set.add(SCTP_DISABLE_FRAGMENTS);
+ set.add(SCTP_EXPLICIT_COMPLETE);
+ set.add(SCTP_FRAGMENT_INTERLEAVE);
+ set.add(SCTP_INIT_MAXSTREAMS);
+ set.add(SCTP_NODELAY);
+ set.add(SCTP_PRIMARY_ADDR);
+ set.add(SCTP_SET_PEER_PRIMARY_ADDR);
+ set.add(SO_SNDBUF);
+ set.add(SO_RCVBUF);
+ set.add(SO_LINGER);
+ return Collections.unmodifiableSet(set);
+ }
+ }
+
+ @Override
+ public final Set<SctpSocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
+ }
+
+ @Override
+ public <T> MessageInfo receive(ByteBuffer buffer,
+ T attachment,
+ NotificationHandler<T> handler)
+ throws IOException {
+ return receive(buffer, attachment, handler, false);
+ }
+
+ private <T> MessageInfo receive(ByteBuffer buffer,
+ T attachment,
+ NotificationHandler<T> handler,
+ boolean fromConnect)
+ throws IOException {
+ if (buffer == null)
+ throw new IllegalArgumentException("buffer cannot be null");
+
+ if (buffer.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+
+ if (receiveInvoked.get())
+ throw new IllegalReceiveException(
+ "cannot invoke receive from handler");
+ receiveInvoked.set(Boolean.TRUE);
+
+ try {
+ SctpResultContainer resultContainer = new SctpResultContainer();
+ do {
+ resultContainer.clear();
+ synchronized (receiveLock) {
+ if (!ensureReceiveOpen())
+ return null;
+
+ if (commUpResultContainer != null) {
+ resultContainer = commUpResultContainer;
+ commUpResultContainer = null;
+ continue;
+ }
+
+ int n = 0;
+ try {
+ begin();
+
+ synchronized (stateLock) {
+ if(!isOpen())
+ return null;
+ receiverThread = NativeThread.current();
+ }
+
+ do {
+ n = receive(fdVal, buffer, resultContainer);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ } finally {
+ receiverCleanup();
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+
+ if (!resultContainer.isNotification()) {
+ /* message or nothing */
+ if (resultContainer.hasSomething()) {
+ /* Set the association before returning */
+ SctpMessageInfoImpl info =
+ resultContainer.getMessageInfo();
+ synchronized (stateLock) {
+ assert association != null;
+ info.setAssociation(association);
+ }
+ return info;
+ } else
+ /* Non-blocking may return null if nothing available*/
+ return null;
+ } else { /* notification */
+ synchronized (stateLock) {
+ handleNotificationInternal(
+ resultContainer);
+ }
+ }
+
+ if (fromConnect) {
+ /* If we reach here, then it was connect that invoked
+ * receive an received the COMM_UP. Save it and allow
+ * the user handler to process it upon next receive. */
+ commUpResultContainer = resultContainer;
+ return null;
+ }
+ } /* receiveLock */
+ } while (handler == null ? true :
+ (invokeNotificationHandler(resultContainer, handler, attachment)
+ == HandlerResult.CONTINUE));
+
+ return null;
+ } finally {
+ receiveInvoked.set(Boolean.FALSE);
+ }
+ }
+
+ private int receive(int fd,
+ ByteBuffer dst,
+ SctpResultContainer resultContainer)
+ throws IOException {
+ int pos = dst.position();
+ int lim = dst.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+ if (dst instanceof DirectBuffer && rem > 0)
+ return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);
+
+ /* Substitute a native buffer */
+ int newSize = Math.max(rem, 1);
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
+ try {
+ int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);
+ bb.flip();
+ if (n > 0 && rem > 0)
+ dst.put(bb);
+ return n;
+ } finally {
+ Util.releaseTemporaryDirectBuffer(bb);
+ }
+ }
+
+ private int receiveIntoNativeBuffer(int fd,
+ SctpResultContainer resultContainer,
+ ByteBuffer bb,
+ int rem,
+ int pos)
+ throws IOException
+ {
+ int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);
+
+ if (n > 0)
+ bb.position(pos + n);
+ return n;
+ }
+
+ private InternalNotificationHandler<?> internalNotificationHandler =
+ new InternalNotificationHandler();
+
+ private void handleNotificationInternal(SctpResultContainer resultContainer)
+ {
+ invokeNotificationHandler(resultContainer,
+ internalNotificationHandler, null);
+ }
+
+ private class InternalNotificationHandler<T>
+ extends AbstractNotificationHandler<T>
+ {
+ @Override
+ public HandlerResult handleNotification(
+ AssociationChangeNotification not, T unused) {
+ if (not.event().equals(
+ AssociationChangeNotification.AssocChangeEvent.COMM_UP)) {
+ assert association == null;
+ SctpAssocChange sac = (SctpAssocChange) not;
+ association = new SctpAssociationImpl
+ (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());
+ }
+ return HandlerResult.CONTINUE;
+ }
+ }
+
+ private <T> HandlerResult invokeNotificationHandler
+ (SctpResultContainer resultContainer,
+ NotificationHandler<T> handler,
+ T attachment) {
+ SctpNotification notification = resultContainer.notification();
+ synchronized (stateLock) {
+ notification.setAssociation(association);
+ }
+
+ if (!(handler instanceof AbstractNotificationHandler)) {
+ return handler.handleNotification(notification, attachment);
+ }
+
+ /* AbstractNotificationHandler */
+ AbstractNotificationHandler absHandler =
+ (AbstractNotificationHandler)handler;
+ switch(resultContainer.type()) {
+ case ASSOCIATION_CHANGED :
+ return absHandler.handleNotification(
+ resultContainer.getAssociationChanged(), attachment);
+ case PEER_ADDRESS_CHANGED :
+ return absHandler.handleNotification(
+ resultContainer.getPeerAddressChanged(), attachment);
+ case SEND_FAILED :
+ return absHandler.handleNotification(
+ resultContainer.getSendFailed(), attachment);
+ case SHUTDOWN :
+ return absHandler.handleNotification(
+ resultContainer.getShutdown(), attachment);
+ default :
+ /* implementation specific handlers */
+ return absHandler.handleNotification(
+ resultContainer.notification(), attachment);
+ }
+ }
+
+ private void checkAssociation(Association sendAssociation) {
+ synchronized (stateLock) {
+ if (sendAssociation != null && !sendAssociation.equals(association)) {
+ throw new IllegalArgumentException(
+ "Cannot send to another association");
+ }
+ }
+ }
+
+ private void checkStreamNumber(int streamNumber) {
+ synchronized (stateLock) {
+ if (association != null) {
+ if (streamNumber < 0 ||
+ streamNumber >= association.maxOutboundStreams())
+ throw new InvalidStreamException();
+ }
+ }
+ }
+
+ /* TODO: Add support for ttl and isComplete to both 121 12M
+ * SCTP_EOR not yet supported on reference platforms
+ * TTL support limited...
+ */
+ @Override
+ public int send(ByteBuffer buffer, MessageInfo messageInfo)
+ throws IOException {
+ if (buffer == null)
+ throw new IllegalArgumentException("buffer cannot be null");
+
+ if (messageInfo == null)
+ throw new IllegalArgumentException("messageInfo cannot be null");
+
+ checkAssociation(messageInfo.association());
+ checkStreamNumber(messageInfo.streamNumber());
+
+ synchronized (sendLock) {
+ ensureSendOpen();
+
+ int n = 0;
+ try {
+ begin();
+
+ synchronized (stateLock) {
+ if(!isOpen())
+ return 0;
+ senderThread = NativeThread.current();
+ }
+
+ do {
+ n = send(fdVal, buffer, messageInfo);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+ return IOStatus.normalize(n);
+ } finally {
+ senderCleanup();
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+ }
+ }
+
+ private int send(int fd, ByteBuffer src, MessageInfo messageInfo)
+ throws IOException {
+ int streamNumber = messageInfo.streamNumber();
+ SocketAddress target = messageInfo.address();
+ boolean unordered = messageInfo.isUnordered();
+ int ppid = messageInfo.payloadProtocolID();
+ int pos = src.position();
+ int lim = src.limit();
+
+ assert (pos <= lim && streamNumber > 0);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ if (src instanceof DirectBuffer)
+ return sendFromNativeBuffer(fd, src, rem, pos, target, streamNumber,
+ unordered, ppid);
+
+ /* Substitute a native buffer */
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+ try {
+ bb.put(src);
+ bb.flip();
+ /* Do not update src until we see how many bytes were written */
+ src.position(pos);
+
+ int n = sendFromNativeBuffer(fd, bb, rem, pos, target, streamNumber,
+ unordered, ppid);
+ if (n > 0) {
+ /* now update src */
+ src.position(pos + n);
+ }
+ return n;
+ } finally {
+ Util.releaseTemporaryDirectBuffer(bb);
+ }
+ }
+
+ private int sendFromNativeBuffer(int fd,
+ ByteBuffer bb,
+ int rem,
+ int pos,
+ SocketAddress target,
+ int streamNumber,
+ boolean unordered,
+ int ppid)
+ throws IOException {
+ int written = send0(fd, ((DirectBuffer)bb).address() + pos,
+ rem, target, -1 /*121*/, streamNumber, unordered, ppid);
+ if (written > 0)
+ bb.position(pos + written);
+ return written;
+ }
+
+ @Override
+ public SctpChannel shutdown() throws IOException {
+ synchronized(stateLock) {
+ if (isShutdown)
+ return this;
+
+ ensureSendOpen();
+ SctpNet.shutdown(fdVal, -1);
+ if (senderThread != 0)
+ NativeThread.signal(senderThread);
+ isShutdown = true;
+ }
+ return this;
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ return Collections.EMPTY_SET;
+
+ return SctpNet.getLocalAddresses(fdVal);
+ }
+ }
+
+ @Override
+ public Set<SocketAddress> getRemoteAddresses()
+ throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isConnected())
+ return Collections.EMPTY_SET;
+
+ return SctpNet.getRemoteAddresses(fdVal, 0/*unused*/);
+ }
+ }
+
+ /* Native */
+ private static native void initIDs();
+
+ static native int receive0(int fd, SctpResultContainer resultContainer,
+ long address, int length) throws IOException;
+
+ static native int send0(int fd, long address, int length,
+ SocketAddress target, int assocId, int streamNumber,
+ boolean unordered, int ppid) throws IOException;
+
+ private static native int checkConnect(FileDescriptor fd, boolean block,
+ boolean ready) throws IOException;
+
+ static {
+ Util.load(); /* loads nio & net native libraries */
+ java.security.AccessController.doPrivileged(
+ new sun.security.action.LoadLibraryAction("sctp"));
+ initIDs();
+ nd = new SctpSocketDispatcher();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,959 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static sun.nio.ch.SctpResultContainer.*;
+
+/**
+ * An implementation of SctpMultiChannel
+ */
+public class SctpMultiChannelImpl extends SctpMultiChannel
+ implements SelChImpl
+{
+ /* Used to make native close and preClose calls */
+ private static NativeDispatcher nd;
+
+ private final FileDescriptor fd;
+
+ private final int fdVal;
+
+ /* IDs of native threads doing send and receives, for signalling */
+ private volatile long receiverThread = 0;
+ private volatile long senderThread = 0;
+
+ /* Lock held by current receiving thread */
+ private final Object receiveLock = new Object();
+
+ /* Lock held by current sending thread */
+ private final Object sendLock = new Object();
+
+ /* Lock held by any thread that modifies the state fields declared below
+ * DO NOT invoke a blocking I/O operation while holding this lock! */
+ private final Object stateLock = new Object();
+
+ private enum ChannelState {
+ UNINITIALIZED,
+ KILLPENDING,
+ KILLED,
+ }
+
+ /* -- The following fields are protected by stateLock -- */
+ private ChannelState state = ChannelState.UNINITIALIZED;
+
+ /* Binding: Once bound the port will remain constant. */
+ int port = -1;
+ private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+ /* Has the channel been bound to the wildcard address */
+ private boolean wildcard; /* false */
+
+ /* Keeps a map of addresses to association, and visa versa */
+ private HashMap<SocketAddress, Association> addressMap =
+ new HashMap<SocketAddress, Association>();
+ private HashMap<Association, Set<SocketAddress>> associationMap =
+ new HashMap<Association, Set<SocketAddress>>();
+
+ /* -- End of fields protected by stateLock -- */
+
+ /* If an association has been shutdown mark it for removal after
+ * the user handler has been invoked */
+ private final ThreadLocal<Association> associationToRemove =
+ new ThreadLocal<Association>() {
+ @Override protected Association initialValue() {
+ return null;
+ }
+ };
+
+ /* A notification handler cannot invoke receive */
+ private final ThreadLocal<Boolean> receiveInvoked =
+ new ThreadLocal<Boolean>() {
+ @Override protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ public SctpMultiChannelImpl(SelectorProvider provider)
+ throws IOException {
+ //TODO: update provider, remove public modifier
+ super(provider);
+ this.fd = SctpNet.socket(false /*one-to-many*/);
+ this.fdVal = IOUtil.fdVal(fd);
+ }
+
+ @Override
+ public SctpMultiChannel bind(SocketAddress local, int backlog)
+ throws IOException {
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ synchronized (stateLock) {
+ ensureOpen();
+ if (isBound())
+ throw new AlreadyBoundException();
+ InetSocketAddress isa = (local == null) ?
+ new InetSocketAddress(0) : Net.checkAddress(local);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkListen(isa.getPort());
+ Net.bind(fd, isa.getAddress(), isa.getPort());
+
+ InetSocketAddress boundIsa = Net.localAddress(fd);
+ port = boundIsa.getPort();
+ localAddresses.add(isa);
+ if (isa.getAddress().isAnyLocalAddress())
+ wildcard = true;
+
+ Net.listen(fd, backlog < 1 ? 50 : backlog);
+ }
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public SctpMultiChannel bindAddress(InetAddress address)
+ throws IOException {
+ return bindUnbindAddress(address, true);
+ }
+
+ @Override
+ public SctpMultiChannel unbindAddress(InetAddress address)
+ throws IOException {
+ return bindUnbindAddress(address, false);
+ }
+
+ private SctpMultiChannel bindUnbindAddress(InetAddress address,
+ boolean add)
+ throws IOException {
+ if (address == null)
+ throw new IllegalArgumentException();
+
+ synchronized (receiveLock) {
+ synchronized (sendLock) {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ throw new NotYetBoundException();
+ if (wildcard)
+ throw new IllegalStateException(
+ "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+ if (address.isAnyLocalAddress())
+ throw new IllegalArgumentException(
+ "Cannot add or remove the wildcard address");
+ if (add) {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ throw new AlreadyBoundException();
+ }
+ }
+ } else { /*removing */
+ /* Verify that there is more than one address
+ * and that address is already bound */
+ if (localAddresses.size() <= 1)
+ throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+ boolean foundAddress = false;
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ foundAddress = true;
+ break;
+ }
+ }
+ if (!foundAddress )
+ throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+ }
+
+ SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+ /* Update our internal Set to reflect the addition/removal */
+ if (add)
+ localAddresses.add(new InetSocketAddress(address, port));
+ else {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ localAddresses.remove(addr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public Set<Association> associations()
+ throws ClosedChannelException, NotYetBoundException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ throw new NotYetBoundException();
+
+ return Collections.unmodifiableSet(associationMap.keySet());
+ }
+ }
+
+ private boolean isBound() {
+ synchronized (stateLock) {
+ return port == -1 ? false : true;
+ }
+ }
+
+ private void ensureOpen() throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ }
+ }
+
+ private void receiverCleanup() throws IOException {
+ synchronized (stateLock) {
+ receiverThread = 0;
+ if (state == ChannelState.KILLPENDING)
+ kill();
+ }
+ }
+
+ private void senderCleanup() throws IOException {
+ synchronized (stateLock) {
+ senderThread = 0;
+ if (state == ChannelState.KILLPENDING)
+ kill();
+ }
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ IOUtil.configureBlocking(fd, block);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ synchronized (stateLock) {
+ nd.preClose(fd);
+
+ if (receiverThread != 0)
+ NativeThread.signal(receiverThread);
+
+ if (senderThread != 0)
+ NativeThread.signal(senderThread);
+
+ if (!isRegistered())
+ kill();
+ }
+ }
+
+ @Override
+ public FileDescriptor getFD() {
+ return fd;
+ }
+
+ @Override
+ public int getFDVal() {
+ return fdVal;
+ }
+
+ /**
+ * Translates native poll revent ops into a ready operation ops
+ */
+ private boolean translateReadyOps(int ops, int initialOps,
+ SelectionKeyImpl sk) {
+ int intOps = sk.nioInterestOps();
+ int oldOps = sk.nioReadyOps();
+ int newOps = initialOps;
+
+ if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+ /* This should only happen if this channel is pre-closed while a
+ * selection operation is in progress
+ * ## Throw an error if this channel has not been pre-closed */
+ return false;
+ }
+
+ if ((ops & (PollArrayWrapper.POLLERR
+ | PollArrayWrapper.POLLHUP)) != 0) {
+ newOps = intOps;
+ sk.nioReadyOps(newOps);
+ return (newOps & ~oldOps) != 0;
+ }
+
+ if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+ ((intOps & SelectionKey.OP_READ) != 0))
+ newOps |= SelectionKey.OP_READ;
+
+ if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
+ ((intOps & SelectionKey.OP_WRITE) != 0))
+ newOps |= SelectionKey.OP_WRITE;
+
+ sk.nioReadyOps(newOps);
+ return (newOps & ~oldOps) != 0;
+ }
+
+ @Override
+ public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, sk.nioReadyOps(), sk);
+ }
+
+ @Override
+ public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, 0, sk);
+ }
+
+ @Override
+ public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+ int newOps = 0;
+ if ((ops & SelectionKey.OP_READ) != 0)
+ newOps |= PollArrayWrapper.POLLIN;
+ if ((ops & SelectionKey.OP_WRITE) != 0)
+ newOps |= PollArrayWrapper.POLLOUT;
+ sk.selector.putEventOps(sk, newOps);
+ }
+
+ @Override
+ public void kill() throws IOException {
+ synchronized (stateLock) {
+ if (state == ChannelState.KILLED)
+ return;
+ if (state == ChannelState.UNINITIALIZED) {
+ state = ChannelState.KILLED;
+ return;
+ }
+ assert !isOpen() && !isRegistered();
+
+ /* Postpone the kill if there is a thread sending or receiving. */
+ if (receiverThread == 0 && senderThread == 0) {
+ nd.close(fd);
+ state = ChannelState.KILLED;
+ } else {
+ state = ChannelState.KILLPENDING;
+ }
+ }
+ }
+
+ @Override
+ public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+ T value,
+ Association association)
+ throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!(supportedOptions().contains(name)))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||
+ name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {
+ checkAssociation(association);
+ }
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ SctpNet.setSocketOption(fdVal, name, value,
+ association.associationID());
+ }
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getOption(SctpSocketOption<T> name, Association association)
+ throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ checkAssociation(association);
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ return (T)SctpNet.getSocketOption(fdVal, name,
+ association.associationID());
+ }
+ }
+
+ private static class DefaultOptionsHolder {
+ static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+ private static Set<SctpSocketOption<?>> defaultOptions() {
+ HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);
+ set.add(SCTP_DISABLE_FRAGMENTS);
+ set.add(SCTP_EXPLICIT_COMPLETE);
+ set.add(SCTP_FRAGMENT_INTERLEAVE);
+ set.add(SCTP_INIT_MAXSTREAMS);
+ set.add(SCTP_NODELAY);
+ set.add(SCTP_PRIMARY_ADDR);
+ set.add(SCTP_SET_PEER_PRIMARY_ADDR);
+ set.add(SO_SNDBUF);
+ set.add(SO_RCVBUF);
+ set.add(SO_LINGER);
+ return Collections.unmodifiableSet(set);
+ }
+ }
+
+ @Override
+ public final Set<SctpSocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
+ }
+
+ @Override
+ public <T> MessageInfo receive(ByteBuffer buffer,
+ T attachment,
+ NotificationHandler<T> handler)
+ throws IOException {
+ if (buffer == null)
+ throw new IllegalArgumentException("buffer cannot be null");
+
+ if (buffer.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+
+ if (receiveInvoked.get())
+ throw new IllegalReceiveException(
+ "cannot invoke receive from handler");
+ receiveInvoked.set(Boolean.TRUE);
+
+ try {
+ SctpResultContainer resultContainer = new SctpResultContainer();
+ do {
+ resultContainer.clear();
+ synchronized (receiveLock) {
+ ensureOpen();
+ if (!isBound())
+ throw new NotYetBoundException();
+
+ int n = 0;
+ try {
+ begin();
+
+ synchronized (stateLock) {
+ if(!isOpen())
+ return null;
+ receiverThread = NativeThread.current();
+ }
+
+ do {
+ n = receive(fdVal, buffer, resultContainer);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+ } finally {
+ receiverCleanup();
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+
+ if (!resultContainer.isNotification()) {
+ /* message or nothing */
+ if (resultContainer.hasSomething()) {
+ /* Set the association before returning */
+ SctpMessageInfoImpl info =
+ resultContainer.getMessageInfo();
+ info.setAssociation(lookupAssociation(info.
+ associationID()));
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ InetSocketAddress isa = (InetSocketAddress)info.address();
+ if (!addressMap.containsKey(isa)) {
+ /* must be a new association */
+ try {
+ sm.checkAccept(isa.getAddress().getHostAddress(),
+ isa.getPort());
+ } catch (SecurityException se) {
+ buffer.clear();
+ throw se;
+ }
+ }
+ }
+
+ assert info.association() != null;
+ return info;
+ } else {
+ /* Non-blocking may return null if nothing available*/
+ return null;
+ }
+ } else { /* notification */
+ synchronized (stateLock) {
+ handleNotificationInternal(
+ resultContainer);
+ }
+ }
+ } /* receiveLock */
+ } while (handler == null ? true :
+ (invokeNotificationHandler(resultContainer, handler, attachment)
+ == HandlerResult.CONTINUE));
+ } finally {
+ receiveInvoked.set(Boolean.FALSE);
+ }
+
+ return null;
+ }
+
+ private int receive(int fd,
+ ByteBuffer dst,
+ SctpResultContainer resultContainer)
+ throws IOException {
+ int pos = dst.position();
+ int lim = dst.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+ if (dst instanceof DirectBuffer && rem > 0)
+ return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);
+
+ /* Substitute a native buffer. */
+ int newSize = Math.max(rem, 1);
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
+ try {
+ int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);
+ bb.flip();
+ if (n > 0 && rem > 0)
+ dst.put(bb);
+ return n;
+ } finally {
+ Util.releaseTemporaryDirectBuffer(bb);
+ }
+ }
+
+ private int receiveIntoNativeBuffer(int fd,
+ SctpResultContainer resultContainer,
+ ByteBuffer bb,
+ int rem,
+ int pos)
+ throws IOException {
+ int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);
+ if (n > 0)
+ bb.position(pos + n);
+ return n;
+ }
+
+ private InternalNotificationHandler internalNotificationHandler =
+ new InternalNotificationHandler();
+
+ private void handleNotificationInternal(SctpResultContainer resultContainer)
+ {
+ invokeNotificationHandler(resultContainer,
+ internalNotificationHandler, null);
+ }
+
+ private class InternalNotificationHandler<T>
+ extends AbstractNotificationHandler<T>
+ {
+ @Override
+ public HandlerResult handleNotification(
+ AssociationChangeNotification not, T unused) {
+ SctpAssocChange sac = (SctpAssocChange) not;
+
+ /* Update map to reflect change in association */
+ switch (not.event()) {
+ case COMM_UP :
+ Association newAssociation = new SctpAssociationImpl
+ (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());
+ addAssociation(newAssociation);
+ break;
+ case SHUTDOWN :
+ case COMM_LOST :
+ //case RESTART: ???
+ /* mark association for removal after user handler invoked*/
+ associationToRemove.set(lookupAssociation(sac.assocId()));
+ }
+ return HandlerResult.CONTINUE;
+ }
+ }
+
+ private <T> HandlerResult invokeNotificationHandler(
+ SctpResultContainer resultContainer,
+ NotificationHandler<T> handler,
+ T attachment) {
+ HandlerResult result;
+ SctpNotification notification = resultContainer.notification();
+ notification.setAssociation(lookupAssociation(notification.assocId()));
+
+ if (!(handler instanceof AbstractNotificationHandler)) {
+ result = handler.handleNotification(notification, attachment);
+ } else { /* AbstractNotificationHandler */
+ AbstractNotificationHandler absHandler =
+ (AbstractNotificationHandler)handler;
+ switch(resultContainer.type()) {
+ case ASSOCIATION_CHANGED :
+ result = absHandler.handleNotification(
+ resultContainer.getAssociationChanged(), attachment);
+ case PEER_ADDRESS_CHANGED :
+ result = absHandler.handleNotification(
+ resultContainer.getPeerAddressChanged(), attachment);
+ case SEND_FAILED :
+ result = absHandler.handleNotification(
+ resultContainer.getSendFailed(), attachment);
+ case SHUTDOWN :
+ result = absHandler.handleNotification(
+ resultContainer.getShutdown(), attachment);
+ default :
+ /* implementation specific handlers */
+ result = absHandler.handleNotification(
+ resultContainer.notification(), attachment);
+ }
+ }
+
+ if (!(handler instanceof InternalNotificationHandler)) {
+ /* Only remove associations after user handler
+ * has finished with them */
+ Association assoc = associationToRemove.get();
+ if (assoc != null) {
+ removeAssociation(assoc);
+ associationToRemove.set(null);
+ }
+
+ }
+
+ return result;
+ }
+
+ private Association lookupAssociation(int assocId) {
+ /* Lookup the association in our internal map */
+ synchronized (stateLock) {
+ Set<Association> assocs = associationMap.keySet();
+ for (Association a : assocs) {
+ if (a.associationID() == assocId) {
+ return a;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void addAssociation(Association association) {
+ synchronized (stateLock) {
+ int assocId = association.associationID();
+ Set<SocketAddress> addresses = null;
+
+ try {
+ addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
+ } catch (IOException unused) {
+ /* OK, determining connected addresses may not be possible
+ * shutdown, connection lost, etc */
+ }
+
+ associationMap.put(association, addresses);
+ if (addresses != null) {
+ for (SocketAddress addr : addresses)
+ addressMap.put(addr, association);
+ }
+ }
+ }
+
+ private void removeAssociation(Association association) {
+ synchronized (stateLock) {
+ int assocId = association.associationID();
+ Set<SocketAddress> addresses = null;
+
+ try {
+ addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
+ } catch (IOException unused) {
+ /* OK, determining connected addresses may not be possible
+ * shutdown, connection lost, etc */
+ }
+
+ Set<Association> assocs = associationMap.keySet();
+ for (Association a : assocs) {
+ if (a.associationID() == assocId) {
+ associationMap.remove(a);
+ break;
+ }
+ }
+ if (addresses != null) {
+ for (SocketAddress addr : addresses)
+ addressMap.remove(addr);
+ } else {
+ /* We cannot determine the connected addresses */
+ Set<java.util.Map.Entry<SocketAddress, Association>> addrAssocs =
+ addressMap.entrySet();
+ for (java.util.Map.Entry<SocketAddress, Association> entry : addrAssocs) {
+ if (entry.getValue().equals(association)) {
+ addressMap.remove(entry.getKey());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @throws IllegalArgumentException
+ * If the given association is not controlled by this channel
+ *
+ * @return {@code true} if, and only if, the given association is one
+ * of the current associations controlled by this channel
+ */
+ private boolean checkAssociation(Association messageAssoc) {
+ synchronized (stateLock) {
+ for (Association association : associationMap.keySet()) {
+ if (messageAssoc.equals(association)) {
+ return true;
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "Given Association is not controlled by this channel");
+ }
+
+ private void checkStreamNumber(Association assoc, int streamNumber) {
+ synchronized (stateLock) {
+ if (streamNumber < 0 || streamNumber >= assoc.maxOutboundStreams())
+ throw new InvalidStreamException();
+ }
+ }
+
+ /* TODO: Add support for ttl and isComplete to both 121 12M
+ * SCTP_EOR not yet supported on reference platforms
+ * TTL support limited...
+ */
+ @Override
+ public int send(ByteBuffer buffer, MessageInfo messageInfo)
+ throws IOException {
+ if (buffer == null)
+ throw new IllegalArgumentException("buffer cannot be null");
+
+ if (messageInfo == null)
+ throw new IllegalArgumentException("messageInfo cannot be null");
+
+ synchronized (sendLock) {
+ ensureOpen();
+
+ if (!isBound())
+ bind(null, 0);
+
+ int n = 0;
+ try {
+ int assocId = -1;
+ SocketAddress address = null;
+ begin();
+
+ synchronized (stateLock) {
+ if(!isOpen())
+ return 0;
+ senderThread = NativeThread.current();
+
+ /* Determine what address or association to send to */
+ Association assoc = messageInfo.association();
+ InetSocketAddress addr = (InetSocketAddress)messageInfo.address();
+ if (assoc != null) {
+ checkAssociation(assoc);
+ checkStreamNumber(assoc, messageInfo.streamNumber());
+ assocId = assoc.associationID();
+ /* have we also got a preferred address */
+ if (addr != null) {
+ if (!assoc.equals(addressMap.get(addr)))
+ throw new IllegalArgumentException("given preferred address is not part of this association");
+ address = addr;
+ }
+ } else if (addr != null) {
+ address = addr;
+ Association association = addressMap.get(addr);
+ if (association != null) {
+ checkStreamNumber(association, messageInfo.streamNumber());
+ assocId = association.associationID();
+
+ } else { /* must be new association */
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(addr.getAddress().getHostAddress(),
+ addr.getPort());
+ }
+ } else {
+ throw new AssertionError(
+ "Both association and address cannot be null");
+ }
+ }
+
+ do {
+ n = send(fdVal, buffer, assocId, address, messageInfo);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+ return IOStatus.normalize(n);
+ } finally {
+ senderCleanup();
+ end((n > 0) || (n == IOStatus.UNAVAILABLE));
+ assert IOStatus.check(n);
+ }
+ }
+ }
+
+ private int send(int fd,
+ ByteBuffer src,
+ int assocId,
+ SocketAddress target,
+ MessageInfo messageInfo)
+ throws IOException {
+ int streamNumber = messageInfo.streamNumber();
+ boolean unordered = messageInfo.isUnordered();
+ int ppid = messageInfo.payloadProtocolID();
+ int pos = src.position();
+ int lim = src.limit();
+ assert (pos <= lim && streamNumber > 0);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ if (src instanceof DirectBuffer)
+ return sendFromNativeBuffer(fd, src, rem, pos, target, assocId,
+ streamNumber, unordered, ppid);
+
+ /* Substitute a native buffer */
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+ try {
+ bb.put(src);
+ bb.flip();
+ /* Do not update src until we see how many bytes were written */
+ src.position(pos);
+
+ int n = sendFromNativeBuffer(fd, bb, rem, pos, target, assocId,
+ streamNumber, unordered, ppid);
+ if (n > 0) {
+ /* now update src */
+ src.position(pos + n);
+ }
+ return n;
+ } finally {
+ Util.releaseTemporaryDirectBuffer(bb);
+ }
+ }
+
+ private int sendFromNativeBuffer(int fd,
+ ByteBuffer bb,
+ int rem,
+ int pos,
+ SocketAddress target,
+ int assocId,
+ int streamNumber,
+ boolean unordered,
+ int ppid)
+ throws IOException {
+ int written = send0(fd, ((DirectBuffer)bb).address() + pos,
+ rem, target, assocId, streamNumber, unordered, ppid);
+ if (written > 0)
+ bb.position(pos + written);
+ return written;
+ }
+
+ @Override
+ public SctpMultiChannel shutdown(Association association)
+ throws IOException {
+ synchronized (stateLock) {
+ checkAssociation(association);
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ SctpNet.shutdown(fdVal, association.associationID());
+ }
+ return this;
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ return Collections.EMPTY_SET;
+
+ return SctpNet.getLocalAddresses(fdVal);
+ }
+ }
+
+ @Override
+ public Set<SocketAddress> getRemoteAddresses(Association association)
+ throws IOException {
+ synchronized (stateLock) {
+ checkAssociation(association);
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ return SctpNet.getRemoteAddresses(fdVal, association.associationID());
+ }
+ }
+
+ @Override
+ public SctpChannel branch(Association association)
+ throws IOException {
+ synchronized (stateLock) {
+ return null; //TODO: implement
+ }
+ }
+
+ /* Use common native implementation shared between
+ * one-to-one and one-to-many */
+ private static int receive0(int fd,
+ SctpResultContainer resultContainer,
+ long address,
+ int length)
+ throws IOException{
+ return SctpChannelImpl.receive0(fd, resultContainer, address,
+ length);
+ }
+
+ private static int send0(int fd,
+ long address,
+ int length,
+ SocketAddress target,
+ int assocId,
+ int streamNumber,
+ boolean unordered,
+ int ppid)
+ throws IOException {
+ return SctpChannelImpl.send0(fd, address, length, target, assocId,
+ streamNumber, unordered, ppid);
+ }
+
+ static {
+ Util.load(); /* loads nio & net native libraries */
+ java.security.AccessController.doPrivileged(
+ new sun.security.action.LoadLibraryAction("sctp"));
+ nd = new SctpSocketDispatcher();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Set;
+import java.util.HashSet;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+
+public class SctpNet {
+ static final String osName = AccessController.doPrivileged(
+ new GetPropertyAction("os.name"));
+
+ /* -- Miscellaneous SCTP utilities -- */
+
+ static boolean bindxIPv4MappedAddresses() {
+ if ("SunOS".equals(osName)) {
+ /* Solaris supports IPv4Mapped Addresses with bindx */
+ return true;
+ } /* else { //other OS/implementations */
+
+ /* lksctp/linux requires Ipv4 addresses */
+ return false;
+ }
+
+ /**
+ * @param oneToone
+ * if {@code true} returns a one-to-one sctp socket, otherwise
+ * returns a one-to-many sctp socket
+ */
+ static FileDescriptor socket(boolean oneToOne) throws IOException {
+ int nativefd = socket0(oneToOne);
+ return IOUtil.newFD(nativefd);
+ }
+
+ static void bindx(int fd, InetAddress[] addrs, int port, boolean add)
+ throws IOException {
+ bindx(fd, addrs, port, addrs.length, add,
+ bindxIPv4MappedAddresses());
+ }
+
+ static Set<SocketAddress> getLocalAddresses(int fd)
+ throws IOException {
+ HashSet<SocketAddress> set = null;
+ SocketAddress[] saa = getLocalAddresses0(fd);
+
+ if (saa != null) {
+ set = new HashSet<SocketAddress>(saa.length);
+ for (SocketAddress sa : saa)
+ set.add(sa);
+ }
+
+ return set;
+ }
+
+ static Set<SocketAddress> getRemoteAddresses(int fd, int assocId)
+ throws IOException {
+ HashSet<SocketAddress> set = null;
+ SocketAddress[] saa = getRemoteAddresses0(fd, assocId);
+
+ if (saa != null) {
+ set = new HashSet<SocketAddress>(saa.length);
+ for (SocketAddress sa : saa)
+ set.add(sa);
+ }
+
+ return set;
+ }
+
+ static void setSocketOption(int fd,
+ SctpSocketOption name,
+ Object value,
+ int assocId)
+ throws IOException {
+ if (value == null)
+ throw new IllegalArgumentException("Invalid option value");
+
+ Class<?> type = name.type();
+ if (!type.isInstance(value))
+ throw new IllegalArgumentException("Invalid option value");
+
+ if (name.equals(SCTP_INIT_MAXSTREAMS)) {
+ InitMaxStreams maxStreamValue = (InitMaxStreams)value;
+ SctpNet.setInitMsgOption0(fd,
+ maxStreamValue.maxInStreams(), maxStreamValue.maxOutStreams());
+ } else if (name.equals(SCTP_PRIMARY_ADDR) ||
+ name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
+
+ SocketAddress addr = (SocketAddress) value;
+ if (addr == null)
+ throw new IllegalArgumentException("Invalid option value");
+
+ Net.checkAddress(addr);
+ InetSocketAddress netAddr = (InetSocketAddress)addr;
+
+ if (name.equals(SCTP_PRIMARY_ADDR)) {
+ setPrimAddrOption0(fd, assocId,
+ netAddr.getAddress(), netAddr.getPort());
+ } else {
+ setPeerPrimAddrOption0(fd, assocId,
+ netAddr.getAddress(), netAddr.getPort());
+ }
+ } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
+ name.equals(SCTP_EXPLICIT_COMPLETE) ||
+ name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
+ name.equals(SCTP_NODELAY) ||
+ name.equals(SO_SNDBUF) ||
+ name.equals(SO_RCVBUF) ||
+ name.equals(SO_LINGER)) {
+ setIntOption(fd, name, value);
+ } else {
+ throw new AssertionError("Unknown socket option");
+ }
+ }
+
+ static Object getSocketOption(int fd, SctpSocketOption name, int assocId)
+ throws IOException {
+ if (name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
+ throw new IllegalArgumentException(
+ "SCTP_SET_PEER_PRIMARY_ADDR cannot be retrieved");
+ } else if (name.equals(SCTP_INIT_MAXSTREAMS)) {
+ /* container for holding maxIn/Out streams */
+ int[] values = new int[2];
+ SctpNet.getInitMsgOption0(fd, values);
+ return InitMaxStreams.create(values[0], values[1]);
+ } else if (name.equals(SCTP_PRIMARY_ADDR)) {
+ return getPrimAddrOption0(fd, assocId);
+ } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
+ name.equals(SCTP_EXPLICIT_COMPLETE) ||
+ name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
+ name.equals(SCTP_NODELAY) ||
+ name.equals(SO_SNDBUF) ||
+ name.equals(SO_RCVBUF) ||
+ name.equals(SO_LINGER)) {
+ return getIntOption(fd, name);
+ } else {
+ throw new AssertionError("Unknown socket option");
+ }
+ }
+
+ static void setIntOption(int fd, SctpSocketOption name, Object value)
+ throws IOException {
+ if (value == null)
+ throw new IllegalArgumentException("Invalid option value");
+
+ Class<?> type = name.type();
+ if (type != Integer.class && type != Boolean.class)
+ throw new AssertionError("Should not reach here");
+
+ if (name == SO_RCVBUF ||
+ name == SO_SNDBUF)
+ {
+ int i = ((Integer)value).intValue();
+ if (i < 0)
+ throw new IllegalArgumentException(
+ "Invalid send/receive buffer size");
+ } else if (name == SO_LINGER) {
+ int i = ((Integer)value).intValue();
+ if (i < 0)
+ value = Integer.valueOf(-1);
+ if (i > 65535)
+ value = Integer.valueOf(65535);
+ } else if (name.equals(SCTP_FRAGMENT_INTERLEAVE)) {
+ int i = ((Integer)value).intValue();
+ if (i < 0 || i > 2)
+ throw new IllegalArgumentException(
+ "Invalid value for SCTP_FRAGMENT_INTERLEAVE");
+ }
+
+ int arg;
+ if (type == Integer.class) {
+ arg = ((Integer)value).intValue();
+ } else {
+ boolean b = ((Boolean)value).booleanValue();
+ arg = (b) ? 1 : 0;
+ }
+
+ setIntOption0(fd, ((SctpStdSocketOption)name).constValue(), arg);
+ }
+
+ static Object getIntOption(int fd, SctpSocketOption name)
+ throws IOException {
+ Class<?> type = name.type();
+
+ if (type != Integer.class && type != Boolean.class)
+ throw new AssertionError("Should not reach here");
+
+ if (!(name instanceof SctpStdSocketOption))
+ throw new AssertionError("Should not reach here");
+
+ int value = getIntOption0(fd,
+ ((SctpStdSocketOption)name).constValue());
+
+ if (type == Integer.class) {
+ return Integer.valueOf(value);
+ } else {
+ return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
+ }
+ }
+
+ static void shutdown(int fd, int assocId)
+ throws IOException {
+ shutdown0(fd, assocId);
+ }
+
+ /* Native Methods */
+ static native int socket0(boolean oneToOne) throws IOException;
+
+ static native void bindx(int fd, InetAddress[] addrs, int port, int length,
+ boolean add, boolean preferIPv6) throws IOException;
+
+ static native int getIntOption0(int fd, int opt) throws IOException;
+
+ static native void setIntOption0(int fd, int opt, int arg)
+ throws IOException;
+
+ static native SocketAddress[] getLocalAddresses0(int fd) throws IOException;
+
+ static native SocketAddress[] getRemoteAddresses0(int fd, int assocId)
+ throws IOException;
+
+ static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia,
+ int port) throws IOException;
+
+ static native void setPeerPrimAddrOption0(int fd, int assocId,
+ InetAddress ia, int port) throws IOException;
+
+ static native SocketAddress getPrimAddrOption0(int fd, int assocId)
+ throws IOException;
+
+ /* retVals [0] maxInStreams, [1] maxOutStreams */
+ static native void getInitMsgOption0(int fd, int[] retVals) throws IOException;
+
+ static native void setInitMsgOption0(int fd, int arg1, int arg2)
+ throws IOException;
+
+ static native void shutdown0(int fd, int assocId);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNotification.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.Notification;
+
+/**
+ * All Notification implemenations MUST implement this interface to provide
+ * access to the native association identidier.
+ */
+interface SctpNotification extends Notification {
+ int assocId();
+ void setAssociation(Association association);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.PeerAddressChangeNotification;
+
+/**
+ * An implementation of PeerAddressChangeNotification
+ */
+public class SctpPeerAddrChange extends PeerAddressChangeNotification
+ implements SctpNotification
+{
+ /* static final ints so that they can be referenced from native */
+ private final static int SCTP_ADDR_AVAILABLE = 1;
+ private final static int SCTP_ADDR_UNREACHABLE = 2;
+ private final static int SCTP_ADDR_REMOVED = 3;
+ private final static int SCTP_ADDR_ADDED = 4;
+ private final static int SCTP_ADDR_MADE_PRIM = 5;
+ private final static int SCTP_ADDR_CONFIRMED =6;
+
+ private Association association;
+
+ /* assocId is used to lookup the association before the notification is
+ * returned to user code */
+ private int assocId;
+ private SocketAddress address;
+ private AddressChangeEvent event;
+
+ /* Invoked from native */
+ private SctpPeerAddrChange(int assocId, SocketAddress address, int intEvent) {
+ switch (intEvent) {
+ case SCTP_ADDR_AVAILABLE :
+ this.event = AddressChangeEvent.ADDR_AVAILABLE;
+ break;
+ case SCTP_ADDR_UNREACHABLE :
+ this.event = AddressChangeEvent.ADDR_UNREACHABLE;
+ break;
+ case SCTP_ADDR_REMOVED :
+ this.event = AddressChangeEvent.ADDR_REMOVED;
+ break;
+ case SCTP_ADDR_ADDED :
+ this.event = AddressChangeEvent.ADDR_ADDED;
+ break;
+ case SCTP_ADDR_MADE_PRIM :
+ this.event = AddressChangeEvent.ADDR_MADE_PRIMARY;
+ break;
+ case SCTP_ADDR_CONFIRMED :
+ this.event = AddressChangeEvent.ADDR_CONFIRMED;
+ break;
+ default:
+ throw new AssertionError("Unknown event type");
+ }
+ this.assocId = assocId;
+ this.address = address;
+ }
+
+ @Override
+ public int assocId() {
+ return assocId;
+ }
+
+ @Override
+ public void setAssociation(Association association) {
+ this.association = association;
+ }
+
+ @Override
+ public SocketAddress address() {
+ assert address != null;
+ return address;
+ }
+
+ @Override
+ public Association association() {
+ assert association != null;
+ return association;
+ }
+
+ @Override
+ public AddressChangeEvent event() {
+ assert event != null;
+ return event;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString()).append(" [");
+ sb.append("Address: ").append(address);
+ sb.append(", Association:").append(association);
+ sb.append(", Event: ").append(event).append("]");
+ return sb.toString();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpResultContainer.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+/**
+ * Wraps the actual message or notification so that it can be
+ * set and returned from the native receive implementation.
+ */
+public class SctpResultContainer {
+ /* static final ints so that they can be referenced from native */
+ static final int NOTHING = 0;
+ static final int MESSAGE = 1;
+ static final int SEND_FAILED = 2;
+ static final int ASSOCIATION_CHANGED = 3;
+ static final int PEER_ADDRESS_CHANGED = 4;
+ static final int SHUTDOWN = 5;
+
+ private Object value;
+ private int type;
+
+ int type() {
+ return type;
+ }
+
+ boolean hasSomething() {
+ return type() != NOTHING;
+ }
+
+ boolean isNotification() {
+ return type() != MESSAGE && type() != NOTHING ? true : false;
+ }
+
+ void clear() {
+ type = NOTHING;
+ value = null;
+ }
+
+ SctpNotification notification() {
+ assert type() != MESSAGE && type() != NOTHING;
+
+ return (SctpNotification) value;
+ }
+
+ SctpMessageInfoImpl getMessageInfo() {
+ assert type() == MESSAGE;
+
+ if (value instanceof SctpMessageInfoImpl)
+ return (SctpMessageInfoImpl) value;
+
+ return null;
+ }
+
+ SctpSendFailed getSendFailed() {
+ assert type() == SEND_FAILED;
+
+ if (value instanceof SctpSendFailed)
+ return (SctpSendFailed) value;
+
+ return null;
+ }
+
+ SctpAssocChange getAssociationChanged() {
+ assert type() == ASSOCIATION_CHANGED;
+
+ if (value instanceof SctpAssocChange)
+ return (SctpAssocChange) value;
+
+ return null;
+ }
+
+ SctpPeerAddrChange getPeerAddressChanged() {
+ assert type() == PEER_ADDRESS_CHANGED;
+
+ if (value instanceof SctpPeerAddrChange)
+ return (SctpPeerAddrChange) value;
+
+ return null;
+ }
+
+ SctpShutdown getShutdown() {
+ assert type() == SHUTDOWN;
+
+ if (value instanceof SctpShutdown)
+ return (SctpShutdown) value;
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Type: ");
+ switch (type) {
+ case NOTHING: sb.append("NOTHING"); break;
+ case MESSAGE: sb.append("MESSAGE"); break;
+ case SEND_FAILED: sb.append("SEND FAILED"); break;
+ case ASSOCIATION_CHANGED: sb.append("ASSOCIATION CHANGE"); break;
+ case PEER_ADDRESS_CHANGED: sb.append("PEER ADDRESS CHANGE"); break;
+ case SHUTDOWN: sb.append("SHUTDOWN"); break;
+ default : sb.append("Unknown result type");
+ }
+ return sb.append(", Value: ").append(value.toString()).toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpSendFailed.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.nio.ByteBuffer;
+import java.net.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.SendFailedNotification;
+
+/**
+ * An implementation of SendFailedNotification
+ */
+public class SctpSendFailed extends SendFailedNotification
+ implements SctpNotification
+{
+ private Association association;
+ /* assocId is used to lookup the association before the notification is
+ * returned to user code */
+ private int assocId;
+ private SocketAddress address;
+ private ByteBuffer buffer;
+ private int errorCode;
+ private int streamNumber;
+
+ /* Invoked from native */
+ private SctpSendFailed(int assocId,
+ SocketAddress address,
+ ByteBuffer buffer,
+ int errorCode,
+ int streamNumber) {
+ this.assocId = assocId;
+ this.errorCode = errorCode;
+ this.streamNumber = streamNumber;
+ this.address = address;
+ this.buffer = buffer;
+ }
+
+ @Override
+ public int assocId() {
+ return assocId;
+ }
+
+ @Override
+ public void setAssociation(Association association) {
+ this.association = association;
+ }
+
+ @Override
+ public Association association() {
+ /* may be null */
+ return association;
+ }
+
+ @Override
+ public SocketAddress address() {
+ assert address != null;
+ return address;
+ }
+
+ @Override
+ public ByteBuffer buffer() {
+ assert buffer != null;
+ return buffer;
+ }
+
+ @Override
+ public int errorCode() {
+ return errorCode;
+ }
+
+ @Override
+ public int streamNumber() {
+ return streamNumber;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString()).append(" [");
+ sb.append("Association:").append(association);
+ sb.append(", Address: ").append(address);
+ sb.append(", buffer: ").append(buffer);
+ sb.append(", errorCode: ").append(errorCode);
+ sb.append(", streamNumber: ").append(streamNumber);
+ sb.append("]");
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.net.InetAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import com.sun.nio.sctp.SctpStandardSocketOption;
+
+/**
+ * An implementation of SctpServerChannel
+ */
+public class SctpServerChannelImpl extends SctpServerChannel
+ implements SelChImpl
+{
+ /* Used to make native close and preClose calls */
+ private static NativeDispatcher nd;
+
+ private final FileDescriptor fd;
+
+ private final int fdVal;
+
+ /* IDs of native thread doing accept, for signalling */
+ private volatile long thread = 0;
+
+ /* Lock held by thread currently blocked in this channel */
+ private final Object lock = new Object();
+
+ /* Lock held by any thread that modifies the state fields declared below
+ * DO NOT invoke a blocking I/O operation while holding this lock! */
+ private final Object stateLock = new Object();
+
+ private enum ChannelState {
+ UNINITIALIZED,
+ INUSE,
+ KILLPENDING,
+ KILLED,
+ }
+ /* -- The following fields are protected by stateLock -- */
+ private ChannelState state = ChannelState.UNINITIALIZED;
+
+ /* Binding: Once bound the port will remain constant. */
+ int port = -1;
+ private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+ /* Has the channel been bound to the wildcard address */
+ private boolean wildcard; /* false */
+
+ /* -- End of fields protected by stateLock -- */
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ public SctpServerChannelImpl(SelectorProvider provider)
+ throws IOException {
+ //TODO: update provider remove public modifier
+ super(provider);
+ this.fd = SctpNet.socket(true);
+ this.fdVal = IOUtil.fdVal(fd);
+ this.state = ChannelState.INUSE;
+ }
+
+ @Override
+ public SctpServerChannel bind(SocketAddress local, int backlog)
+ throws IOException {
+ synchronized (lock) {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (isBound())
+ throw new AlreadyBoundException();
+
+ InetSocketAddress isa = (local == null) ?
+ new InetSocketAddress(0) : Net.checkAddress(local);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkListen(isa.getPort());
+ Net.bind(fd, isa.getAddress(), isa.getPort());
+
+ InetSocketAddress boundIsa = Net.localAddress(fd);
+ port = boundIsa.getPort();
+ localAddresses.add(isa);
+ if (isa.getAddress().isAnyLocalAddress())
+ wildcard = true;
+
+ Net.listen(fd, backlog < 1 ? 50 : backlog);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public SctpServerChannel bindAddress(InetAddress address)
+ throws IOException {
+ return bindUnbindAddress(address, true);
+ }
+
+ @Override
+ public SctpServerChannel unbindAddress(InetAddress address)
+ throws IOException {
+ return bindUnbindAddress(address, false);
+ }
+
+ private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add)
+ throws IOException {
+ if (address == null)
+ throw new IllegalArgumentException();
+
+ synchronized (lock) {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ throw new NotYetBoundException();
+ if (wildcard)
+ throw new IllegalStateException(
+ "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+ if (address.isAnyLocalAddress())
+ throw new IllegalArgumentException(
+ "Cannot add or remove the wildcard address");
+ if (add) {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ throw new AlreadyBoundException();
+ }
+ }
+ } else { /*removing */
+ /* Verify that there is more than one address
+ * and that address is already bound */
+ if (localAddresses.size() <= 1)
+ throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+ boolean foundAddress = false;
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ foundAddress = true;
+ break;
+ }
+ }
+ if (!foundAddress )
+ throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+ }
+
+ SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+ /* Update our internal Set to reflect the addition/removal */
+ if (add)
+ localAddresses.add(new InetSocketAddress(address, port));
+ else {
+ for (InetSocketAddress addr : localAddresses) {
+ if (addr.getAddress().equals(address)) {
+ localAddresses.remove(addr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ private boolean isBound() {
+ synchronized (stateLock) {
+ return port == -1 ? false : true;
+ }
+ }
+
+ private void acceptCleanup() throws IOException {
+ synchronized (stateLock) {
+ thread = 0;
+ if (state == ChannelState.KILLPENDING)
+ kill();
+ }
+ }
+
+ @Override
+ public SctpChannel accept() throws IOException {
+ synchronized (lock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ throw new NotYetBoundException();
+ SctpChannel sc = null;
+
+ int n = 0;
+ FileDescriptor newfd = new FileDescriptor();
+ InetSocketAddress[] isaa = new InetSocketAddress[1];
+
+ try {
+ begin();
+ if (!isOpen())
+ return null;
+ thread = NativeThread.current();
+ for (;;) {
+ n = accept0(fd, newfd, isaa);
+ if ((n == IOStatus.INTERRUPTED) && isOpen())
+ continue;
+ break;
+ }
+ } finally {
+ acceptCleanup();
+ end(n > 0);
+ assert IOStatus.check(n);
+ }
+
+ if (n < 1)
+ return null;
+
+ IOUtil.configureBlocking(newfd, true);
+ InetSocketAddress isa = isaa[0];
+ sc = new SctpChannelImpl(provider(), newfd);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkAccept(isa.getAddress().getHostAddress(),
+ isa.getPort());
+
+ return sc;
+ }
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ IOUtil.configureBlocking(fd, block);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ synchronized (stateLock) {
+ nd.preClose(fd);
+ if (thread != 0)
+ NativeThread.signal(thread);
+ if (!isRegistered())
+ kill();
+ }
+ }
+
+ @Override
+ public void kill() throws IOException {
+ synchronized (stateLock) {
+ if (state == ChannelState.KILLED)
+ return;
+ if (state == ChannelState.UNINITIALIZED) {
+ state = ChannelState.KILLED;
+ return;
+ }
+ assert !isOpen() && !isRegistered();
+
+ // Postpone the kill if there is a thread in accept
+ if (thread == 0) {
+ nd.close(fd);
+ state = ChannelState.KILLED;
+ } else {
+ state = ChannelState.KILLPENDING;
+ }
+ }
+ }
+
+ @Override
+ public FileDescriptor getFD() {
+ return fd;
+ }
+
+ @Override
+ public int getFDVal() {
+ return fdVal;
+ }
+
+ /**
+ * Translates native poll revent ops into a ready operation ops
+ */
+ private boolean translateReadyOps(int ops, int initialOps,
+ SelectionKeyImpl sk) {
+ int intOps = sk.nioInterestOps();
+ int oldOps = sk.nioReadyOps();
+ int newOps = initialOps;
+
+ if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+ /* This should only happen if this channel is pre-closed while a
+ * selection operation is in progress
+ * ## Throw an error if this channel has not been pre-closed */
+ return false;
+ }
+
+ if ((ops & (PollArrayWrapper.POLLERR
+ | PollArrayWrapper.POLLHUP)) != 0) {
+ newOps = intOps;
+ sk.nioReadyOps(newOps);
+ return (newOps & ~oldOps) != 0;
+ }
+
+ if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+ ((intOps & SelectionKey.OP_ACCEPT) != 0))
+ newOps |= SelectionKey.OP_ACCEPT;
+
+ sk.nioReadyOps(newOps);
+ return (newOps & ~oldOps) != 0;
+ }
+
+ @Override
+ public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, sk.nioReadyOps(), sk);
+ }
+
+ @Override
+ public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+ return translateReadyOps(ops, 0, sk);
+ }
+
+ @Override
+ public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+ int newOps = 0;
+
+ /* Translate ops */
+ if ((ops & SelectionKey.OP_ACCEPT) != 0)
+ newOps |= PollArrayWrapper.POLLIN;
+ /* Place ops into pollfd array */
+ sk.selector.putEventOps(sk, newOps);
+
+ }
+
+ @Override
+ public <T> SctpServerChannel setOption(SctpSocketOption<T> name, T value)
+ throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/);
+ return this;
+ }
+ }
+
+ @Override
+ public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/);
+ }
+ }
+
+ private static class DefaultOptionsHolder {
+ static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+ private static Set<SctpSocketOption<?>> defaultOptions() {
+ HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(1);
+ set.add(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS);
+ return Collections.unmodifiableSet(set);
+ }
+ }
+
+ @Override
+ public final Set<SctpSocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ synchronized (stateLock) {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ if (!isBound())
+ return null;
+
+ return SctpNet.getLocalAddresses(fdVal);
+ }
+ }
+
+ /* Native */
+ private static native void initIDs();
+
+ private static native int accept0(FileDescriptor ssfd,
+ FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException;
+
+ static {
+ Util.load(); // loads nio & net native libraries
+ java.security.AccessController.doPrivileged(
+ new sun.security.action.LoadLibraryAction("sctp"));
+ nd = new SctpSocketDispatcher();
+ initIDs();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpShutdown.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.ShutdownNotification;
+
+/**
+ * An implementation of ShutdownNotification
+ */
+public class SctpShutdown extends ShutdownNotification
+ implements SctpNotification
+{
+ private Association association;
+ /* assocId is used to lookup the association before the notification is
+ * returned to user code */
+ private int assocId;
+
+ /* Invoked from native */
+ private SctpShutdown(int assocId) {
+ this.assocId = assocId;
+ }
+
+ @Override
+ public int assocId() {
+ return assocId;
+ }
+
+ @Override
+ public void setAssociation(Association association) {
+ this.association = association;
+ }
+
+ @Override
+ public Association association() {
+ assert association != null;
+ return association;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString()).append(" [");
+ sb.append("Association:").append(association).append("]");
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+
+/**
+ * Only used for {@code close} and {@code preclose}. All other methods
+ * throw {@code IOException}.
+ */
+class SctpSocketDispatcher extends NativeDispatcher {
+ @Override
+ @SuppressWarnings("unused")
+ int read(FileDescriptor fd, long address, int len) throws IOException {
+ throw new IOException("Operation Unsupported");
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ long readv(FileDescriptor fd, long address, int len) throws IOException {
+ throw new IOException("Operation Unsupported");
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ int write(FileDescriptor fd, long address, int len) throws IOException {
+ throw new IOException("Operation Unsupported");
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ long writev(FileDescriptor fd, long address, int len) throws IOException {
+ throw new IOException("Operation Unsupported");
+ }
+
+ @Override
+ void close(FileDescriptor fd) throws IOException {
+ FileDispatcherImpl.close0(fd);
+ }
+
+ @Override
+ void preClose(FileDescriptor fd) throws IOException {
+ FileDispatcherImpl.preClose0(fd);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/Sctp.h Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+#ifndef SUN_NIO_CH_SCTP_H
+#define SUN_NIO_CH_SCTP_H
+
+#ifdef __solaris__
+
+#define _XPG4_2
+#define __EXTENSIONS__
+#include <sys/socket.h>
+#include <netinet/sctp.h>
+#include "jni.h"
+
+/* Current Solaris headers don't comply with draft rfc */
+#ifndef SCTP_EOF
+#define SCTP_EOF MSG_EOF
+#endif
+
+#ifndef SCTP_UNORDERED
+#define SCTP_UNORDERED MSG_UNORDERED
+#endif
+
+/* The current version of the socket API extension shipped with Solaris does
+ * not define the following options that the Java API (optionally) supports */
+#ifndef SCTP_EXPLICIT_EOR
+#define SCTP_EXPLICIT_EOR -1
+#endif
+#ifndef SCTP_FRAGMENT_INTERLEAVE
+#define SCTP_FRAGMENT_INTERLEAVE -1
+#endif
+#ifndef SCTP_SET_PEER_PRIMARY_ADDR
+#define SCTP_SET_PEER_PRIMARY_ADDR -1
+#endif
+
+/* Function types to support dynamic linking of socket API extension functions
+ * for SCTP. This is so that there is no linkage depandancy during build or
+ * runtime for libsctp.*/
+typedef int sctp_getladdrs_func(int sock, sctp_assoc_t id, void **addrs);
+typedef int sctp_freeladdrs_func(void* addrs);
+typedef int sctp_getpaddrs_func(int sock, sctp_assoc_t id, void **addrs);
+typedef int sctp_freepaddrs_func(void *addrs);
+typedef int sctp_bindx_func(int sock, void *addrs, int addrcnt, int flags);
+
+
+#else /* __linux__ */
+#include <stdint.h>
+#include <linux/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "jni.h"
+
+//Causes compiler error if not found, should make warning and uncomment
+/*#include <netinet/sctp.h>*/
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+/* The current version of lksctp does
+ * not define the following option that the Java API (optionally) supports */
+#ifndef SCTP_EXPLICIT_EOR
+#define SCTP_EXPLICIT_EOR -1
+#endif
+
+/* Definitions taken from lksctp-tools-1.0.8/src/include/netinet/sctp.h */
+#ifndef SCTP_INITMSG
+
+enum sctp_optname {
+ SCTP_RTOINFO,
+#define SCTP_RTOINFO SCTP_RTOINFO
+ SCTP_ASSOCINFO,
+#define SCTP_ASSOCINFO SCTP_ASSOCINFO
+ SCTP_INITMSG,
+#define SCTP_INITMSG SCTP_INITMSG
+ SCTP_NODELAY, /* Get/set nodelay option. */
+#define SCTP_NODELAY SCTP_NODELAY
+ SCTP_AUTOCLOSE,
+#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE
+ SCTP_SET_PEER_PRIMARY_ADDR,
+#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR
+ SCTP_PRIMARY_ADDR,
+#define SCTP_PRIMARY_ADDR SCTP_PRIMARY_ADDR
+ SCTP_ADAPTATION_LAYER,
+#define SCTP_ADAPTATION_LAYER SCTP_ADAPTATION_LAYER
+ SCTP_DISABLE_FRAGMENTS,
+#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS
+ SCTP_PEER_ADDR_PARAMS,
+#define SCTP_PEER_ADDR_PARAMS SCTP_PEER_ADDR_PARAMS
+ SCTP_DEFAULT_SEND_PARAM,
+#define SCTP_DEFAULT_SEND_PARAM SCTP_DEFAULT_SEND_PARAM
+ SCTP_EVENTS,
+#define SCTP_EVENTS SCTP_EVENTS
+ SCTP_I_WANT_MAPPED_V4_ADDR, /* Turn on/off mapped v4 addresses */
+#define SCTP_I_WANT_MAPPED_V4_ADDR SCTP_I_WANT_MAPPED_V4_ADDR
+ SCTP_MAXSEG, /* Get/set maximum fragment. */
+#define SCTP_MAXSEG SCTP_MAXSEG
+ SCTP_STATUS,
+#define SCTP_STATUS SCTP_STATUS
+ SCTP_GET_PEER_ADDR_INFO,
+#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
+ SCTP_DELAYED_ACK_TIME,
+#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME
+ SCTP_CONTEXT, /* Receive Context */
+#define SCTP_CONTEXT SCTP_CONTEXT
+ SCTP_FRAGMENT_INTERLEAVE,
+#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE
+ SCTP_PARTIAL_DELIVERY_POINT, /* Set/Get partial delivery point */
+#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT
+ SCTP_MAX_BURST, /* Set/Get max burst */
+#define SCTP_MAX_BURST SCTP_MAX_BURST
+};
+
+enum sctp_sac_state {
+ SCTP_COMM_UP,
+ SCTP_COMM_LOST,
+ SCTP_RESTART,
+ SCTP_SHUTDOWN_COMP,
+ SCTP_CANT_STR_ASSOC,
+};
+
+enum sctp_spc_state {
+ SCTP_ADDR_AVAILABLE,
+ SCTP_ADDR_UNREACHABLE,
+ SCTP_ADDR_REMOVED,
+ SCTP_ADDR_ADDED,
+ SCTP_ADDR_MADE_PRIM,
+ SCTP_ADDR_CONFIRMED,
+};
+
+enum sctp_sinfo_flags {
+ SCTP_UNORDERED = 1, /* Send/receive message unordered. */
+ SCTP_ADDR_OVER = 2, /* Override the primary destination. */
+ SCTP_ABORT=4, /* Send an ABORT message to the peer. */
+ SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */
+};
+
+enum sctp_sn_type {
+ SCTP_SN_TYPE_BASE = (1<<15),
+ SCTP_ASSOC_CHANGE,
+ SCTP_PEER_ADDR_CHANGE,
+ SCTP_SEND_FAILED,
+ SCTP_REMOTE_ERROR,
+ SCTP_SHUTDOWN_EVENT,
+ SCTP_PARTIAL_DELIVERY_EVENT,
+ SCTP_ADAPTATION_INDICATION,
+};
+
+typedef enum sctp_cmsg_type {
+ SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */
+#define SCTP_INIT SCTP_INIT
+ SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */
+#define SCTP_SNDRCV SCTP_SNDRCV
+} sctp_cmsg_t;
+
+enum sctp_msg_flags {
+ MSG_NOTIFICATION = 0x8000,
+#define MSG_NOTIFICATION MSG_NOTIFICATION
+};
+
+#define SCTP_BINDX_ADD_ADDR 0x01
+#define SCTP_BINDX_REM_ADDR 0x02
+
+typedef __s32 sctp_assoc_t;
+
+struct sctp_initmsg {
+ __u16 sinit_num_ostreams;
+ __u16 sinit_max_instreams;
+ __u16 sinit_max_attempts;
+ __u16 sinit_max_init_timeo;
+};
+
+struct sctp_sndrcvinfo {
+ __u16 sinfo_stream;
+ __u16 sinfo_ssn;
+ __u16 sinfo_flags;
+ __u32 sinfo_ppid;
+ __u32 sinfo_context;
+ __u32 sinfo_timetolive;
+ __u32 sinfo_tsn;
+ __u32 sinfo_cumtsn;
+ sctp_assoc_t sinfo_assoc_id;
+};
+
+struct sctp_event_subscribe {
+ __u8 sctp_data_io_event;
+ __u8 sctp_association_event;
+ __u8 sctp_address_event;
+ __u8 sctp_send_failure_event;
+ __u8 sctp_peer_error_event;
+ __u8 sctp_shutdown_event;
+ __u8 sctp_partial_delivery_event;
+ __u8 sctp_adaptation_layer_event;
+};
+
+struct sctp_send_failed {
+ __u16 ssf_type;
+ __u16 ssf_flags;
+ __u32 ssf_length;
+ __u32 ssf_error;
+ struct sctp_sndrcvinfo ssf_info;
+ sctp_assoc_t ssf_assoc_id;
+ __u8 ssf_data[0];
+};
+
+struct sctp_assoc_change {
+ __u16 sac_type;
+ __u16 sac_flags;
+ __u32 sac_length;
+ __u16 sac_state;
+ __u16 sac_error;
+ __u16 sac_outbound_streams;
+ __u16 sac_inbound_streams;
+ sctp_assoc_t sac_assoc_id;
+ __u8 sac_info[0];
+};
+
+struct sctp_shutdown_event {
+ __u16 sse_type;
+ __u16 sse_flags;
+ __u32 sse_length;
+ sctp_assoc_t sse_assoc_id;
+};
+
+struct sctp_paddr_change {
+ __u16 spc_type;
+ __u16 spc_flags;
+ __u32 spc_length;
+ struct sockaddr_storage spc_aaddr;
+ int spc_state;
+ int spc_error;
+ sctp_assoc_t spc_assoc_id;
+} __attribute__((packed, aligned(4)));
+
+struct sctp_remote_error {
+ __u16 sre_type;
+ __u16 sre_flags;
+ __u32 sre_length;
+ __u16 sre_error;
+ sctp_assoc_t sre_assoc_id;
+ __u8 sre_data[0];
+};
+
+struct sctp_adaptation_event {
+ __u16 sai_type;
+ __u16 sai_flags;
+ __u32 sai_length;
+ __u32 sai_adaptation_ind;
+ sctp_assoc_t sai_assoc_id;
+};
+
+struct sctp_setprim {
+ sctp_assoc_t ssp_assoc_id;
+ struct sockaddr_storage ssp_addr;
+} __attribute__((packed, aligned(4)));
+
+struct sctp_setpeerprim {
+ sctp_assoc_t sspp_assoc_id;
+ struct sockaddr_storage sspp_addr;
+} __attribute__((packed, aligned(4)));
+
+
+struct sctp_pdapi_event {
+ __u16 pdapi_type;
+ __u16 pdapi_flags;
+ __u32 pdapi_length;
+ __u32 pdapi_indication;
+ sctp_assoc_t pdapi_assoc_id;
+};
+
+union sctp_notification {
+ struct {
+ __u16 sn_type; /* Notification type. */
+ __u16 sn_flags;
+ __u32 sn_length;
+ } sn_header;
+ struct sctp_assoc_change sn_assoc_change;
+ struct sctp_paddr_change sn_paddr_change;
+ struct sctp_remote_error sn_remote_error;
+ struct sctp_send_failed sn_send_failed;
+ struct sctp_shutdown_event sn_shutdown_event;
+ struct sctp_adaptation_event sn_adaptation_event;
+ struct sctp_pdapi_event sn_pdapi_event;
+};
+
+#endif /* SCTP_INITMSG */
+
+/* Function types to support dynamic linking of socket API extension functions
+ * for SCTP. This is so that there is no linkage depandancy during build or
+ * runtime for libsctp.*/
+typedef int sctp_getladdrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
+typedef int sctp_freeladdrs_func(struct sockaddr *addrs);
+typedef int sctp_getpaddrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
+typedef int sctp_freepaddrs_func(struct sockaddr *addrs);
+typedef int sctp_bindx_func(int sd, struct sockaddr *addrs, int addrcnt, int flags);
+
+#endif /* __linux__ */
+
+sctp_getladdrs_func* nio_sctp_getladdrs;
+sctp_freeladdrs_func* nio_sctp_freeladdrs;
+sctp_getpaddrs_func* nio_sctp_getpaddrs;
+sctp_freepaddrs_func* nio_sctp_freepaddrs;
+sctp_bindx_func* nio_sctp_bindx;
+
+jboolean loadSocketExtensionFuncs(JNIEnv* env);
+
+#endif /* !SUN_NIO_CH_SCTP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpChannelImpl.c Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "Sctp.h"
+
+#include "jni.h"
+#include "nio_util.h"
+#include "nio.h"
+#include "net_util.h"
+#include "net_util_md.h"
+#include "sun_nio_ch_SctpNet.h"
+#include "sun_nio_ch_SctpChannelImpl.h"
+#include "sun_nio_ch_SctpAssocChange.h"
+#include "sun_nio_ch_SctpResultContainer.h"
+#include "sun_nio_ch_SctpPeerAddrChange.h"
+
+/* sizeof(union sctp_notification */
+#define NOTIFICATION_BUFFER_SIZE 280
+
+#define MESSAGE_IMPL_CLASS "sun/nio/ch/SctpMessageInfoImpl"
+#define RESULT_CONTAINER_CLASS "sun/nio/ch/SctpResultContainer"
+#define SEND_FAILED_CLASS "sun/nio/ch/SctpSendFailed"
+#define ASSOC_CHANGE_CLASS "sun/nio/ch/SctpAssocChange"
+#define PEER_CHANGE_CLASS "sun/nio/ch/SctpPeerAddrChange"
+#define SHUTDOWN_CLASS "sun/nio/ch/SctpShutdown"
+
+struct controlData {
+ int assocId;
+ unsigned short streamNumber;
+ jboolean unordered;
+ unsigned int ppid;
+};
+
+static jclass smi_class; /* sun.nio.ch.SctpMessageInfoImpl */
+static jmethodID smi_ctrID; /* sun.nio.ch.SctpMessageInfoImpl.<init> */
+static jfieldID src_valueID; /* sun.nio.ch.SctpResultContainer.value */
+static jfieldID src_typeID; /* sun.nio.ch.SctpResultContainer.type */
+static jclass ssf_class; /* sun.nio.ch.SctpSendFailed */
+static jmethodID ssf_ctrID; /* sun.nio.ch.SctpSendFailed.<init> */
+static jclass sac_class; /* sun.nio.ch.SctpAssociationChanged */
+static jmethodID sac_ctrID; /* sun.nio.ch.SctpAssociationChanged.<init> */
+static jclass spc_class; /* sun.nio.ch.SctpPeerAddressChanged */
+static jmethodID spc_ctrID; /* sun.nio.ch.SctpPeerAddressChanged.<init> */
+static jclass ss_class; /* sun.nio.ch.SctpShutdown */
+static jmethodID ss_ctrID; /* sun.nio.ch.SctpShutdown.<init> */
+static jfieldID isa_addrID; /* java.net.InetSocketAddress.addr */
+static jfieldID isa_portID; /* java.net.InetSocketAddress.port */
+
+/* defined in SctpNet.c */
+jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr);
+
+/* use SocketChannelImpl's checkConnect implementation */
+extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env,
+ jobject this, jobject fdo, jboolean block, jboolean ready);
+
+/*
+ * Class: sun_nio_ch_SctpChannelImpl
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpChannelImpl_initIDs
+ (JNIEnv *env, jclass klass) {
+ jclass cls;
+
+ /* SctpMessageInfoImpl */
+ cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS);
+ CHECK_NULL(cls);
+ smi_class = (*env)->NewGlobalRef(env, cls);
+ CHECK_NULL(smi_class);
+ smi_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+ "(ILjava/net/SocketAddress;IIZZI)V");
+ CHECK_NULL(smi_ctrID);
+
+ /* SctpResultContainer */
+ cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS);
+ CHECK_NULL(cls);
+ src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;");
+ CHECK_NULL(src_valueID);
+ src_typeID = (*env)->GetFieldID(env, cls, "type", "I");
+ CHECK_NULL(src_typeID);
+
+ /* SctpSendFailed */
+ cls = (*env)->FindClass(env, SEND_FAILED_CLASS);
+ CHECK_NULL(cls);
+ ssf_class = (*env)->NewGlobalRef(env, cls);
+ CHECK_NULL(ssf_class);
+ ssf_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+ "(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V");
+ CHECK_NULL(ssf_ctrID);
+
+ /* SctpAssocChange */
+ cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS);
+ CHECK_NULL(cls);
+ sac_class = (*env)->NewGlobalRef(env, cls);
+ CHECK_NULL(sac_class);
+ sac_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(IIII)V");
+ CHECK_NULL(sac_ctrID);
+
+ /* SctpPeerAddrChange */
+ cls = (*env)->FindClass(env, PEER_CHANGE_CLASS);
+ CHECK_NULL(cls);
+ spc_class = (*env)->NewGlobalRef(env, cls);
+ CHECK_NULL(spc_class);
+ spc_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+ "(ILjava/net/SocketAddress;I)V");
+ CHECK_NULL(spc_ctrID);
+
+ /* sun.nio.ch.SctpShutdown */
+ cls = (*env)->FindClass(env, SHUTDOWN_CLASS);
+ CHECK_NULL(cls);
+ ss_class = (*env)->NewGlobalRef(env, cls);
+ CHECK_NULL(ss_class);
+ ss_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(I)V");
+ CHECK_NULL(ss_ctrID);
+
+ /* InetSocketAddress */
+ cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
+ CHECK_NULL(cls);
+ isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;");
+ CHECK_NULL(isa_addrID);
+ isa_portID = (*env)->GetFieldID(env, cls, "port", "I");
+}
+
+void getControlData
+ (struct msghdr* msg, struct controlData* cdata) {
+ struct cmsghdr* cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) {
+ struct sctp_sndrcvinfo *sri;
+
+ sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg);
+ cdata->assocId = sri->sinfo_assoc_id;
+ cdata->streamNumber = sri->sinfo_stream;
+ cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE :
+ JNI_FALSE;
+ cdata->ppid = ntohl(sri->sinfo_ppid);
+
+ return;
+ }
+ }
+ return;
+}
+
+void setControlData
+ (struct msghdr* msg, struct controlData* cdata) {
+ struct cmsghdr* cmsg;
+ struct sctp_sndrcvinfo *sri;
+
+ cmsg = CMSG_FIRSTHDR(msg);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+ /* Initialize the payload */
+ sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
+ memset(sri, 0, sizeof (*sri));
+
+ if (cdata->streamNumber > 0) {
+ sri->sinfo_stream = cdata->streamNumber;
+ }
+ if (cdata->assocId > 0) {
+ sri->sinfo_assoc_id = cdata->assocId;
+ }
+ if (cdata->unordered == JNI_TRUE) {
+ sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED;
+ }
+
+ if (cdata->ppid > 0) {
+ sri->sinfo_ppid = htonl(cdata->ppid);
+ }
+
+ /* Sum of the length of all control messages in the buffer. */
+ msg->msg_controllen = cmsg->cmsg_len;
+}
+
+// TODO: test: can create send failed without any data? if so need to
+// update API so that buffer can be null if no data.
+void handleSendFailed
+ (JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf,
+ int read, jboolean isEOR, struct sockaddr* sap) {
+ jobject bufferObj = NULL, resultObj, isaObj;
+ char *addressP;
+ struct sctp_sndrcvinfo *sri;
+ int remaining, dataLength;
+
+ /* the actual undelivered message data is directly after the ssf */
+ int dataOffset = sizeof(struct sctp_send_failed);
+
+ sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info;
+
+ /* the number of bytes remaining to be read in the sctp_send_failed notif*/
+ remaining = ssf->ssf_length - read;
+
+ /* the size of the actual undelivered message */
+ dataLength = ssf->ssf_length - dataOffset;
+
+ /* retrieved address from sockaddr */
+ isaObj = SockAddrToInetSocketAddress(env, sap);
+
+ /* data retrieved from sff_data */
+ if (dataLength > 0) {
+ struct iovec iov[1];
+ struct msghdr msg[1];
+ int rv, alreadyRead;
+ char *dataP = (char*) ssf;
+ dataP += dataOffset;
+
+ if ((addressP = malloc(dataLength)) == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "handleSendFailed");
+ return;
+ }
+
+ memset(msg, 0, sizeof (*msg));
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+
+ bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength);
+ CHECK_NULL(bufferObj);
+
+ alreadyRead = read - dataOffset;
+ if (alreadyRead > 0) {
+ memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead);
+ iov->iov_base = addressP + alreadyRead;
+ iov->iov_len = dataLength - alreadyRead;
+ } else {
+ iov->iov_base = addressP;
+ iov->iov_len = dataLength;
+ }
+
+ if (remaining > 0) {
+ if ((rv = recvmsg(fd, msg, 0)) < 0) {
+ fprintf(stdout, "\nNative: handleSFN: recvmsg failed: errno = %d ", errno);
+ handleSocketError(env, errno);
+ return;
+ }
+
+ if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) {
+ //TODO: assert false: "should not reach here";
+ return;
+ }
+ // TODO: Set and document (in API) buffers position.
+ }
+ }
+
+ /* create SctpSendFailed */
+ resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id,
+ sri->sinfo_stream, ssf->ssf_error, isaObj, bufferObj);
+ CHECK_NULL(resultObj);
+ (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+ (*env)->SetIntField(env, resultContainerObj, src_typeID,
+ sun_nio_ch_SctpResultContainer_SEND_FAILED);
+}
+
+void handleAssocChange
+ (JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) {
+ jobject resultObj;
+ int state = 0;
+
+ switch (sac->sac_state) {
+ case SCTP_COMM_UP :
+ state = sun_nio_ch_SctpAssocChange_SCTP_COMM_UP;
+ break;
+ case SCTP_COMM_LOST :
+ state = sun_nio_ch_SctpAssocChange_SCTP_COMM_LOST;
+ break;
+ case SCTP_RESTART :
+ state = sun_nio_ch_SctpAssocChange_SCTP_RESTART;
+ break;
+ case SCTP_SHUTDOWN_COMP :
+ state = sun_nio_ch_SctpAssocChange_SCTP_SHUTDOWN;
+ break;
+ case SCTP_CANT_STR_ASSOC :
+ state = sun_nio_ch_SctpAssocChange_SCTP_CANT_START;
+ }
+
+ /* create SctpAssociationChanged */
+ resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id,
+ state, sac->sac_outbound_streams, sac->sac_inbound_streams);
+ CHECK_NULL(resultObj);
+ (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+ (*env)->SetIntField(env, resultContainerObj, src_typeID,
+ sun_nio_ch_SctpResultContainer_ASSOCIATION_CHANGED);
+}
+
+void handleShutdown
+ (JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) {
+ /* create SctpShutdown */
+ jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id);
+ CHECK_NULL(resultObj);
+ (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+ (*env)->SetIntField(env, resultContainerObj, src_typeID,
+ sun_nio_ch_SctpResultContainer_SHUTDOWN);
+}
+
+void handlePeerAddrChange
+ (JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) {
+ int event = 0;
+ jobject addressObj, resultObj;
+ unsigned int state = spc->spc_state;
+
+ switch (state) {
+ case SCTP_ADDR_AVAILABLE :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE;
+ break;
+ case SCTP_ADDR_UNREACHABLE :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE;
+ break;
+ case SCTP_ADDR_REMOVED :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_REMOVED;
+ break;
+ case SCTP_ADDR_ADDED :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_ADDED;
+ break;
+ case SCTP_ADDR_MADE_PRIM :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM;
+#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */
+ break;
+ case SCTP_ADDR_CONFIRMED :
+ event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED;
+#endif /* __linux__ */
+ }
+
+ addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr);
+
+ /* create SctpPeerAddressChanged */
+ resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id,
+ addressObj, event);
+ CHECK_NULL(resultObj);
+ (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+ (*env)->SetIntField(env, resultContainerObj, src_typeID,
+ sun_nio_ch_SctpResultContainer_PEER_ADDRESS_CHANGED);
+}
+
+void handleUninteresting
+ (union sctp_notification *snp) {
+ //fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type);
+}
+
+/**
+ * Handle notifications from the SCTP stack.
+ * Returns JNI_TRUE if the notification is one that is of interest to the
+ * Java API, otherwise JNI_FALSE.
+ */
+jboolean handleNotification
+ (JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp,
+ int read, jboolean isEOR, struct sockaddr* sap) {
+ switch (snp->sn_header.sn_type) {
+ case SCTP_SEND_FAILED:
+ handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed,
+ read, isEOR, sap);
+ return JNI_TRUE;
+ case SCTP_ASSOC_CHANGE:
+ handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change);
+ return JNI_TRUE;
+ case SCTP_SHUTDOWN_EVENT:
+ handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event);
+ return JNI_TRUE;
+ case SCTP_PEER_ADDR_CHANGE:
+ handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change);
+ return JNI_TRUE;
+ default :
+ /* the Java API is not interested in this event, maybe we are? */
+ handleUninteresting(snp);
+ }
+ return JNI_FALSE;
+}
+
+void handleMessage
+ (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read,
+ jboolean isEOR, struct sockaddr* sap) {
+ jobject isa, resultObj;
+ struct controlData cdata[1];
+
+ if (read == 0) {
+ /* we reached EOF */
+ read = -1;
+ }
+
+ isa = SockAddrToInetSocketAddress(env, sap);
+ getControlData(msg, cdata);
+
+ /* create SctpMessageInfoImpl */
+ resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId,
+ isa, read, cdata->streamNumber,
+ isEOR ? JNI_TRUE : JNI_FALSE,
+ cdata->unordered, cdata->ppid);
+ CHECK_NULL(resultObj);
+ (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+ (*env)->SetIntField(env, resultContainerObj, src_typeID,
+ sun_nio_ch_SctpResultContainer_MESSAGE);
+}
+
+/*
+ * Class: sun_nio_ch_SctpChannelImpl
+ * Method: receive0
+ * Signature: (ILsun/nio/ch/SctpResultContainer;JI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_receive0
+ (JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj,
+ jlong address, jint length) {
+ SOCKADDR sa;
+ int sa_len = sizeof(sa);
+ ssize_t rv = 0;
+ jlong *addr = jlong_to_ptr(address);
+ struct iovec iov[1];
+ struct msghdr msg[1];
+ char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+
+ /* Set up the msghdr structure for receiving */
+ memset(msg, 0, sizeof (*msg));
+ msg->msg_name = &sa;
+ msg->msg_namelen = sa_len;
+ iov->iov_base = addr;
+ iov->iov_len = length;
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+ msg->msg_control = cbuf;
+ msg->msg_controllen = sizeof(cbuf);
+ msg->msg_flags = 0;
+
+ do {
+ if ((rv = recvmsg(fd, msg, 0)) < 0) {
+ if (errno == EWOULDBLOCK) {
+ return IOS_UNAVAILABLE;
+ } else if (errno == EINTR) {
+ return IOS_INTERRUPTED;
+
+#ifdef __linux__
+ } else if (errno == ENOTCONN) {
+ /* ENOTCONN when EOF reached */
+ rv = 0;
+ /* there will be no control data */
+ msg->msg_controllen = 0;
+#endif /* __linux__ */
+
+ } else {
+ handleSocketError(env, errno);
+ return 0;
+ }
+ }
+
+ if (msg->msg_flags & MSG_NOTIFICATION) {
+ char *bufp = (char*)addr;
+ union sctp_notification *snp;
+
+ if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) {
+ char buf[NOTIFICATION_BUFFER_SIZE];
+ int rvSAVE = rv;
+ memcpy(buf, addr, rv);
+ iov->iov_base = buf + rv;
+ iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv;
+ if ((rv = recvmsg(fd, msg, 0)) < 0) {
+ handleSocketError(env, errno);
+ return 0;
+ }
+ bufp = buf;
+ rv += rvSAVE;
+ }
+ snp = (union sctp_notification *) bufp;
+ if (handleNotification(env, fd, resultContainerObj, snp, rv,
+ (msg->msg_flags & MSG_EOR),
+ (struct sockaddr*)&sa ) == JNI_TRUE) {
+ /* We have received a notification that is of interest to
+ to the Java API. The appropriate notification will be
+ set in the result container. */
+ return 0;
+ }
+
+ // set iov back to addr, and reset msg_controllen
+ iov->iov_base = addr;
+ iov->iov_len = length;
+ msg->msg_control = cbuf;
+ msg->msg_controllen = sizeof(cbuf);
+ }
+ } while (msg->msg_flags & MSG_NOTIFICATION);
+
+ handleMessage(env, resultContainerObj, msg, rv,
+ (msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa);
+ return rv;
+}
+
+/*
+ * Class: sun_nio_ch_SctpChannelImpl
+ * Method: send0
+ * Signature: (IJILjava/net/SocketAddress;IIZI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_send0
+ (JNIEnv *env, jclass klass, jint fd, jlong address, jint length,
+ jobject saTarget, jint assocId, jint streamNumber, jboolean unordered,
+ jint ppid) {
+ SOCKADDR sa;
+ int sa_len = sizeof(sa);
+ ssize_t rv = 0;
+ jlong *addr = jlong_to_ptr(address);
+ struct iovec iov[1];
+ struct msghdr msg[1];
+ int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
+ char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+ struct controlData cdata[1];
+
+ /* SctpChannel:
+ * saTarget may contain the preferred address or NULL to use primary,
+ * assocId will always be -1
+ * SctpMultiChannell:
+ * Setup new association, saTarget will contain address, assocId = -1
+ * Association already existing, assocId != -1, saTarget = preferred addr
+ */
+ if (saTarget != NULL /*&& assocId <= 0*/) {
+
+ jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID);
+ jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID);
+
+ if (NET_InetAddressToSockaddr(env, targetAddress, targetPort,
+ (struct sockaddr *)&sa,
+ &sa_len, JNI_TRUE) != 0) {
+ return IOS_THROWN;
+ }
+ } else {
+ memset(&sa, '\x0', sa_len);
+ sa_len = 0;
+ }
+
+ /* Set up the msghdr structure for sending */
+ memset(msg, 0, sizeof (*msg));
+ memset(cbuf, 0, cbuf_size);
+ msg->msg_name = &sa;
+ msg->msg_namelen = sa_len;
+ iov->iov_base = addr;
+ iov->iov_len = length;
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+ msg->msg_control = cbuf;
+ msg->msg_controllen = cbuf_size;
+ msg->msg_flags = 0;
+
+ cdata->streamNumber = streamNumber;
+ cdata->assocId = assocId;
+ cdata->unordered = unordered;
+ cdata->ppid = ppid;
+ setControlData(msg, cdata);
+
+ if ((rv = sendmsg(fd, msg, 0)) < 0) {
+ if (errno == EWOULDBLOCK) {
+ return IOS_UNAVAILABLE;
+ } else if (errno == EINTR) {
+ return IOS_INTERRUPTED;
+ } else if (errno == EPIPE) {
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+ "Socket is shutdown for writing");
+ } else {
+ handleSocketError(env, errno);
+ return 0;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Class: sun_nio_ch_SctpChannelImpl
+ * Method: checkConnect
+ * Signature: (Ljava/io/FileDescriptor;ZZ)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_checkConnect
+ (JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) {
+ return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this,
+ fdo, block, ready);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpNet.c Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "Sctp.h"
+#include "jni.h"
+#include "jni_util.h"
+#include "nio_util.h"
+#include "nio.h"
+#include "net_util.h"
+#include "net_util_md.h"
+#include "sun_nio_ch_SctpNet.h"
+#include "sun_nio_ch_SctpStdSocketOption.h"
+
+static jclass isaCls = 0;
+static jmethodID isaCtrID = 0;
+
+static const char* nativeSctpLib = "libsctp.so.1";
+static jboolean funcsLoaded = JNI_FALSE;
+
+JNIEXPORT jint JNICALL JNI_OnLoad
+ (JavaVM *vm, void *reserved) {
+ return JNI_VERSION_1_2;
+}
+
+/**
+ * Loads the native sctp library that contains the socket extension
+ * functions, as well as locating the individual functions.
+ * There will be a pending exception if this method returns false.
+ */
+jboolean loadSocketExtensionFuncs
+ (JNIEnv* env) {
+ if (dlopen(nativeSctpLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ if ((nio_sctp_getladdrs = (sctp_getladdrs_func*)
+ dlsym(RTLD_DEFAULT, "sctp_getladdrs")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ if ((nio_sctp_freeladdrs = (sctp_freeladdrs_func*)
+ dlsym(RTLD_DEFAULT, "sctp_freeladdrs")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ if ((nio_sctp_getpaddrs = (sctp_getpaddrs_func*)
+ dlsym(RTLD_DEFAULT, "sctp_getpaddrs")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ if ((nio_sctp_freepaddrs = (sctp_freepaddrs_func*)
+ dlsym(RTLD_DEFAULT, "sctp_freepaddrs")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ if ((nio_sctp_bindx = (sctp_bindx_func*)
+ dlsym(RTLD_DEFAULT, "sctp_bindx")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
+ funcsLoaded = JNI_TRUE;
+ return JNI_TRUE;
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: socket0
+ * Signature: (Z)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_socket0
+ (JNIEnv *env, jclass klass, jboolean oneToOne) {
+ int fd;
+ struct sctp_event_subscribe event;
+
+ /* Try to load the socket API extension functions */
+ if (!funcsLoaded && !loadSocketExtensionFuncs(env)) {
+ return 0;
+ }
+
+ fd = socket(ipv6_available() ? AF_INET6 : AF_INET,
+ (oneToOne ? SOCK_STREAM : SOCK_SEQPACKET), IPPROTO_SCTP);
+
+ if (fd < 0) {
+ return handleSocketError(env, errno);
+ }
+
+ /* Enable events */
+ memset(&event, 0, sizeof(event));
+ event.sctp_data_io_event = 1;
+ event.sctp_association_event = 1;
+ event.sctp_address_event = 1;
+ event.sctp_send_failure_event = 1;
+ //event.sctp_peer_error_event = 1;
+ event.sctp_shutdown_event = 1;
+ //event.sctp_partial_delivery_event = 1;
+ //event.sctp_adaptation_layer_event = 1;
+ if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
+ handleSocketError(env, errno);
+ }
+ return fd;
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: bindx
+ * Signature: (I[Ljava/net/InetAddress;IIZ)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_bindx
+ (JNIEnv *env, jclass klass, jint fd, jobjectArray addrs, jint port,
+ jint addrsLength, jboolean add, jboolean preferIPv6) {
+ SOCKADDR *sap, *tmpSap;
+ int i, sa_len = sizeof(SOCKADDR);
+ jobject ia;
+
+ if (addrsLength < 1)
+ return;
+
+ if ((sap = calloc(addrsLength, sa_len)) == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+ return;
+ }
+
+ tmpSap = sap;
+ for (i=0; i<addrsLength; i++) {
+ ia = (*env)->GetObjectArrayElement(env, addrs, i);
+ if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr*)tmpSap,
+ &sa_len, preferIPv6) != 0) {
+ free(sap);
+ return;
+ }
+ tmpSap++;
+ }
+
+ if (nio_sctp_bindx(fd, (void*)sap, addrsLength, add ? SCTP_BINDX_ADD_ADDR :
+ SCTP_BINDX_REM_ADDR) != 0) {
+ handleSocketError(env, errno);
+ }
+
+ free(sap);
+}
+
+void initializeISA
+ (JNIEnv* env) {
+ if (isaCls == 0) {
+ jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress");
+ CHECK_NULL(c);
+ isaCls = (*env)->NewGlobalRef(env, c);
+ CHECK_NULL(isaCls);
+ (*env)->DeleteLocalRef(env, c);
+ isaCtrID = (*env)->GetMethodID(env, isaCls, "<init>",
+ "(Ljava/net/InetAddress;I)V");
+ }
+}
+
+jobject SockAddrToInetSocketAddress
+ (JNIEnv *env, struct sockaddr* sap) {
+ int port = 0;
+
+ jobject ia = NET_SockaddrToInetAddress(env, sap, &port);
+ if (ia == NULL)
+ return NULL;
+
+ if (isaCls == 0) {
+ initializeISA(env);
+ CHECK_NULL_RETURN(isaCls, NULL);
+ }
+
+ return (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: getLocalAddresses0
+ * Signature: (I)[Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getLocalAddresses0
+ (JNIEnv *env, jclass klass, jint fd) {
+ void *addr_buf, *laddr;
+ struct sockaddr* sap;
+ int i, addrCount;
+ jobjectArray isaa;
+
+#ifdef __solaris__
+ if ((addrCount = nio_sctp_getladdrs(fd, 0, (void **)&addr_buf)) == -1) {
+#else /* __linux__ */
+ if ((addrCount = nio_sctp_getladdrs(fd, 0, (struct sockaddr **)&addr_buf)) == -1) {
+#endif
+ handleSocketError(env, errno);
+ return NULL;
+ }
+
+ if (addrCount < 1)
+ return NULL;
+
+ if (isaCls == 0) {
+ initializeISA(env);
+ CHECK_NULL_RETURN(isaCls, NULL);
+ }
+
+ isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
+ if (isaa == NULL) {
+ nio_sctp_freeladdrs(addr_buf);
+ return NULL;
+ }
+
+ laddr = addr_buf;
+ for (i=0; i<addrCount; i++) {
+ int port = 0;
+ jobject isa = NULL, ia;
+ sap = (struct sockaddr*)addr_buf;
+ ia = NET_SockaddrToInetAddress(env, sap, &port);
+ if (ia != NULL)
+ isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+ if (isa != NULL)
+ (*env)->SetObjectArrayElement(env, isaa, i, isa);
+
+ if (sap->sa_family == AF_INET)
+ addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
+ else
+ addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
+ }
+
+ nio_sctp_freeladdrs(laddr);
+ return isaa;
+}
+
+jobjectArray getRemoteAddresses
+ (JNIEnv *env, jint fd, sctp_assoc_t id) {
+ void *addr_buf, *paddr;
+ struct sockaddr* sap;
+ int i, addrCount;
+ jobjectArray isaa;
+
+#if __solaris__
+ if ((addrCount = nio_sctp_getpaddrs(fd, id, (void **)&addr_buf)) == -1) {
+#else /* __linux__ */
+ if ((addrCount = nio_sctp_getpaddrs(fd, id, (struct sockaddr**)&addr_buf)) == -1) {
+#endif
+ handleSocketError(env, errno);
+ return NULL;
+ }
+
+ if (addrCount < 1)
+ return NULL;
+
+ if (isaCls == 0) {
+ initializeISA(env);
+ CHECK_NULL_RETURN(isaCls, NULL);
+ }
+
+ isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
+ if (isaa == NULL) {
+ nio_sctp_freepaddrs(addr_buf);
+ return NULL;
+ }
+
+ paddr = addr_buf;
+ for (i=0; i<addrCount; i++) {
+ jobject ia, isa = NULL;
+ int port;
+ sap = (struct sockaddr*)addr_buf;
+ ia = NET_SockaddrToInetAddress(env, sap, &port);
+ if (ia != NULL)
+ isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+ if (isa != NULL)
+ (*env)->SetObjectArrayElement(env, isaa, i, isa);
+
+ if (sap->sa_family == AF_INET)
+ addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
+ else
+ addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
+ }
+
+ nio_sctp_freepaddrs(paddr);
+
+ return isaa;
+}
+
+ /*
+ * Class: sun_nio_ch_SctpNet
+ * Method: getRemoteAddresses0
+ * Signature: (II)[Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getRemoteAddresses0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+ return getRemoteAddresses(env, fd, assocId);
+}
+
+/* Map the Java level option to the native level */
+int mapSocketOption
+ (jint cmd, int *level, int *optname) {
+ static struct {
+ jint cmd;
+ int level;
+ int optname;
+ } const opts[] = {
+ { sun_nio_ch_SctpStdSocketOption_SCTP_DISABLE_FRAGMENTS, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS },
+ { sun_nio_ch_SctpStdSocketOption_SCTP_EXPLICIT_COMPLETE, IPPROTO_SCTP, SCTP_EXPLICIT_EOR },
+ { sun_nio_ch_SctpStdSocketOption_SCTP_FRAGMENT_INTERLEAVE, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE },
+ { sun_nio_ch_SctpStdSocketOption_SCTP_NODELAY, IPPROTO_SCTP, SCTP_NODELAY },
+ { sun_nio_ch_SctpStdSocketOption_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF },
+ { sun_nio_ch_SctpStdSocketOption_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF },
+ { sun_nio_ch_SctpStdSocketOption_SO_LINGER, SOL_SOCKET, SO_LINGER } };
+
+ int i;
+ for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) {
+ if (cmd == opts[i].cmd) {
+ *level = opts[i].level;
+ *optname = opts[i].optname;
+ return 0;
+ }
+ }
+
+ /* not found */
+ return -1;
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: setIntOption0
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setIntOption0
+ (JNIEnv *env, jclass klass, jint fd, jint opt, int arg) {
+ int klevel, kopt;
+ int result;
+ struct linger linger;
+ void *parg;
+ int arglen;
+
+ if (mapSocketOption(opt, &klevel, &kopt) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "Unsupported socket option");
+ return;
+ }
+
+ if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
+ parg = (void *)&linger;
+ arglen = sizeof(linger);
+ if (arg >= 0) {
+ linger.l_onoff = 1;
+ linger.l_linger = arg;
+ } else {
+ linger.l_onoff = 0;
+ linger.l_linger = 0;
+ }
+ } else {
+ parg = (void *)&arg;
+ arglen = sizeof(arg);
+ }
+
+ if (setsockopt(fd, klevel, kopt, parg, arglen) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun_nio_ch_SctpNet.setIntOption0");
+ }
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: getIntOption0
+ * Signature: (II)I
+ */
+JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_getIntOption0
+ (JNIEnv *env, jclass klass, jint fd, jint opt) {
+ int klevel, kopt;
+ int result;
+ struct linger linger;
+ void *arg;
+ unsigned int arglen;
+
+ if (mapSocketOption(opt, &klevel, &kopt) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "Unsupported socket option");
+ return -1;
+ }
+
+ if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
+ arg = (void *)&linger;
+ arglen = sizeof(linger);
+ } else {
+ arg = (void *)&result;
+ arglen = sizeof(result);
+ }
+
+ if (getsockopt(fd, klevel, kopt, arg, &arglen) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.Net.getIntOption");
+ return -1;
+ }
+
+ if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER)
+ return linger.l_onoff ? linger.l_linger : -1;
+ else
+ return result;
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: getPrimAddrOption0
+ * Signature: (II)Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobject JNICALL Java_sun_nio_ch_SctpNet_getPrimAddrOption0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+ struct sctp_setprim prim;
+ struct sockaddr_storage ss;
+ int ss_len = sizeof(ss);
+ unsigned int prim_len = sizeof(prim);
+
+ prim.ssp_assoc_id = assocId;
+ prim.ssp_addr = ss;
+
+ if (getsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, &prim_len) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.SctpNet.getPrimAddrOption0");
+ return NULL;
+ }
+
+ return SockAddrToInetSocketAddress(env, (struct sockaddr*)&ss);
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: setPrimAddrOption0
+ * Signature: (IILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPrimAddrOption0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
+ struct sctp_setprim prim;
+ struct sockaddr_storage ss;
+ int ss_len = sizeof(ss);
+
+ if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
+ &ss_len, JNI_TRUE) != 0) {
+ return;
+ }
+
+ prim.ssp_assoc_id = assocId;
+ prim.ssp_addr = ss;
+
+ if (setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.SctpNet.setPrimAddrOption0");
+ }
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: setPeerPrimAddrOption0
+ * Signature: (IILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
+ struct sctp_setpeerprim prim;
+ struct sockaddr_storage ss;
+ int ss_len = sizeof(ss);
+
+ if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
+ &ss_len, JNI_TRUE) != 0) {
+ return;
+ }
+
+ prim.sspp_assoc_id = assocId;
+ prim.sspp_addr = ss;
+
+ if (setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &prim,
+ sizeof(prim)) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.SctpNet.setPeerPrimAddrOption0");
+ }
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: getInitMsgOption0
+ * Signature: (I[I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_getInitMsgOption0
+ (JNIEnv *env, jclass klass, jint fd, jintArray retVal) {
+ struct sctp_initmsg sctp_initmsg;
+ unsigned int sim_len = sizeof(sctp_initmsg);
+ int vals[2];
+
+ if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
+ &sim_len) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.SctpNet.getInitMsgOption0");
+ return;
+ }
+
+ vals[0] = sctp_initmsg.sinit_max_instreams;
+ vals[1] = sctp_initmsg.sinit_num_ostreams;
+ (*env)->SetIntArrayRegion(env, retVal, 0, 2, vals);
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: setInitMsgOption0
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setInitMsgOption0
+ (JNIEnv *env, jclass klass, jint fd, jint inArg, jint outArg) {
+ struct sctp_initmsg sctp_initmsg;
+
+ sctp_initmsg.sinit_max_instreams = (unsigned int)inArg;
+ sctp_initmsg.sinit_num_ostreams = (unsigned int)outArg;
+ sctp_initmsg.sinit_max_attempts = 0; // default
+ sctp_initmsg.sinit_max_init_timeo = 0; // default
+
+ if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
+ sizeof(sctp_initmsg)) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "sun.nio.ch.SctpNet.setInitMsgOption0");
+ }
+}
+
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: shutdown0
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_shutdown0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+ int rv;
+ struct msghdr msg[1];
+ struct iovec iov[1];
+ int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
+ char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+ struct cmsghdr* cmsg;
+ struct sctp_sndrcvinfo *sri;
+
+ /* SctpSocketChannel */
+ if (assocId < 0) {
+ shutdown(fd, SHUT_WR);
+ return;
+ }
+
+ memset(msg, 0, sizeof (*msg));
+ memset(cbuf, 0, cbuf_size);
+ msg->msg_name = NULL;
+ msg->msg_namelen = 0;
+ iov->iov_base = NULL;
+ iov->iov_len = 0;
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+ msg->msg_control = cbuf;
+ msg->msg_controllen = cbuf_size;
+ msg->msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR(msg);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+ /* Initialize the payload: */
+ sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
+ memset(sri, 0, sizeof (*sri));
+
+ if (assocId > 0) {
+ sri->sinfo_assoc_id = assocId;
+ }
+
+ sri->sinfo_flags = sri->sinfo_flags | SCTP_EOF;
+
+ /* Sum of the length of all control messages in the buffer. */
+ msg->msg_controllen = cmsg->cmsg_len;
+
+ if ((rv = sendmsg(fd, msg, 0)) < 0) {
+ handleSocketError(env, errno);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+#include "sun_nio_ch_SctpServerChannelImpl.h"
+
+extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env,
+ jclass c);
+
+extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env,
+ jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa);
+
+/*
+ * Class: sun_nio_ch_SctpServerChannelImpl
+ * Method: initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpServerChannelImpl_initIDs
+ (JNIEnv* env, jclass c) {
+ Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c);
+}
+
+/*
+ * Class: sun_nio_ch_SctpServerChannelImpl
+ * Method: accept0
+ * Signature: (Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[Ljava/net/InetSocketAddress;)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpServerChannelImpl_accept0
+ (JNIEnv* env, jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa) {
+ return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this,
+ ssfdo, newfdo, isaa);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpChannelImpl extends SctpChannel
+{
+ private static final String message = "SCTP not supported on this platform";
+
+ public SctpChannelImpl(SelectorProvider provider) {
+ super(provider);
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Association association() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel bind(SocketAddress local)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel bindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel unbindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public boolean connect(SocketAddress remote) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public boolean connect(SocketAddress remote, int maxOutStreams,
+ int maxInStreams) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public boolean isConnectionPending() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public boolean finishConnect() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SocketAddress> getRemoteAddresses()
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel shutdown() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> T getOption(SctpSocketOption<T> name)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SctpSocketOption<?>> supportedOptions() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> MessageInfo receive(ByteBuffer dst, T attachment,
+ NotificationHandler<T> handler) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public int send(ByteBuffer src, MessageInfo messageInfo)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpMultiChannelImpl extends SctpMultiChannel
+{
+ private static final String message = "SCTP not supported on this platform";
+
+ public SctpMultiChannelImpl(SelectorProvider provider) {
+ super(provider);
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<Association> associations() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpMultiChannel bind(SocketAddress local,
+ int backlog) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpMultiChannel bindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpMultiChannel unbindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SocketAddress> getRemoteAddresses
+ (Association association) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpMultiChannel shutdown(Association association)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> T getOption(SctpSocketOption<T> name,
+ Association association) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+ T value, Association association) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SctpSocketOption<?>> supportedOptions() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> MessageInfo receive(ByteBuffer buffer, T attachment,
+ NotificationHandler<T> handler) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public int send(ByteBuffer buffer, MessageInfo messageInfo)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel branch(Association association)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.nio.ch;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpServerChannelImpl extends SctpServerChannel
+{
+ private static final String message = "SCTP not supported on this platform";
+
+ public SctpServerChannelImpl(SelectorProvider provider) {
+ super(provider);
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpChannel accept() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpServerChannel bind(SocketAddress local,
+ int backlog) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpServerChannel bindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public SctpServerChannel unbindAddress(InetAddress address)
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SocketAddress> getAllLocalAddresses()
+ throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public <T> SctpServerChannel setOption(SctpSocketOption<T> name,
+ T value) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public Set<SctpSocketOption<?>> supportedOptions() {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ protected void implConfigureBlocking(boolean block) throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+
+ @Override
+ public void implCloseSelectableChannel() throws IOException {
+ throw new UnsupportedOperationException(message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/MessageInfoTests.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.MessageInfo;
+
+public class MessageInfoTests {
+ static final int DEFAULT_STREAM_NUMBER = 14;
+ static final int TEST_STREAM_NUMBER = 15;
+ static final int TEST_PPID = 8;
+ static final long TEST_TTL = 10000L;
+ static final SocketAddress addr = new TestSocketAddress();
+ static final Association assoc = new TestAssociation(1, 1, 1);
+
+ void test(String[] args) {
+ /* TEST 1 : createOutGoing(SocketAddress,int) */
+ MessageInfo info = MessageInfo.createOutgoing(addr,
+ DEFAULT_STREAM_NUMBER);
+ checkDefaults(info);
+ checkGetterSetters(info);
+
+ /* TEST 2 : createOutGoing(Association,SocketAddress,int) */
+ info = MessageInfo.createOutgoing(assoc, addr, DEFAULT_STREAM_NUMBER);
+ checkDefaults(info);
+ check(info.association().equals(assoc), "incorrect association");
+ checkGetterSetters(info);
+
+ /* TEST 3: null values */
+ info = MessageInfo.createOutgoing(null, 0);
+ check(info.address() == null, "address should be null");
+ check(info.association() == null, "association should be null");
+ info = MessageInfo.createOutgoing(assoc, null, 0);
+ check(info.address() == null, "address should be null");
+
+ /* Test 4: IllegalArgumentException */
+ testIAE(new Runnable() {
+ public void run() { MessageInfo.createOutgoing(addr, -1); } });
+ testIAE(new Runnable() {
+ public void run() { MessageInfo.createOutgoing(addr, 65537); } });
+ testIAE(new Runnable() {
+ public void run() { MessageInfo.createOutgoing(null, addr, 0); } });
+ testIAE(new Runnable() {
+ public void run() { MessageInfo.createOutgoing(assoc, addr, -1); } });
+ testIAE(new Runnable() {
+ public void run() { MessageInfo.createOutgoing(assoc, addr, 65537);}});
+
+ final MessageInfo iaeInfo = MessageInfo.createOutgoing(assoc, addr, 0);
+ testIAE(new Runnable() {
+ public void run() { iaeInfo.streamNumber(-1); } });
+ testIAE(new Runnable() {
+ public void run() { iaeInfo.streamNumber(65537); } });
+ }
+
+ /* TEST : unordered = false, timeToLive = 0, complete = true,
+ * payloadProtocolID = 0. */
+ void checkDefaults(MessageInfo info) {
+ check(info.isUnordered() == false, "default unordered value not false");
+ check(info.timeToLive() == 0L, "timeToLive should be 0L");
+ check(info.isComplete() == true, "default complete value not true");
+ check(info.payloadProtocolID() == 0, "default PPID not 0");
+ check(info.bytes() == 0, "default bytes value not 0");
+ check(info.streamNumber() == DEFAULT_STREAM_NUMBER,
+ "incorrect default stream number");
+ check(info.address().equals(addr), "incorrect address");
+ }
+
+ void checkGetterSetters(MessageInfo info) {
+ check(info.streamNumber(TEST_STREAM_NUMBER).streamNumber() ==
+ TEST_STREAM_NUMBER, "stream number not being set correctly");
+
+ check(info.complete(false).isComplete() == false,
+ "complete not being set correctly");
+
+ check(info.unordered(true).isUnordered() == true,
+ "unordered not being set correctly");
+
+ check(info.payloadProtocolID(TEST_PPID).payloadProtocolID() ==
+ TEST_PPID, "PPID not being set correctly");
+
+ check(info.timeToLive(TEST_TTL).timeToLive() == TEST_TTL,
+ "TTL not being set correctly");
+ }
+
+ void testIAE(Runnable runnable) {
+ try {
+ runnable.run();
+ fail("IllegalArgumentException should have been thrown");
+ } catch(IllegalArgumentException iae) {
+ pass();
+ }
+ }
+
+ static class TestSocketAddress extends SocketAddress {}
+
+ static class TestAssociation extends Association {
+ TestAssociation(int assocID, int maxInStreams, int maxOutStreams) {
+ super(assocID, maxInStreams, maxOutStreams);
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass();
+ else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Bind.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.List;
+import java.util.Set;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.PeerAddressChangeNotification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+
+/**
+ * Tests bind, bindAddress, unbindAddress, getLocalAddress, and
+ * getAllLocalAddresses.
+ */
+public class Bind {
+ void test(String[] args) {
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ /* Simply bind tests */
+ testBind();
+
+ /* Test unconnected */
+ testBindUnbind(false);
+
+ /* Test connected */
+ /* Adding/Removing addresses from a connected association is optional.
+ * This test can be run on systems that support dynamic address
+ * reconfiguration */
+ //testBindUnbind(true);
+ }
+
+ void testBind() {
+ SctpChannel channel = null;
+ try {
+ channel = SctpChannel.open();
+
+ /* TEST 1: empty set if channel is not bound */
+ check(channel.getAllLocalAddresses().isEmpty(),
+ "getAllLocalAddresses returned non empty set for unbound channel");
+
+ /* TEST 2: null to bind the channel to an automatically assigned
+ * socket address */
+ channel.bind(null);
+
+ /* TEST 3: non empty set if the channel is bound */
+ check(!channel.getAllLocalAddresses().isEmpty(),
+ "getAllLocalAddresses returned empty set for bound channel");
+ debug("getAllLocalAddresses on channel bound to the wildcard:\n"
+ + channel.getAllLocalAddresses());
+
+ /* TEST 4: AlreadyBoundException if this channel is already bound */
+ try { channel.bind(null); }
+ catch (AlreadyBoundException unused) { pass(); }
+ catch (IOException ioe) { unexpected(ioe); }
+
+ /* TEST 5: UnsupportedAddressTypeException */
+ try {
+ channel.close(); /* open a new unbound channel for test */
+ channel = SctpChannel.open();
+ channel.bind(new UnsupportedSocketAddress());
+ fail("UnsupportedSocketAddress expected");
+ } catch (UnsupportedAddressTypeException unused) { pass();
+ } catch (IOException ioe) { unexpected(ioe); }
+
+ /* TEST 6: AlreadyConnectedException */
+ try {
+ channel.close(); /* open a new unbound channel for test */
+ channel = SctpChannel.open();
+ connectChannel(channel);
+ channel.bind(null);
+ fail("AlreadyConnectedException expected");
+ } catch (AlreadyConnectedException unused) { pass();
+ } catch (IOException ioe) { unexpected(ioe); }
+
+ /* TEST 7: ClosedChannelException - If this channel is closed */
+ try {
+ channel.close(); /* open a new unbound channel for test */
+ channel = SctpChannel.open();
+ channel.close();
+ channel.bind(null);
+ fail("ClosedChannelException expected");
+ } catch (ClosedChannelException unused) { pass();
+ } catch (IOException ioe) { unexpected(ioe); }
+
+ /* TEST 8: ClosedChannelException if channel is closed */
+ try {
+ channel.getAllLocalAddresses();
+ fail("should have thrown ClosedChannelException");
+ } catch (ClosedChannelException cce) {
+ pass();
+ } catch (Exception ioe) {
+ unexpected(ioe);
+ }
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ try { channel.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+
+ void testBindUnbind(boolean connected) {
+ SctpChannel channel = null;
+ SctpChannel peerChannel = null;
+
+ debug("testBindUnbind, connected: " + connected);
+ try {
+ channel = SctpChannel.open();
+
+ List<InetAddress> addresses = Util.getAddresses(true, false);
+ Iterator iterator = addresses.iterator();
+ InetSocketAddress a = new InetSocketAddress((InetAddress)iterator.next(), 0);
+ debug("channel.bind( " + a + ")");
+ channel.bind(a);
+ while (iterator.hasNext()) {
+ InetAddress ia = (InetAddress)iterator.next();
+ debug("channel.bindAddress(" + ia + ")");
+ channel.bindAddress(ia);
+ }
+ if (debug) {Util.dumpAddresses(channel, out);}
+
+ if (connected) {
+ /* Test with connected channel */
+ peerChannel = connectChannel(channel);
+ }
+
+ /* TEST 1: bind/unbindAddresses on the system addresses */
+ debug("bind/unbindAddresses on the system addresses");
+ List<InetAddress> addrs = Util.getAddresses(true, false);
+ for (InetAddress addr : addrs) {
+ try {
+ debug("unbindAddress: " + addr);
+ check(boundAddress(channel, addr), "trying to remove address that is not bound");
+ channel.unbindAddress(addr);
+ if (debug) {Util.dumpAddresses(channel, out);}
+ check(!boundAddress(channel, addr), "address was not removed");
+
+ debug("bindAddress: " + addr);
+ channel.bindAddress(addr);
+ if (debug) {Util.dumpAddresses(channel, out);}
+ check(boundAddress(channel, addr), "address is not bound");
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ }
+
+ /* TEST 2: bindAddress - already bound address. */
+ InetAddress againAddress = addrs.get(0);
+ try {
+ debug("bind already bound address " + againAddress);
+ channel.bindAddress(againAddress);
+ } catch (AlreadyBoundException unused) {
+ debug("Caught AlreadyBoundException - OK");
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 3: bind non local address */
+ try {
+ InetAddress nla = InetAddress.getByName("123.123.123.123");
+ debug("bind non local address " + nla);
+ channel.bindAddress(nla);
+ } catch (IOException ioe) {
+ debug("Informative only " + ioe);
+ }
+
+ /* TEST 4: unbind address that is not bound */
+ try {
+ debug("unbind address that is not bound " + againAddress);
+ /* remove address first then again */
+ channel.unbindAddress(againAddress);
+ channel.unbindAddress(againAddress);
+ } catch (IllegalUnbindException unused) {
+ debug("Caught IllegalUnbindException - OK");
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 5: unbind address that is not bound */
+ try {
+ InetAddress nla = InetAddress.getByName("123.123.123.123");
+ debug("unbind address that is not bound " + nla);
+ channel.unbindAddress(nla);
+
+ } catch (IllegalUnbindException unused) {
+ debug("Caught IllegalUnbindException - OK");
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ if (connected) {
+ channel.shutdown();
+
+ BindNotificationHandler handler = new BindNotificationHandler();
+ ByteBuffer buffer = ByteBuffer.allocate(10);
+ MessageInfo info;
+ while((info = peerChannel.receive(buffer, null, handler)) != null) {
+ if (info != null) {
+ if (info.bytes() == -1) {
+ debug("peerChannel Reached EOF");
+ break;
+ }
+ }
+ }
+
+ while((info = channel.receive(buffer, null, handler)) != null) {
+ if (info != null) {
+ if (info.bytes() == -1) {
+ debug("channel Reached EOF");
+ break;
+ }
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } finally {
+ try { if (channel != null) channel.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+
+ boolean boundAddress(SctpChannel channel, InetAddress addr)
+ throws IOException {
+ for (SocketAddress boundAddr : channel.getAllLocalAddresses()) {
+ if (((InetSocketAddress) boundAddr).getAddress().equals(addr))
+ return true;
+ }
+ return false;
+ }
+
+ SctpChannel connectChannel(SctpChannel channel)
+ throws IOException {
+ debug("connecting channel...");
+ try {
+ SctpServerChannel ssc = SctpServerChannel.open();
+ ssc.bind(null);
+ Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ Iterator<SocketAddress> iterator = addrs.iterator();
+ SocketAddress addr = iterator.next();
+ debug("using " + addr + "...");
+ channel.connect(addr);
+ SctpChannel peerChannel = ssc.accept();
+ ssc.close();
+ debug("connected");
+ return peerChannel;
+ } catch (IOException ioe) {
+ debug("Cannot connect channel");
+ unexpected(ioe);
+ throw ioe;
+ }
+ }
+
+ class BindNotificationHandler extends AbstractNotificationHandler<Object>
+ {
+ @Override
+ public HandlerResult handleNotification(
+ AssociationChangeNotification acn, Object unused)
+ {
+ debug("AssociationChangeNotification: " + acn);
+ return HandlerResult.CONTINUE;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ PeerAddressChangeNotification pacn, Object unused)
+ {
+ debug("PeerAddressChangeNotification: " + pacn);
+ return HandlerResult.CONTINUE;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ ShutdownNotification sn, Object unused)
+ {
+ debug("ShutdownNotification: " + sn);
+ return HandlerResult.CONTINUE;
+ }
+ }
+
+ class UnsupportedSocketAddress extends SocketAddress { }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Connect.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ConnectionPendingException;
+import java.nio.channels.NoConnectionPendingException;
+import java.nio.channels.UnresolvedAddressException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+/**
+ * Tests connect, finishConnect, isConnectionPending,
+ * getRemoteAddresses and association.
+ */
+public class Connect {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ Server server = null;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connect to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new Server();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doTest(address);
+ }
+
+ void doTest(SocketAddress addr) {
+ SctpChannel channel = null;
+ final SocketAddress peerAddress = addr;
+
+ try {
+ channel = SctpChannel.open();
+
+ /* TEST 0.5 Verify default values for new/unconnected channel */
+ check(channel.getRemoteAddresses().isEmpty(),
+ "non empty set for unconnected channel");
+ check(channel.association() == null,
+ "non-null association for unconnected channel");
+ check(!channel.isConnectionPending(),
+ "should not have a connection pending");
+
+ /* TEST 1: non-blocking connect */
+ channel.configureBlocking(false);
+ if (channel.connect(peerAddress) != true) {
+ debug("non-blocking connect did not immediately succeed");
+ check(channel.isConnectionPending(),
+ "should return true for isConnectionPending");
+ try {
+ channel.connect(peerAddress);
+ fail("should have thrown ConnectionPendingException");
+ } catch (ConnectionPendingException cpe) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ channel.configureBlocking(true);
+ check(channel.finishConnect(),
+ "finishConnect should have returned true");
+ }
+
+ /* TEST 1.5 Verify after connect */
+ check(!channel.getRemoteAddresses().isEmpty(),
+ "empty set for connected channel");
+ check(channel.association() != null,
+ "null association for connected channel");
+ check(!channel.isConnectionPending(),
+ "pending connection for connected channel");
+
+ /* TEST 2: Verify AlreadyConnectedException thrown */
+ try {
+ channel.connect(peerAddress);
+ fail("should have thrown AlreadyConnectedException");
+ } catch (AlreadyConnectedException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 3: UnresolvedAddressException */
+ channel.close();
+ channel = SctpChannel.open();
+ InetSocketAddress unresolved =
+ InetSocketAddress.createUnresolved("xxyyzzabc", 4567);
+ try {
+ channel.connect(unresolved);
+ fail("should have thrown UnresolvedAddressException");
+ } catch (UnresolvedAddressException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 4: UnsupportedAddressTypeException */
+ SocketAddress unsupported = new UnsupportedSocketAddress();
+ try {
+ channel.connect(unsupported);
+ fail("should have thrown UnsupportedAddressTypeException");
+ } catch (UnsupportedAddressTypeException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 5: ClosedChannelException */
+ channel.close();
+ final SctpChannel closedChannel = channel;
+ testCCE(new Callable<Void>() {
+ public Void call() throws IOException {
+ closedChannel.connect(peerAddress); return null; } });
+
+ /* TEST 5.5 getRemoteAddresses */
+ testCCE(new Callable<Void>() {
+ public Void call() throws IOException {
+ closedChannel.getRemoteAddresses(); return null; } });
+ testCCE(new Callable<Void>() {
+ public Void call() throws IOException {
+ closedChannel.association(); return null; } });
+ check(!channel.isConnectionPending(),
+ "pending connection for closed channel");
+
+ /* Run some more finishConnect tests */
+
+ /* TEST 6: NoConnectionPendingException */
+ channel = SctpChannel.open();
+ try {
+ channel.finishConnect();
+ fail("should have thrown NoConnectionPendingException");
+ } catch (NoConnectionPendingException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 7: ClosedChannelException */
+ channel.close();
+ final SctpChannel cceChannel = channel;
+ testCCE(new Callable<Void>() {
+ public Void call() throws IOException {
+ cceChannel.finishConnect(); return null; } });
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ finishedLatch.countDown();
+ try { if (channel != null) channel.close(); }
+ catch (IOException e) { unexpected(e);}
+ }
+ }
+
+ class UnsupportedSocketAddress extends SocketAddress { }
+
+ void testCCE(Callable callable) {
+ try {
+ callable.call();
+ fail("should have thrown ClosedChannelException");
+ } catch (ClosedChannelException cce) {
+ pass();
+ } catch (Exception ioe) {
+ unexpected(ioe);
+ }
+ }
+
+ class Server implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+
+ public Server() throws IOException {
+ ssc = SctpServerChannel.open().bind(null);
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+ }
+
+ public void start() {
+ (new Thread(this, "Server-" + serverAddr.getPort())).start();
+ }
+
+ public InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ SctpChannel sc = null;
+ try {
+ sc = ssc.accept();
+ finishedLatch.await();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ } finally {
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ try { if (sc != null) sc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Receive.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Notification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Receive {
+ /* Latches used to synchronize between the client and server so that
+ * connections without any IO may not be closed without being accepted */
+ final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+ final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+ /* Used to verify that the ppid is being sent and received correctly */
+ static final int PPID = 5;
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ Server server;
+
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new Server();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doTest(address);
+ }
+
+ void doTest(SocketAddress peerAddress) {
+ SctpChannel channel = null;
+ ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+ MessageInfo info;
+
+ try {
+ channel = SctpChannel.open();
+ ReceiveNotificationHandler handler =
+ new ReceiveNotificationHandler(channel);
+
+ /* TEST 1: Verify NotYetConnectedException thrown */
+ try {
+ channel.receive(buffer, null, handler);
+ fail("should have thrown NotYetConnectedException");
+ } catch (NotYetConnectedException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ channel.connect(peerAddress);
+
+ /* TEST 2: receive small message */
+ do {
+ debug("Test 2: invoking receive");
+ info = channel.receive(buffer, null, handler);
+ if (info == null) {
+ fail("unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(handler.receivedCommUp(), "SCTP_COMM_UP not received");
+ check(info != null, "info is null");
+ check(info.address() != null, "address is null");
+ check(info.association() != null, "association is null");
+ check(info.isComplete(), "message is not complete");
+ check(info.isUnordered() != true,
+ "message should not be unordered");
+ check(info.streamNumber() >= 0, "invalid stream number");
+ check(info.payloadProtocolID() == PPID, "PPID incorrect");
+ check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.SMALL_MESSAGE),
+ "received message not the same as sent message");
+
+ buffer.clear();
+
+ /* TEST 3: receive large message */
+ do {
+ debug("Test 3: invoking receive");
+ info = channel.receive(buffer, null, handler);
+ if (info == null) {
+ fail("unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.address() != null, "address is null");
+ check(info.association() != null, "association is null");
+ check(info.isComplete(), "message is not complete");
+ check(info.isUnordered() != true,
+ "message should not be unordered");
+ check(info.streamNumber() >= 0, "invalid stream number");
+ check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.LARGE_MESSAGE),
+ "received message not the same as sent message");
+
+ buffer.clear();
+
+ /* TEST 4: EOF */
+ buffer.clear(); // buffer position 0
+ info = channel.receive(buffer,null, handler);
+ check(info != null, "info is null");
+ check(info.bytes() == -1, "should have received EOF");
+ check(buffer.position() == 0, "buffer position should be unchanged");
+
+ /* TEST 5: ClosedChannelException */
+ channel.close();
+ try {
+ channel.receive(buffer, null, null);
+ fail("should have thrown ClosedChannelException");
+ } catch (ClosedChannelException cce) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ handler = null;
+
+ /* TEST 6: handler returns RETURN after handling a notification */
+ ReceiveNotificationHandler handler2 =
+ new ReceiveNotificationHandler(null); /* HandlerResult.RETURN */
+ channel = SctpChannel.open(peerAddress, 0, 0);
+ info = channel.receive(buffer, null, handler2);
+ check(info == null, "channel should return null");
+ check(handler2.receivedCommUp(), "SCTP_COMM_UP not received");
+ check(buffer.position() == 0, "buffer position should be unchanged");
+
+ /* TEST 7: Non blocking channel return null if no data */
+ channel.configureBlocking(false);
+ info = channel.receive(buffer, null, null);
+ check(info == null, "non-blocking channel should return null");
+ check(buffer.position() == 0, "buffer position should be unchanged");
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ clientFinishedLatch.countDown();
+ try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+ catch (InterruptedException ie) { unexpected(ie); }
+ if (channel != null) {
+ try { channel.close(); }
+ catch (IOException e) { unexpected (e);}
+ }
+ }
+ }
+
+ class Server implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+
+ public Server() throws IOException {
+ ssc = SctpServerChannel.open().bind(null);
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+ }
+
+ public void start() {
+ (new Thread(this, "Server-" + serverAddr.getPort())).start();
+ }
+
+ public InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ try {
+ SctpChannel sc = ssc.accept();
+
+ /* send a small message */
+ MessageInfo info = MessageInfo.createOutgoing(null, 0)
+ .payloadProtocolID(PPID);
+ ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
+ buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buf.flip();
+
+ debug("sending small message: " + buf);
+ sc.send(buf, info);
+
+ /* send a large message */
+ buf = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+ buf.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+ buf.flip();
+
+ debug("sending large message: " + buf);
+ sc.send(buf, info);
+ sc.shutdown();
+ debug("shutdown");
+ ReceiveNotificationHandler handler =
+ new ReceiveNotificationHandler(sc);
+ sc.receive(buf, null, handler);
+ sc.close();
+
+ /* accept another socket for the TEST 6 */
+ sc = ssc.accept();
+ ssc.close();
+
+ clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+ serverFinishedLatch.countDown();
+ sc.close();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ }
+ }
+ }
+
+ class ReceiveNotificationHandler extends AbstractNotificationHandler<Object>
+ {
+ SctpChannel channel;
+ boolean receivedCommUp; // false
+
+ public ReceiveNotificationHandler(SctpChannel channel) {
+ this.channel = channel;
+ }
+
+ public boolean receivedCommUp() {
+ return receivedCommUp;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ Notification notification, Object attachment) {
+ fail("Unknown notification type");
+ return HandlerResult.CONTINUE;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ AssociationChangeNotification notification, Object attachment) {
+ AssocChangeEvent event = notification.event();
+ debug("AssociationChangeNotification");
+ debug(" Association: " + notification.association());
+ debug(" Event: " + event);
+
+ if (event.equals(AssocChangeEvent.COMM_UP))
+ receivedCommUp = true;
+
+ if (channel == null)
+ return HandlerResult.RETURN;
+
+ /* TEST 4: IllegalReceiveException - If the given handler invokes
+ * the receive method of this channel*/
+ ByteBuffer buffer = ByteBuffer.allocate(10);
+ try {
+ channel.receive(buffer, null, this);
+ fail("IllegalReceiveException expected");
+ } catch (IllegalReceiveException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ return HandlerResult.CONTINUE;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ ShutdownNotification notification, Object attachment) {
+ debug("ShutdownNotification");
+ debug(" Association: " + notification.association());
+ return HandlerResult.CONTINUE;
+ }
+ }
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) {
+ System.out.println(Thread.currentThread() + " " + message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Send.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Notification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Send {
+ /* Latches used to synchronize between the client and server so that
+ * connections without any IO may not be closed without being accepted */
+ final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+ final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+ SendNotificationHandler handler = new SendNotificationHandler();
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ Server server = null;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new Server();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doTest(address);
+ }
+
+ void doTest(SocketAddress peerAddress) {
+ SctpChannel channel = null;
+ ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+ MessageInfo info = MessageInfo.createOutgoing(null, 0);
+
+ try {
+ channel = SctpChannel.open();
+
+ /* TEST 1: Verify NotYetConnectedException thrown */
+ try {
+ channel.send(buffer, info);
+ fail("should have thrown NotYetConnectedException");
+ } catch (NotYetConnectedException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ channel.connect(peerAddress);
+ /* Receive CommUp */
+ channel.receive(buffer, null, handler);
+
+ /* save for TEST 8 */
+ Association association = channel.association();
+
+ /* TEST 2: send small message */
+ int streamNumber = 0;
+ debug("sending on stream number: " + streamNumber);
+ info = MessageInfo.createOutgoing(null, streamNumber);
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ int position = buffer.position();
+ int remaining = buffer.remaining();
+
+ debug("sending small message: " + buffer);
+ int sent = channel.send(buffer, info);
+
+ check(sent == remaining, "sent should be equal to remaining");
+ check(buffer.position() == (position + sent),
+ "buffers position should have been incremented by sent");
+
+ buffer.clear();
+
+ /* TEST 3: send large message */
+ streamNumber = handler.maxOutStreams() - 1;
+ debug("sending on stream number: " + streamNumber);
+ info = MessageInfo.createOutgoing(null, streamNumber);
+ buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ position = buffer.position();
+ remaining = buffer.remaining();
+
+ debug("sending large message: " + buffer);
+ sent = channel.send(buffer, info);
+
+ check(sent == remaining, "sent should be equal to remaining");
+ check(buffer.position() == (position + sent),
+ "buffers position should have been incremented by sent");
+
+ /* TEST 4: InvalidStreamExcepton */
+ streamNumber = handler.maxInStreams;
+ info = MessageInfo.createOutgoing(null, streamNumber);
+ buffer.clear();
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ position = buffer.position();
+ remaining = buffer.remaining();
+
+ debug("sending on stream number: " + streamNumber);
+ debug("sending small message: " + buffer);
+ try {
+ sent = channel.send(buffer, info);
+ fail("should have thrown InvalidStreamExcepton");
+ } catch (InvalidStreamException ise){
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ check(buffer.remaining() == remaining,
+ "remaining should not be changed");
+ check(buffer.position() == position,
+ "buffers position should not be changed");
+
+ /* TEST 5: Non blocking send should return zero if there is
+ insufficient room in the underlying output buffer */
+ buffer.clear();
+ channel.configureBlocking(false);
+ info = MessageInfo.createOutgoing(null, 1);
+ buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+
+ int count = 0; // do not loop forever
+ do {
+ position = buffer.position();
+ remaining = buffer.remaining();
+ debug("sending large message: " + buffer);
+ sent = channel.send(buffer, info);
+ if (sent == 0) {
+ check(buffer.remaining() == remaining,
+ "remaining should not be changed");
+ check(buffer.position() == position,
+ "buffers position should not be changed");
+ }
+ buffer.rewind();
+ } while (sent != 0 && count++ < 100);
+
+ /* TEST 6: ClosedChannelException */
+ channel.close();
+ try {
+ channel.send(buffer, info);
+ fail("should have thrown ClosedChannelException");
+ } catch (ClosedChannelException cce) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 7: send without previous receive.
+ * Verify that send can still throw InvalidStreamExcepton */
+ debug("Opening new channel.");
+ channel = SctpChannel.open(peerAddress, 0, 0);
+ streamNumber = Short.MAX_VALUE - 1;
+ info = MessageInfo.createOutgoing(null, streamNumber);
+ buffer.clear();
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ position = buffer.position();
+ remaining = buffer.remaining();
+
+ debug("sending on stream number: " + streamNumber);
+ debug("sending small message: " + buffer);
+ try {
+ sent = channel.send(buffer, info);
+ fail("should have thrown InvalidStreamExcepton");
+ } catch (InvalidStreamException ise){
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ check(buffer.remaining() == remaining,
+ "remaining should not be changed");
+ check(buffer.position() == position,
+ "buffers position should not be changed");
+
+ /* Receive CommUp */
+ channel.receive(buffer, null, handler);
+ check(handler.receivedCommUp(), "should have received COMM_UP");
+
+ /* TEST 8: Send to an invalid preferred SocketAddress */
+ SocketAddress addr = new InetSocketAddress("123.123.123.123", 3456);
+ info = MessageInfo.createOutgoing(addr, 0);
+ debug("sending to " + addr);
+ debug("sending small message: " + buffer);
+ try {
+ sent = channel.send(buffer, info);
+ fail("Invalid address should have thrown an Exception.");
+ } catch (Exception e){
+ pass();
+ debug("OK, caught " + e);
+ }
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ clientFinishedLatch.countDown();
+ try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+ catch (InterruptedException ie) { unexpected(ie); }
+ if (channel != null) {
+ try { channel.close(); }
+ catch (IOException e) { unexpected (e);}
+ }
+ }
+ }
+
+ class Server implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+
+ public Server() throws IOException {
+ ssc = SctpServerChannel.open().bind(null);
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+ }
+
+ public void start() {
+ (new Thread(this, "Server-" + serverAddr.getPort())).start();
+ }
+
+ public InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+ SctpChannel sc1 = null, sc2 = null;
+ try {
+ sc1 = ssc.accept();
+
+ /* receive a small message */
+ MessageInfo info;
+ do {
+ info = sc1.receive(buffer, null, null);
+ if (info == null) {
+ fail("Server: unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.streamNumber() == 0,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.SMALL_MESSAGE),
+ "received message not the same as sent message");
+
+ /* receive a large message */
+ buffer.clear();
+ do {
+ info = sc1.receive(buffer, null, null);
+ if (info == null) {
+ fail("Server: unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.streamNumber() == handler.maxOutStreams() - 1,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.LARGE_MESSAGE),
+ "received message not the same as sent message");
+
+ /* TEST 7 ++ */
+ sc2 = ssc.accept();
+
+ clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+ serverFinishedLatch.countDown();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ } finally {
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException unused) {}
+ try { if (sc1 != null) sc1.close(); }
+ catch (IOException unused) {}
+ try { if (sc2 != null) sc2.close(); }
+ catch (IOException unused) {}
+ }
+ }
+ }
+
+ class SendNotificationHandler extends AbstractNotificationHandler<Void>
+ {
+ boolean receivedCommUp; // false
+ int maxInStreams;
+ int maxOutStreams;
+
+ public boolean receivedCommUp() {
+ return receivedCommUp;
+ }
+
+ public int maxInStreams() {
+ return maxInStreams;
+ }
+
+ public int maxOutStreams(){
+ return maxOutStreams;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ Notification notification, Void attachment) {
+ fail("Unknown notification type");
+ return HandlerResult.CONTINUE;
+ }
+
+ @Override
+ public HandlerResult handleNotification(
+ AssociationChangeNotification notification, Void attachment) {
+ AssocChangeEvent event = notification.event();
+ Association association = notification.association();
+ debug("AssociationChangeNotification");
+ debug(" Association: " + notification.association());
+ debug(" Event: " + event);
+
+ if (event.equals(AssocChangeEvent.COMM_UP))
+ receivedCommUp = true;
+
+ this.maxInStreams = association.maxInboundStreams();
+ this.maxOutStreams = association.maxOutboundStreams();
+
+ return HandlerResult.RETURN;
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Shutdown.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetConnectedException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Shutdown {
+ static CountDownLatch finishedLatch = new CountDownLatch(1);
+ static CountDownLatch sentLatch = new CountDownLatch(1);
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ ShutdownServer server = null;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new ShutdownServer();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doTest(address);
+ }
+
+ void doTest(SocketAddress peerAddress) {
+ SctpChannel channel = null;
+ ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
+ MessageInfo info;
+
+ try {
+ channel = SctpChannel.open();
+
+ /* TEST 1: Verify NotYetConnectedException thrown */
+ debug("Test 1: NotYetConnectedException");
+ try {
+ channel.shutdown();
+ fail("shutdown not throwing expected NotYetConnectedException");
+ } catch (NotYetConnectedException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ channel.connect(peerAddress);
+ sentLatch.await();
+ channel.shutdown();
+
+ /* TEST 2: receive data sent before shutdown */
+ do {
+ debug("Test 2: invoking receive");
+ info = channel.receive(buffer, null, null);
+ if (info == null) {
+ fail("unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.SMALL_MESSAGE),
+ "received message not the same as sent message");
+
+ buffer.clear();
+
+ /* TEST 3: receive notifications on the SCTP stack */
+ debug("Test 3: receive notifications");
+ while ((info = channel.receive(buffer, null, null )) != null &&
+ info.bytes() != -1 );
+
+
+ /* TEST 4: If the channel is already shutdown then invoking this
+ * method has no effect. */
+ debug("Test 4: no-op");
+ try {
+ channel.shutdown();
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ /* TEST 5: Further sends will throw ClosedChannelException */
+ debug("Test 5: ClosedChannelException");
+ info = MessageInfo.createOutgoing(null, 1);
+ try {
+ channel.send(buffer, info);
+ fail("shutdown not throwing expected ClosedChannelException");
+ } catch (ClosedChannelException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ }finally {
+ finishedLatch.countDown();
+ try { if (channel != null) channel.close(); }
+ catch (IOException e) { unexpected(e);}
+ }
+ }
+
+ class ShutdownServer implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+
+ public ShutdownServer() throws IOException {
+ ssc = SctpServerChannel.open().bind(null);
+ //serverAddr = (InetSocketAddress)(ssc.getAllLocalAddresses().iterator().next());
+
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+
+ }
+
+ public void start() {
+ (new Thread(this, "ShutdownServer-" + serverAddr.getPort())).start();
+ }
+
+ public InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ SctpChannel sc = null;
+ try {
+ sc = ssc.accept();
+
+ /* send a message */
+ MessageInfo info = MessageInfo.createOutgoing(null, 1);
+ ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
+ buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buf.flip();
+ sc.send(buf, info);
+
+ /* notify client that the data has been sent */
+ sentLatch.countDown();
+
+ /* wait until after the client has finished its tests */
+ finishedLatch.await();
+
+ buf.clear();
+ ShutdownNotificationHandler handler =
+ new ShutdownNotificationHandler();
+ BooleanWrapper bool = new BooleanWrapper();
+ sc.configureBlocking(false);
+ sc.receive(buf, bool, handler);
+ check(bool.booleanValue(), "SHUTDOWN not received on Server");
+
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ } finally {
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ try { if (sc != null) sc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+ }
+
+ class BooleanWrapper {
+ boolean bool;
+
+ boolean booleanValue() {
+ return bool;
+ }
+
+ void booleanValue(boolean value) {
+ bool = value;
+ }
+ }
+
+ class ShutdownNotificationHandler extends AbstractNotificationHandler<BooleanWrapper>
+ {
+ @Override
+ public HandlerResult handleNotification(
+ ShutdownNotification sn, BooleanWrapper bool)
+ {
+ bool.booleanValue(true);
+ debug(sn.toString());
+ return HandlerResult.RETURN;
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.List;
+import java.util.Arrays;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static java.lang.System.out;
+
+public class SocketOptionTests {
+ <T> void checkOption(SctpChannel sc, SctpSocketOption<T> name,
+ T expectedValue) throws IOException {
+ T value = sc.getOption(name);
+ check(value.equals(expectedValue), name + ": value (" + value +
+ ") not as expected (" + expectedValue + ")");
+ }
+
+ <T> void optionalSupport(SctpChannel sc, SctpSocketOption<T> name,
+ T value) {
+ try {
+ sc.setOption(name, value);
+ checkOption(sc, name, value);
+ } catch (IOException e) {
+ /* Informational only, not all options have native support */
+ out.println(name + " not supported. " + e);
+ }
+ }
+
+ void test(String[] args) {
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ try {
+ SctpChannel sc = SctpChannel.open();
+
+ /* check supported options */
+ Set<SctpSocketOption<?>> options = sc.supportedOptions();
+ List<? extends SctpSocketOption<?>> expected = Arrays.<SctpSocketOption<?>>asList(
+ SCTP_DISABLE_FRAGMENTS, SCTP_EXPLICIT_COMPLETE,
+ SCTP_FRAGMENT_INTERLEAVE, SCTP_INIT_MAXSTREAMS,
+ SCTP_NODELAY, SCTP_PRIMARY_ADDR, SCTP_SET_PEER_PRIMARY_ADDR,
+ SO_SNDBUF, SO_RCVBUF, SO_LINGER);
+
+ for (SctpSocketOption opt: expected) {
+ if (!options.contains(opt))
+ fail(opt.name() + " should be supported");
+ }
+
+ InitMaxStreams streams = InitMaxStreams.create(1024, 1024);
+ sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
+ checkOption(sc, SCTP_INIT_MAXSTREAMS, streams);
+ streams = sc.getOption(SCTP_INIT_MAXSTREAMS);
+ check(streams.maxInStreams() == 1024, "Max in streams: value: "
+ + streams.maxInStreams() + ", expected 1024 ");
+ check(streams.maxOutStreams() == 1024, "Max out streams: value: "
+ + streams.maxOutStreams() + ", expected 1024 ");
+
+ optionalSupport(sc, SCTP_DISABLE_FRAGMENTS, true);
+ optionalSupport(sc, SCTP_EXPLICIT_COMPLETE, true);
+ optionalSupport(sc, SCTP_FRAGMENT_INTERLEAVE, 1);
+
+
+ //TODO: SCTP_PRIMARY_ADDR
+ //sc.bind(null);
+ //connect
+ //InetSocketAddress addr = new InetSocketAddress(0);
+ //sc.setOption(SCTP_PRIMARY_ADDR, addr);
+
+ sc.setOption(SCTP_NODELAY, true);
+ checkOption(sc, SCTP_NODELAY, true);
+ sc.setOption(SO_SNDBUF, 16*1024);
+ sc.setOption(SO_RCVBUF, 16*1024);
+ checkOption(sc, SO_LINGER, -1); /* default should be negative */
+ sc.setOption(SO_LINGER, 2000);
+ checkOption(sc, SO_LINGER, 2000);
+
+
+ /* NullPointerException */
+ try {
+ sc.setOption(null, "value");
+ fail("NullPointerException not thrown for setOption");
+ } catch (NullPointerException unused) {
+ pass();
+ }
+ try {
+ sc.getOption(null);
+ fail("NullPointerException not thrown for getOption");
+ } catch (NullPointerException unused) {
+ pass();
+ }
+
+ /* ClosedChannelException */
+ sc.close();
+ try {
+ sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
+ fail("ClosedChannelException not thrown");
+ } catch (ClosedChannelException unused) {
+ pass();
+ }
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Util.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+ static final int SMALL_BUFFER = 128;
+ static final String SMALL_MESSAGE =
+ "Under the bridge and over the dam, looking for berries, berries for jam";
+
+ static final int LARGE_BUFFER = 32768;
+ static final String LARGE_MESSAGE;
+
+ static {
+ StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+ for (int i=0; i<460; i++)
+ sb.append(SMALL_MESSAGE);
+
+ LARGE_MESSAGE = sb.toString();
+ }
+
+ static boolean isSCTPSupported() {
+ try {
+ SctpChannel c = SctpChannel.open();
+ c.close();
+ return true;
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } catch (UnsupportedOperationException e) {
+ out.println(e);
+ }
+
+ return false;
+ }
+ /**
+ * Returns a list of all the addresses on the system.
+ * @param inclLoopback
+ * if {@code true}, include the loopback addresses
+ * @param ipv4Only
+ * it {@code true}, only IPv4 addresses will be included
+ */
+ static List<InetAddress> getAddresses(boolean inclLoopback,
+ boolean ipv4Only)
+ throws SocketException {
+ ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+ Enumeration<NetworkInterface> nets =
+ NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netInf : Collections.list(nets)) {
+ Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+ for (InetAddress addr : Collections.list(addrs)) {
+ if (!list.contains(addr) &&
+ (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+ (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+ list.add(addr);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ static void dumpAddresses(SctpChannel channel,
+ PrintStream printStream)
+ throws IOException {
+ Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+ printStream.println("Local Addresses: ");
+ for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ printStream.println("\t" + addr);
+ }
+ }
+
+ /**
+ * Compare the contents of the given ByteBuffer with the contens of the
+ * given byte array. true if, and only if, the contents are the same.
+ */
+ static boolean compare(ByteBuffer bb, byte[] message) {
+ if (message.length != bb.remaining()) {
+ out.println("Compare failed, byte array length != to buffer remaining");
+ return false;
+ }
+
+ for (int i=0; i<message.length; i++) {
+ byte b = bb.get();
+ if (message[i] != b) {
+ out.println("Position " + i + ": " + message[i] + " != " + b);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static boolean compare(ByteBuffer bb, String str) {
+ try{
+ return Util.compare(bb, str.getBytes("ISO-8859-1"));
+ } catch (UnsupportedEncodingException unsupported) {
+ throw new AssertionError(unsupported);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/Send.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpMultiChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Send {
+ /* Latches used to synchronize between the client and server so that
+ * connections without any IO may not be closed without being accepted */
+ final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+ final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ Server server = null;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new Server();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doTest(address);
+ }
+
+ void doTest(SocketAddress peerAddress) {
+ SctpMultiChannel channel = null;
+ ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+ MessageInfo info = MessageInfo.createOutgoing(null, 0);
+
+ try {
+ channel = SctpMultiChannel.open();
+
+ /* TEST 1: send small message */
+ int streamNumber = 0;
+ debug("sending to " + peerAddress + " on stream number: " + streamNumber);
+ info = MessageInfo.createOutgoing(peerAddress, streamNumber);
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ int position = buffer.position();
+ int remaining = buffer.remaining();
+
+ debug("sending small message: " + buffer);
+ int sent = channel.send(buffer, info);
+
+ check(sent == remaining, "sent should be equal to remaining");
+ check(buffer.position() == (position + sent),
+ "buffers position should have been incremented by sent");
+
+ /* TEST 2: receive the echoed message */
+ buffer.clear();
+ info = channel.receive(buffer, null, null);
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.streamNumber() == streamNumber,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.SMALL_MESSAGE),
+ "received message not the same as sent message");
+
+
+ /* TEST 3: send large message */
+ Set<Association> assocs = channel.associations();
+ check(assocs.size() == 1, "there should be only one association");
+ Iterator<Association> it = assocs.iterator();
+ check(it.hasNext());
+ Association assoc = it.next();
+ streamNumber = assoc.maxOutboundStreams() - 1;
+
+ debug("sending on stream number: " + streamNumber);
+ info = MessageInfo.createOutgoing(assoc, null, streamNumber);
+ buffer.clear();
+ buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ position = buffer.position();
+ remaining = buffer.remaining();
+
+ debug("sending large message: " + buffer);
+ sent = channel.send(buffer, info);
+
+ check(sent == remaining, "sent should be equal to remaining");
+ check(buffer.position() == (position + sent),
+ "buffers position should have been incremented by sent");
+
+ /* TEST 4: receive the echoed message */
+ buffer.clear();
+ info = channel.receive(buffer, null, null);
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.streamNumber() == streamNumber,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.LARGE_MESSAGE),
+ "received message not the same as sent message");
+
+
+ /* TEST 5: InvalidStreamExcepton */
+ streamNumber = assoc.maxOutboundStreams() + 1;
+ info = MessageInfo.createOutgoing(assoc, null, streamNumber);
+ buffer.clear();
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+ position = buffer.position();
+ remaining = buffer.remaining();
+
+ debug("sending on stream number: " + streamNumber);
+ debug("sending small message: " + buffer);
+ try {
+ sent = channel.send(buffer, info);
+ fail("should have thrown InvalidStreamExcepton");
+ } catch (InvalidStreamException ise){
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ check(buffer.remaining() == remaining,
+ "remaining should not be changed");
+ check(buffer.position() == position,
+ "buffers position should not be changed");
+
+
+ /* TEST 5: getRemoteAddresses(Association) */
+ channel.getRemoteAddresses(assoc);
+
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ clientFinishedLatch.countDown();
+ try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+ catch (InterruptedException ie) { unexpected(ie); }
+ if (channel != null) {
+ try { channel.close(); }
+ catch (IOException e) { unexpected (e);}
+ }
+ }
+ }
+
+ class Server implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpMultiChannel serverChannel;
+
+ public Server() throws IOException {
+ serverChannel = SctpMultiChannel.open().bind(null);
+ java.util.Set<SocketAddress> addrs = serverChannel.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+ }
+
+ public void start() {
+ (new Thread(this, "Server-" + serverAddr.getPort())).start();
+ }
+
+ public InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+ try {
+ MessageInfo info;
+
+ /* receive a small message */
+ do {
+ info = serverChannel.receive(buffer, null, null);
+ if (info == null) {
+ fail("Server: unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+ check(info != null, "info is null");
+ check(info.streamNumber() == 0,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.SMALL_MESSAGE),
+ "received message not the same as sent message");
+
+ check(info != null, "info is null");
+ Set<Association> assocs = serverChannel.associations();
+ check(assocs.size() == 1, "there should be only one association");
+ Iterator<Association> it = assocs.iterator();
+ check(it.hasNext());
+ Association assoc = it.next();
+
+ /* echo the message */
+ debug("Server: echoing first message");
+ buffer.flip();
+ int bytes = serverChannel.send(buffer, info);
+ debug("Server: sent " + bytes + "bytes");
+
+ /* receive a large message */
+ buffer.clear();
+ do {
+ info = serverChannel.receive(buffer, null, null);
+ if (info == null) {
+ fail("Server: unexpected null from receive");
+ return;
+ }
+ } while (!info.isComplete());
+
+ buffer.flip();
+
+ check(info.streamNumber() == assoc.maxInboundStreams() - 1,
+ "message not sent on the correct stream");
+ check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+ length, "bytes received not equal to message length");
+ check(info.bytes() == buffer.remaining(), "bytes != remaining");
+ check(Util.compare(buffer, Util.LARGE_MESSAGE),
+ "received message not the same as sent message");
+
+ /* echo the message */
+ debug("Server: echoing second message");
+ buffer.flip();
+ bytes = serverChannel.send(buffer, info);
+ debug("Server: sent " + bytes + "bytes");
+
+ clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+ serverFinishedLatch.countDown();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ } finally {
+ try { if (serverChannel != null) serverChannel.close(); }
+ catch (IOException unused) {}
+ }
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { System.out.println(message); } }
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/Util.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+ static final int SMALL_BUFFER = 128;
+ static final String SMALL_MESSAGE =
+ "Under the bridge and over the dam, looking for berries, berries for jam";
+
+ static final int LARGE_BUFFER = 32768;
+ static final String LARGE_MESSAGE;
+
+ static {
+ StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+ for (int i=0; i<460; i++)
+ sb.append(SMALL_MESSAGE);
+
+ LARGE_MESSAGE = sb.toString();
+ }
+
+ static boolean isSCTPSupported() {
+ try {
+ SctpChannel c = SctpChannel.open();
+ c.close();
+ return true;
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } catch (UnsupportedOperationException e) {
+ out.println(e);
+ }
+
+ return false;
+ }
+ /**
+ * Returns a list of all the addresses on the system.
+ * @param inclLoopback
+ * if {@code true}, include the loopback addresses
+ * @param ipv4Only
+ * it {@code true}, only IPv4 addresses will be included
+ */
+ static List<InetAddress> getAddresses(boolean inclLoopback,
+ boolean ipv4Only)
+ throws SocketException {
+ ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+ Enumeration<NetworkInterface> nets =
+ NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netInf : Collections.list(nets)) {
+ Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+ for (InetAddress addr : Collections.list(addrs)) {
+ if (!list.contains(addr) &&
+ (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+ (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+ list.add(addr);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ static void dumpAddresses(SctpChannel channel,
+ PrintStream printStream)
+ throws IOException {
+ Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+ printStream.println("Local Addresses: ");
+ for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ printStream.println("\t" + addr);
+ }
+ }
+
+ /**
+ * Compare the contents of the given ByteBuffer with the contens of the
+ * given byte array. true if, and only if, the contents are the same.
+ */
+ static boolean compare(ByteBuffer bb, byte[] message) {
+ if (message.length != bb.remaining()) {
+ out.println("Compare failed, byte array length != to buffer remaining");
+ return false;
+ }
+
+ for (int i=0; i<message.length; i++) {
+ byte b = bb.get();
+ if (message[i] != b) {
+ out.println("Position " + i + ": " + message[i] + " != " + b);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static boolean compare(ByteBuffer bb, String str) {
+ try{
+ return Util.compare(bb, str.getBytes("ISO-8859-1"));
+ } catch (UnsupportedEncodingException unsupported) {
+ throw new AssertionError(unsupported);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/Accept.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Accept {
+ static CountDownLatch acceptLatch = new CountDownLatch(1);
+ static CountDownLatch closeByIntLatch = new CountDownLatch(1);
+ static CountDownLatch asyncCloseLatch = new CountDownLatch(1);
+ AcceptServer server = null;
+
+ void test(String[] args) {
+ SocketAddress address = null;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new AcceptServer();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doClient(address);
+ }
+
+ void doClient(SocketAddress peerAddress) {
+ SctpChannel channel = null;
+
+ try {
+ channel = SctpChannel.open(peerAddress, 0, 0);
+ acceptLatch.await();
+
+ /* for test 4 */
+ closeByIntLatch.await();
+ sleep(500);
+ server.thread().interrupt();
+
+ /* for test 5 */
+ asyncCloseLatch.await();
+ sleep(500);
+ server.channel().close();
+
+ /* wait for the server thread to finish */
+ join(server.thread(), 10000);
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ } finally {
+ try { if (channel != null) channel.close(); }
+ catch (IOException e) { unexpected(e);}
+ }
+ }
+
+ class AcceptServer implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+ private Thread serverThread;
+
+ public AcceptServer() throws IOException {
+ ssc = SctpServerChannel.open();
+
+ /* TEST 1: NotYetBoundException */
+ debug("TEST 1: NotYetBoundException");
+ try {
+ ssc.accept();
+ fail();
+ } catch (NotYetBoundException nybe) {
+ debug(" caught NotYetBoundException");
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
+ ssc.bind(null);
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+
+ /* TEST 2: null if this channel is in non-blocking mode and no
+ * association is available to be accepted */
+ ssc.configureBlocking(false);
+ debug("TEST 2: non-blocking mode null");
+ try {
+ SctpChannel sc = ssc.accept();
+ check(sc == null, "non-blocking mode should return null");
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } finally {
+ ssc.configureBlocking(true);
+ }
+ }
+
+ void start() {
+ serverThread = new Thread(this, "AcceptServer-" +
+ serverAddr.getPort());
+ serverThread.start();
+ }
+
+ InetSocketAddress address() {
+ return serverAddr;
+ }
+
+ SctpServerChannel channel() {
+ return ssc;
+ }
+
+ Thread thread() {
+ return serverThread;
+ }
+
+ @Override
+ public void run() {
+ SctpChannel sc = null;
+ try {
+ /* TEST 3: accepted channel */
+ debug("TEST 3: accepted channel");
+ sc = ssc.accept();
+
+ checkAcceptedChannel(sc);
+ acceptLatch.countDown();
+
+ /* TEST 4: ClosedByInterruptException */
+ debug("TEST 4: ClosedByInterruptException");
+ try {
+ closeByIntLatch.countDown();
+ ssc.accept();
+ fail();
+ } catch (ClosedByInterruptException unused) {
+ debug(" caught ClosedByInterruptException");
+ pass();
+ }
+
+ /* TEST 5: AsynchronousCloseException */
+ debug("TEST 5: AsynchronousCloseException");
+ /* reset thread interrupt status */
+ Thread.currentThread().interrupted();
+
+ ssc = SctpServerChannel.open().bind(null);
+ try {
+ asyncCloseLatch.countDown();
+ ssc.accept();
+ fail();
+ } catch (AsynchronousCloseException unused) {
+ debug(" caught AsynchronousCloseException");
+ pass();
+ }
+
+ /* TEST 6: ClosedChannelException */
+ debug("TEST 6: ClosedChannelException");
+ try {
+ ssc.accept();
+ fail();
+ } catch (ClosedChannelException unused) {
+ debug(" caught ClosedChannelException");
+ pass();
+ }
+ ssc = null;
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } finally {
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ try { if (sc != null) sc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+ }
+
+ void checkAcceptedChannel(SctpChannel sc) {
+ try {
+ debug("Checking accepted SctpChannel");
+ check(sc.association() != null,
+ "accepted channel should have an association");
+ check(!(sc.getRemoteAddresses().isEmpty()),
+ "accepted channel should be connected");
+ check(!(sc.isConnectionPending()),
+ "accepted channel should not have a connection pending");
+ check(sc.isBlocking(),
+ "accepted channel should be blocking");
+ try { sc.connect(new TestSocketAddress()); fail(); }
+ catch (AlreadyConnectedException unused) { pass(); }
+ try { sc.bind(new TestSocketAddress()); fail(); }
+ catch (AlreadyConnectedException unused) { pass(); }
+ } catch (IOException unused) { fail(); }
+ }
+
+ static class TestSocketAddress extends SocketAddress {}
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { out.println(message); } }
+ void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
+ catch(InterruptedException ie) { unexpected(ie); }}
+ void join(Thread thread, long millis) { try { thread.join(millis); }
+ catch(InterruptedException ie) { unexpected(ie); }}
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.Selector;
+import java.nio.channels.SelectionKey;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class NonBlockingAccept {
+ static CountDownLatch acceptLatch = new CountDownLatch(1);
+ static final int SEL_TIMEOUT = 10000;
+ static final int NUM_TEST_CONNECTIONS = 10;
+
+ void test(String[] args) {
+ SocketAddress address = null;
+ NonblockingServer server;
+
+ if (!Util.isSCTPSupported()) {
+ out.println("SCTP protocol is not supported");
+ out.println("Test cannot be run");
+ return;
+ }
+
+ if (args.length == 2) {
+ /* requested to connecct to a specific address */
+ try {
+ int port = Integer.valueOf(args[1]);
+ address = new InetSocketAddress(args[0], port);
+ } catch (NumberFormatException nfe) {
+ err.println(nfe);
+ }
+ } else {
+ /* start server on local machine, default */
+ try {
+ server = new NonblockingServer();
+ server.start();
+ address = server.address();
+ debug("Server started and listening on " + address);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return;
+ }
+ }
+
+ doClient(address);
+ }
+
+ void doClient(SocketAddress peerAddress) {
+ Set<SctpChannel> channels = new HashSet<SctpChannel>(NUM_TEST_CONNECTIONS);
+
+ try {
+ for (int i=0; i<NUM_TEST_CONNECTIONS;) {
+ debug("connecting " + ++i);
+ channels.add(SctpChannel.open(peerAddress, 0, 0));
+ sleep(100);
+ }
+
+ /* don't close the channels until they have been accepted */
+ acceptLatch.await();
+
+ for(SctpChannel sc: channels)
+ sc.close();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ } catch (InterruptedException ie) {
+ unexpected(ie);
+ }
+ }
+
+ class NonblockingServer implements Runnable
+ {
+ final InetSocketAddress serverAddr;
+ private SctpServerChannel ssc;
+ private Thread serverThread;
+
+ public NonblockingServer() throws IOException {
+ ssc = SctpServerChannel.open().bind(null);
+ java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ serverAddr = (InetSocketAddress) addrs.iterator().next();
+ }
+
+ void start() {
+ serverThread = new Thread(this, "NonblockingServer-" +
+ serverAddr.getPort());
+ serverThread.start();
+ }
+
+ InetSocketAddress address () {
+ return serverAddr;
+ }
+
+ @Override
+ public void run() {
+ Selector acceptSelector = null;
+ SelectionKey acceptKey = null;
+
+ try {
+ acceptSelector = Selector.open();
+ ssc.configureBlocking(false);
+ check(ssc.isBlocking() == false, "Should be in non-blocking mode");
+ acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
+
+ int connectionsAccepted = 0;
+ while (connectionsAccepted < NUM_TEST_CONNECTIONS) {
+ int keysAdded = acceptSelector.select(SEL_TIMEOUT);
+ if (keysAdded > 0) {
+ Set<SelectionKey> keys = acceptSelector.selectedKeys();
+ Iterator<SelectionKey> i = keys.iterator();
+ while(i.hasNext()) {
+ SelectionKey sk = i.next();
+ i.remove();
+ SctpServerChannel nextReady =
+ (SctpServerChannel)sk.channel();
+ check(nextReady.equals(ssc),
+ "channels should be equal");
+ check(sk.isAcceptable(),
+ "key should be acceptable");
+ check(!sk.isReadable(),
+ "key should not be readable");
+ check(!sk.isWritable(),
+ "key should not be writable");
+ check(!sk.isConnectable(),
+ "key should not be connectable");
+ SctpChannel acceptsc = nextReady.accept();
+ connectionsAccepted++;
+ debug("Accepted " + connectionsAccepted + " connections");
+ check(acceptsc != null,
+ "Accepted channel should not be null");
+ if (acceptsc != null) {
+ checkAcceptedChannel(acceptsc);
+ acceptsc.close();
+ }
+ } /* while */
+ } /* if */
+ } /* while */
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } finally {
+ acceptLatch.countDown();
+ if (acceptKey != null) acceptKey.cancel();
+ try { if (acceptSelector != null) acceptSelector.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException ioe) { unexpected(ioe); }
+ }
+ }
+ }
+
+ void checkAcceptedChannel(SctpChannel sc) {
+ try {
+ debug("Checking accepted SctpChannel");
+ check(sc.association() != null,
+ "accepted channel should have an association");
+ check(!(sc.getRemoteAddresses().isEmpty()),
+ "accepted channel should be connected");
+ check(!(sc.isConnectionPending()),
+ "accepted channel should not have a connection pending");
+ check(sc.isBlocking(),
+ "accepted channel should be blocking");
+ try { sc.connect(new TestSocketAddress()); fail(); }
+ catch (AlreadyConnectedException unused) { pass(); }
+ try { sc.bind(new TestSocketAddress()); fail(); }
+ catch (AlreadyConnectedException unused) { pass(); }
+ } catch (IOException unused) { fail(); }
+ }
+
+ static class TestSocketAddress extends SocketAddress {}
+
+ //--------------------- Infrastructure ---------------------------
+ boolean debug = true;
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+ void debug(String message) {if(debug) { out.println(message); } }
+ void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
+ catch(InterruptedException ie) { unexpected(ie); }}
+ public static void main(String[] args) throws Throwable {
+ Class<?> k = new Object(){}.getClass().getEnclosingClass();
+ try {k.getMethod("instanceMain",String[].class)
+ .invoke( k.newInstance(), (Object) args);}
+ catch (Throwable e) {throw e.getCause();}}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/Util.java Wed Jul 05 16:51:32 2017 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.net.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+ static final int SMALL_BUFFER = 128;
+ static final String SMALL_MESSAGE =
+ "Under the bridge and over the dam, looking for berries, berries for jam";
+
+ static final int LARGE_BUFFER = 32768;
+ static final String LARGE_MESSAGE;
+
+ static {
+ StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+ for (int i=0; i<460; i++)
+ sb.append(SMALL_MESSAGE);
+
+ LARGE_MESSAGE = sb.toString();
+ }
+
+ static boolean isSCTPSupported() {
+ try {
+ SctpChannel c = SctpChannel.open();
+ c.close();
+ return true;
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } catch (UnsupportedOperationException e) {
+ out.println(e);
+ }
+
+ return false;
+ }
+ /**
+ * Returns a list of all the addresses on the system.
+ * @param inclLoopback
+ * if {@code true}, include the loopback addresses
+ * @param ipv4Only
+ * it {@code true}, only IPv4 addresses will be included
+ */
+ static List<InetAddress> getAddresses(boolean inclLoopback,
+ boolean ipv4Only)
+ throws SocketException {
+ ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+ Enumeration<NetworkInterface> nets =
+ NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netInf : Collections.list(nets)) {
+ Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+ for (InetAddress addr : Collections.list(addrs)) {
+ if (!list.contains(addr) &&
+ (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+ (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+ list.add(addr);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ static void dumpAddresses(SctpChannel channel,
+ PrintStream printStream)
+ throws IOException {
+ Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+ printStream.println("Local Addresses: ");
+ for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ printStream.println("\t" + addr);
+ }
+ }
+
+ /**
+ * Compare the contents of the given ByteBuffer with the contens of the
+ * given byte array. true if, and only if, the contents are the same.
+ */
+ static boolean compare(ByteBuffer bb, byte[] message) {
+ if (message.length != bb.remaining()) {
+ out.println("Compare failed, byte array length != to buffer remaining");
+ return false;
+ }
+
+ for (int i=0; i<message.length; i++) {
+ byte b = bb.get();
+ if (message[i] != b) {
+ out.println("Position " + i + ": " + message[i] + " != " + b);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static boolean compare(ByteBuffer bb, String str) {
+ try{
+ return Util.compare(bb, str.getBytes("ISO-8859-1"));
+ } catch (UnsupportedEncodingException unsupported) {
+ throw new AssertionError(unsupported);
+ }
+ }
+}