Merge "Update README to add the nullability annotations to ojluni classes"
diff --git a/JavaLibrary.bp b/JavaLibrary.bp
index 4838a11..03d84c2 100644
--- a/JavaLibrary.bp
+++ b/JavaLibrary.bp
@@ -484,6 +484,7 @@
"//frameworks/base/location/tests/locationtests",
"//frameworks/base/core/tests/coretests",
"//frameworks/base/wifi/tests",
+ "//libcore/luni/src/test/java9compatibility",
"//packages/modules/Wifi/framework/tests",
],
hostdex: true,
@@ -655,6 +656,7 @@
static_libs: [
"core-compat-test-rules",
+ "core-java-9-compatibility-tests",
"core-java-9-language-tests",
"core-java-11-language-tests",
"core-test-rules",
diff --git a/api/current.txt b/api/current.txt
index 0f46df2..7de96a1 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5610,6 +5610,7 @@
field public static final int SO_OOBINLINE = 4099; // 0x1003
field public static final int SO_RCVBUF = 4098; // 0x1002
field public static final int SO_REUSEADDR = 4; // 0x4
+ field public static final int SO_REUSEPORT = 14; // 0xe
field public static final int SO_SNDBUF = 4097; // 0x1001
field public static final int SO_TIMEOUT = 4102; // 0x1006
field public static final int TCP_NODELAY = 1; // 0x1
@@ -5641,6 +5642,7 @@
field public static final java.net.SocketOption<java.lang.Integer> SO_LINGER;
field public static final java.net.SocketOption<java.lang.Integer> SO_RCVBUF;
field public static final java.net.SocketOption<java.lang.Boolean> SO_REUSEADDR;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_REUSEPORT;
field public static final java.net.SocketOption<java.lang.Integer> SO_SNDBUF;
field public static final java.net.SocketOption<java.lang.Boolean> TCP_NODELAY;
}
@@ -8784,6 +8786,10 @@
field public static final long serialVersionUID = -8727434096241101194L; // 0x86e1ecedeceab676L
}
+ public interface XECKey {
+ method public java.security.spec.AlgorithmParameterSpec getParams();
+ }
+
}
package java.security.spec {
@@ -8975,6 +8981,18 @@
method public final String getFormat();
}
+ public class XECPrivateKeySpec implements java.security.spec.KeySpec {
+ ctor public XECPrivateKeySpec(java.security.spec.AlgorithmParameterSpec, byte[]);
+ method public java.security.spec.AlgorithmParameterSpec getParams();
+ method public byte[] getScalar();
+ }
+
+ public class XECPublicKeySpec implements java.security.spec.KeySpec {
+ ctor public XECPublicKeySpec(java.security.spec.AlgorithmParameterSpec, java.math.BigInteger);
+ method public java.security.spec.AlgorithmParameterSpec getParams();
+ method public java.math.BigInteger getU();
+ }
+
}
package java.sql {
diff --git a/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java b/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java
index 7a1c20b..4c4f300 100644
--- a/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java
+++ b/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java
@@ -16,6 +16,8 @@
package dalvik.system;
+import sun.invoke.util.Wrapper;
+
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -177,6 +179,20 @@
}
/**
+ * Convert parameter index to index within references array.
+ */
+ int getReferenceIndex(int parameterIndex) {
+ final Class [] ptypes = type.ptypes();
+ int refIndex = 0;
+ for (int i = 0; i < parameterIndex; ++i) {
+ if (!ptypes[i].isPrimitive()) {
+ refIndex += 1;
+ }
+ }
+ return refIndex;
+ }
+
+ /**
* Sets the {@code idx} to {@code reference}. Type checks are performed.
*/
public void setReference(int idx, Object reference) {
@@ -184,12 +200,11 @@
if (idx < 0 || idx >= ptypes.length) {
throw new IllegalArgumentException("Invalid index: " + idx);
}
-
if (reference != null && !ptypes[idx].isInstance(reference)) {
throw new IllegalStateException("reference is not of type: " + type.ptypes()[idx]);
}
-
- references[idx] = reference;
+ int referenceIndex = getReferenceIndex(idx);
+ references[referenceIndex] = reference;
}
/**
@@ -200,8 +215,8 @@
throw new IllegalArgumentException("Argument: " + idx +
" is of type " + type.ptypes()[idx] + " expected " + referenceType + "");
}
-
- return (T) references[idx];
+ int referenceIndex = getReferenceIndex(idx);
+ return (T) references[referenceIndex];
}
/**
@@ -387,26 +402,36 @@
}
}
- public static void copyNext(StackFrameReader reader, StackFrameWriter writer,
- Class<?> type) {
- if (!type.isPrimitive()) {
- writer.putNextReference(reader.nextReference(type), type);
- } else if (type == boolean.class) {
- writer.putNextBoolean(reader.nextBoolean());
- } else if (type == byte.class) {
- writer.putNextByte(reader.nextByte());
- } else if (type == char.class) {
- writer.putNextChar(reader.nextChar());
- } else if (type == short.class) {
- writer.putNextShort(reader.nextShort());
- } else if (type == int.class) {
- writer.putNextInt(reader.nextInt());
- } else if (type == long.class) {
- writer.putNextLong(reader.nextLong());
- } else if (type == float.class) {
- writer.putNextFloat(reader.nextFloat());
- } else if (type == double.class) {
- writer.putNextDouble(reader.nextDouble());
+ public static void copyNext(
+ StackFrameReader reader, StackFrameWriter writer, Class<?> type) {
+ switch (Wrapper.basicTypeChar(type)) {
+ case 'L':
+ writer.putNextReference(reader.nextReference(type), type);
+ break;
+ case 'Z':
+ writer.putNextBoolean(reader.nextBoolean());
+ break;
+ case 'B':
+ writer.putNextByte(reader.nextByte());
+ break;
+ case 'C':
+ writer.putNextChar(reader.nextChar());
+ break;
+ case 'S':
+ writer.putNextShort(reader.nextShort());
+ break;
+ case 'I':
+ writer.putNextInt(reader.nextInt());
+ break;
+ case 'J':
+ writer.putNextLong(reader.nextLong());
+ break;
+ case 'F':
+ writer.putNextFloat(reader.nextFloat());
+ break;
+ case 'D':
+ writer.putNextDouble(reader.nextDouble());
+ break;
}
}
}
diff --git a/luni/src/test/java/libcore/dalvik/system/EmulatedStackFrameTest.java b/luni/src/test/java/libcore/dalvik/system/EmulatedStackFrameTest.java
index c17fad7..8b69016 100644
--- a/luni/src/test/java/libcore/dalvik/system/EmulatedStackFrameTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/EmulatedStackFrameTest.java
@@ -195,7 +195,7 @@
} catch (IllegalArgumentException expected) {
}
- // Should succeeed.
+ // Should succeed.
assertFalse(reader.nextBoolean());
// The next attempt should fail.
@@ -215,7 +215,7 @@
} catch (IllegalArgumentException expected) {
}
- // Should succeeed.
+ // Should succeed.
writer.putNextBoolean(true);
// The next attempt should fail.
@@ -225,4 +225,13 @@
} catch (IllegalArgumentException expected) {
}
}
+
+ public void testGetSetReference() {
+ EmulatedStackFrame stackFrame = EmulatedStackFrame.create(MethodType.methodType(
+ void.class, new Class<?>[] { Integer.class, boolean.class, String.class }));
+ stackFrame.setReference(0, Integer.valueOf(-1));
+ assertEquals(Integer.valueOf(-1), stackFrame.getReference(0, Integer.class));
+ stackFrame.setReference(2, "Hello");
+ assertEquals("Hello", stackFrame.getReference(2, String.class));
+ }
}
diff --git a/luni/src/test/java/libcore/java/io/ObjectStreamClassSuidTest.java b/luni/src/test/java/libcore/java/io/ObjectStreamClassSuidTest.java
new file mode 100644
index 0000000..28ab598
--- /dev/null
+++ b/luni/src/test/java/libcore/java/io/ObjectStreamClassSuidTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.io;
+
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamClass.DefaultSUIDCompatibilityListener;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@RunWith(JUnitParamsRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ObjectStreamClassSuidTest {
+
+ @Rule
+ public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+
+ /**
+ * The default SUID for this should not be affected by the b/29064453 patch.
+ */
+ public static class BaseWithStaticInitializer implements Serializable {
+ static {
+ System.out.println(
+ "Static initializer for " + BaseWithoutStaticInitializer.class.getCanonicalName());
+ }
+ }
+
+ /**
+ * The default SUID for this should not be affected by the b/29064453 patch.
+ */
+ public static class BaseWithoutStaticInitializer implements Serializable {
+ }
+
+ /**
+ * The default SUID for this should not be affected by the b/29064453 patch.
+ */
+ public static class WithStaticInitializer extends BaseWithoutStaticInitializer {
+ static {
+ System.out.println(
+ "Static initializer for " + WithStaticInitializer.class.getCanonicalName());
+ }
+ }
+
+ /**
+ * The default SUID for this should not be affected by the b/29064453 patch.
+ */
+ public static class WithoutStaticInitializer extends BaseWithoutStaticInitializer {
+ }
+
+ /**
+ * The default SUID for this should be affected by the b/29064453 patch and so should differ
+ * between version <= 23 and version > 23.
+ */
+ public static class InheritStaticInitializer extends BaseWithStaticInitializer {
+ }
+
+ public static Object[][] defaultSUIDs() {
+ return new Object[][] {
+ // The default SUID for BaseWithStaticInitializer should not be affected by the b/29064453
+ // patch.
+ { BaseWithStaticInitializer.class, 1857698805282079740L, 1857698805282079740L },
+
+ // The default SUID for BaseWithoutStaticInitializer should not be affected by the
+ // b/29064453 patch.
+ { BaseWithoutStaticInitializer.class, -4805670618654058372L, -4805670618654058372L },
+
+ // The default SUID for WithStaticInitializer should not be affected by the b/29064453
+ // patch.
+ { WithStaticInitializer.class, 8758222524306909802L, 8758222524306909802L },
+
+ // The default SUID for WithStaticInitializer should not be affected by the
+ // b/29064453 patch.
+ { WithoutStaticInitializer.class, -6923417559496792279L, -6923417559496792279L },
+
+ // The default SUID for the InheritStaticInitializer should be affected by the b/29064453
+ // patch and so should differ between version <= 23 and version > 23.
+ { InheritStaticInitializer.class, 509356435664048990L, -6712883765570708525L },
+ };
+ }
+
+ @Parameters(method = "defaultSUIDs")
+ @Test
+ public void computeDefaultSUID_current(Class<?> clazz, long suid,
+ @SuppressWarnings("unused") long suid23) {
+ checkSerialVersionUID(suid, clazz, false);
+ }
+
+ @Parameters(method = "defaultSUIDs")
+ @Test
+ @TargetSdkVersion(23)
+ public void computeDefaultSUID_targetSdkVersion_23(Class<?> clazz, long suid, long suid23) {
+ // If the suid and suid23 hashes are different then a warning is expected to be logged.
+ boolean expectedWarning = suid23 != suid;
+ checkSerialVersionUID(suid23, clazz, expectedWarning);
+ }
+
+ private static void checkSerialVersionUID(
+ long expectedSUID, Class<?> clazz, boolean expectedWarning) {
+ // Use reflection to call the private static computeDefaultSUID method directly to avoid the
+ // caching performed by ObjectStreamClass.lookup(Class).
+ long defaultSUID;
+ DefaultSUIDCompatibilityListener savedListener
+ = ObjectStreamClass.suidCompatibilityListener;
+ try {
+ ObjectStreamClass.suidCompatibilityListener = (c, hash) -> {
+ // Delegate to the existing listener so that the warning is logged.
+ savedListener.warnDefaultSUIDTargetVersionDependent(clazz, hash);
+ if (expectedWarning) {
+ assertEquals(clazz, c);
+ assertEquals(expectedSUID, hash);
+ } else {
+ fail("Unexpected warning for " + c + " with defaultSUID " + hash);
+ }
+ };
+
+ Method computeDefaultSUIDMethod =
+ ObjectStreamClass.class.getDeclaredMethod("computeDefaultSUID", Class.class);
+ computeDefaultSUIDMethod.setAccessible(true);
+
+ defaultSUID = (Long) computeDefaultSUIDMethod.invoke(null, clazz);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ ObjectStreamClass.suidCompatibilityListener = savedListener;
+ }
+ assertEquals(expectedSUID, defaultSUID);
+ }
+}
diff --git a/luni/src/test/java9compatibility/Android.bp b/luni/src/test/java9compatibility/Android.bp
new file mode 100644
index 0000000..1d5dab5
--- /dev/null
+++ b/luni/src/test/java9compatibility/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Android tests related to compatibility with pre-Java 11 language features.
+
+java_library {
+ name: "core-java-9-compatibility-tests",
+ hostdex: true,
+ srcs: [
+ "java/**/*.java",
+ ],
+ java_version: "1.9",
+ sdk_version: "none",
+ system_modules: "core-all-system-modules",
+ static_libs: [
+ "core-test-rules",
+ "junit",
+ "junit-params",
+ ],
+ visibility: ["//libcore"],
+}
diff --git a/luni/src/test/java/libcore/java/io/ObjectStreamClassTest.java b/luni/src/test/java9compatibility/java/libcore/java/io/ObjectStreamClassTest.java
similarity index 100%
rename from luni/src/test/java/libcore/java/io/ObjectStreamClassTest.java
rename to luni/src/test/java9compatibility/java/libcore/java/io/ObjectStreamClassTest.java
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandle.java b/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
index 17387a6..cb9eaa7 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
@@ -434,35 +434,26 @@
// @interface PolymorphicSignature { }
public @interface PolymorphicSignature { }
- // Android-added: Comment to differentiate between type and nominalType.
/**
* The type of this method handle, this corresponds to the exact type of the method
* being invoked.
- *
- * @see #nominalType
*/
private final MethodType type;
- // Android-removed: LambdaForm and customizationCount unused on Android.
+
+ // Android-removed: LambdaForm is unused on Android.
// They will be substituted with appropriate implementation / delegate classes.
/*
/*private* final LambdaForm form;
// form is not private so that invokers can easily fetch it
- /*private* MethodHandle asTypeCache;
+ */
+ /*private*/ MethodHandle asTypeCache;
// asTypeCache is not private so that invokers can easily fetch it
+ /*
+ // Android-removed: customizationCount is unused on Android.
/*non-public* byte customizationCount;
// customizationCount should be accessible from invokers
*/
- // BEGIN Android-added: Android specific implementation.
- // The MethodHandle functionality is tightly coupled with internal details of the runtime and
- // so Android has a completely different implementation compared to the RI.
- /**
- * The nominal type of this method handle, will be non-null if a method handle declares
- * a different type from its "real" type, which is either the type of the method being invoked
- * or the type of the emulated stackframe expected by an underyling adapter.
- */
- private MethodType nominalType;
-
/**
* The spread invoker associated with this type with zero trailing arguments.
* This is used to speed up invokeWithArguments.
@@ -522,11 +513,6 @@
* @return the method handle type
*/
public MethodType type() {
- // Android-added: Added nominalType field.
- if (nominalType != null) {
- return nominalType;
- }
-
return type;
}
@@ -715,19 +701,15 @@
* @see MethodHandles#spreadInvoker
*/
public Object invokeWithArguments(Object... arguments) throws Throwable {
+ MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
// BEGIN Android-changed: Android specific implementation.
- // MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
// return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
- MethodHandle invoker = null;
- synchronized (this) {
- if (cachedSpreadInvoker == null) {
- cachedSpreadInvoker = MethodHandles.spreadInvoker(this.type(), 0);
- }
-
- invoker = cachedSpreadInvoker;
+ MethodHandle invoker = cachedSpreadInvoker;
+ if (invoker == null || !invoker.type().equals(invocationType)) {
+ invoker = MethodHandles.spreadInvoker(invocationType, 0);
+ cachedSpreadInvoker = invoker;
}
-
- return invoker.invoke(this, arguments);
+ return invoker.invoke(asType(invocationType), arguments);
// END Android-changed: Android specific implementation.
}
@@ -854,31 +836,26 @@
public MethodHandle asType(MethodType newType) {
// Fast path alternative to a heavyweight {@code asType} call.
// Return 'this' if the conversion will be a no-op.
- // Android-changed: Use `type()` rather than `type` due to nominal type.
- if (newType == type()) {
+ // Android-changed: use equals() rather than = since MethodTypes are not interned.
+ if (newType.equals(type)) {
return this;
}
- // Android-removed: Type conversion memoizing is unsupported on Android.
- /*
// Return 'this.asTypeCache' if the conversion is already memoized.
MethodHandle atc = asTypeCached(newType);
if (atc != null) {
return atc;
}
- */
return asTypeUncached(newType);
}
- // Android-removed: Type conversion memoizing is unsupported on Android.
- /*
private MethodHandle asTypeCached(MethodType newType) {
MethodHandle atc = asTypeCache;
- if (atc != null && newType == atc.type) {
+ // Android-changed: use equals() rather than = since MethodTypes are not interned.
+ if (atc != null && newType.equals(atc.type)) {
return atc;
}
return null;
}
- */
/** Override this to change asType behavior. */
/*non-public*/ MethodHandle asTypeUncached(MethodType newType) {
@@ -886,9 +863,7 @@
throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
// BEGIN Android-changed: Android specific implementation.
// return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, true);
- MethodHandle mh = duplicate();
- mh.nominalType = newType;
- return mh;
+ return asTypeCache = new Transformers.AsTypeAdapter(this, newType);
// END Android-changed: Android specific implementation.
}
@@ -1415,8 +1390,7 @@
* @see MethodHandles#insertArguments
*/
public MethodHandle bindTo(Object x) {
- // Android-changed: use `type()` instead of `type` due to nominal type.
- x = type().leadingReferenceParameter().cast(x); // throw CCE if needed
+ x = type.leadingReferenceParameter().cast(x); // throw CCE if needed
// Android-changed: Android specific implementation.
// return bindArgumentL(0, x);
return new Transformers.BindTo(this, x);
@@ -1444,8 +1418,7 @@
return standardString();
}
String standardString() {
- // Android-changed: use `type()` rather than `type` due to Android's nominal type.
- return "MethodHandle"+type();
+ return "MethodHandle"+type;
}
// BEGIN Android-removed: Debugging support unused on Android.
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodType.java b/ojluni/src/main/java/java/lang/invoke/MethodType.java
index 4652c93..8bf0839 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodType.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodType.java
@@ -842,11 +842,12 @@
/*non-public*/
boolean isConvertibleTo(MethodType newType) {
- MethodTypeForm oldForm = this.form();
- MethodTypeForm newForm = newType.form();
- if (oldForm == newForm)
- // same parameter count, same primitive/object mix
- return true;
+ // Android-removed: use of MethodTypeForm does not apply to Android implementation.
+ // MethodTypeForm oldForm = this.form();
+ // MethodTypeForm newForm = newType.form();
+ // if (oldForm == newForm)
+ // // same parameter count, same primitive/object mix
+ // return true;
if (!canConvert(returnType(), newType.returnType()))
return false;
Class<?>[] srcTypes = newType.ptypes;
@@ -861,13 +862,14 @@
return false;
return true;
}
- if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
- (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
- // Somewhat complicated test to avoid a loop of 2 or more trips.
- // If either type has only Object parameters, we know we can convert.
- assert(canConvertParameters(srcTypes, dstTypes));
- return true;
- }
+ // Android-removed: use of MethodTypeForm does not apply to Android implementation.
+ // if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
+ // (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
+ // // Somewhat complicated test to avoid a loop of 2 or more trips.
+ // // If either type has only Object parameters, we know we can convert.
+ // assert(canConvertParameters(srcTypes, dstTypes));
+ // return true;
+ // }
return canConvertParameters(srcTypes, dstTypes);
}
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 59bb159..be0c3e1 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -21,41 +21,43 @@
package java.lang.invoke;
+import static dalvik.system.EmulatedStackFrame.StackFrameAccessor.copyNext;
+
import dalvik.system.EmulatedStackFrame;
import dalvik.system.EmulatedStackFrame.Range;
import dalvik.system.EmulatedStackFrame.StackFrameAccessor;
import dalvik.system.EmulatedStackFrame.StackFrameReader;
import dalvik.system.EmulatedStackFrame.StackFrameWriter;
+
+import sun.invoke.util.Wrapper;
+import sun.misc.Unsafe;
+
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import sun.invoke.util.Wrapper;
-import sun.misc.Unsafe;
-import static dalvik.system.EmulatedStackFrame.StackFrameAccessor.copyNext;
-/**
- * @hide Public for testing only.
- */
+/** @hide Public for testing only. */
public class Transformers {
private Transformers() {}
static {
try {
- TRANSFORM_INTERNAL = MethodHandle.class.getDeclaredMethod("transformInternal",
- EmulatedStackFrame.class);
+ TRANSFORM_INTERNAL =
+ MethodHandle.class.getDeclaredMethod(
+ "transformInternal", EmulatedStackFrame.class);
} catch (NoSuchMethodException nsme) {
throw new AssertionError();
}
}
/**
- * Method reference to the private {@code MethodHandle.transformInternal} method. This is
- * cached here because it's the point of entry for all transformers.
+ * Method reference to the private {@code MethodHandle.transformInternal} method. This is cached
+ * here because it's the point of entry for all transformers.
*/
private static final Method TRANSFORM_INTERNAL;
/** @hide */
- public static abstract class Transformer extends MethodHandle implements Cloneable {
+ public abstract static class Transformer extends MethodHandle implements Cloneable {
protected Transformer(MethodType type) {
super(TRANSFORM_INTERNAL.getArtMethod(), MethodHandle.INVOKE_TRANSFORM, type);
}
@@ -68,20 +70,38 @@
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
+
+ protected void invokeFromTransform(MethodHandle target, EmulatedStackFrame stackFrame)
+ throws Throwable {
+ if (target instanceof Transformer) {
+ ((Transformer) target).transform(stackFrame);
+ } else {
+ target.invoke(stackFrame);
+ }
+ }
+
+ protected void invokeExactFromTransform(MethodHandle target, EmulatedStackFrame stackFrame)
+ throws Throwable {
+ if (target instanceof Transformer) {
+ ((Transformer) target).transform(stackFrame);
+ } else {
+ target.invokeExact(stackFrame);
+ }
+ }
}
/**
* A method handle that always throws an exception of a specified type.
*
- * The handle declares a nominal return type, which is immaterial to the execution
- * of the handle because it never returns.
+ * <p>The handle declares a nominal return type, which is immaterial to the execution of the
+ * handle because it never returns.
*
* @hide
*/
public static class AlwaysThrow extends Transformer {
private final Class<? extends Throwable> exceptionType;
- public AlwaysThrow(Class<?> nominalReturnType, Class<? extends Throwable> exType) {
+ public AlwaysThrow(Class<?> nominalReturnType, Class<? extends Throwable> exType) {
super(MethodType.methodType(nominalReturnType, exType));
this.exceptionType = exType;
}
@@ -92,22 +112,19 @@
}
}
- /**
- * Implements {@code MethodHandles.dropArguments}.
- */
+ /** Implements {@code MethodHandles.dropArguments}. */
public static class DropArguments extends Transformer {
private final MethodHandle delegate;
private final EmulatedStackFrame.Range range1;
/**
- * Note that {@code range2} will be null if the arguments that are being dropped
- * are the last {@code n}.
+ * Note that {@code range2} will be null if the arguments that are being dropped are the
+ * last {@code n}.
*/
/* @Nullable */ private final EmulatedStackFrame.Range range2;
- public DropArguments(MethodType type, MethodHandle delegate,
- int startPos, int numDropped) {
+ public DropArguments(MethodType type, MethodHandle delegate, int startPos, int numDropped) {
super(type);
this.delegate = delegate;
@@ -127,25 +144,23 @@
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(delegate.type());
- emulatedStackFrame.copyRangeTo(calleeFrame, range1,
- 0 /* referencesStart */, 0 /* stackFrameStart */);
+ emulatedStackFrame.copyRangeTo(
+ calleeFrame, range1, 0 /* referencesStart */, 0 /* stackFrameStart */);
if (range2 != null) {
final int referencesStart = range1.numReferences;
final int stackFrameStart = range1.numBytes;
- emulatedStackFrame.copyRangeTo(calleeFrame, range2,
- referencesStart, stackFrameStart);
+ emulatedStackFrame.copyRangeTo(
+ calleeFrame, range2, referencesStart, stackFrameStart);
}
- delegate.invoke(calleeFrame);
+ invokeFromTransform(delegate, calleeFrame);
calleeFrame.copyReturnValueTo(emulatedStackFrame);
}
}
- /**
- * Implements {@code MethodHandles.catchException}.
- */
+ /** Implements {@code MethodHandles.catchException}. */
public static class CatchException extends Transformer {
private final MethodHandle target;
private final MethodHandle handler;
@@ -163,14 +178,15 @@
// We only copy the first "count" args, dropping others if required. Note that
// we subtract one because the first handler arg is the exception thrown by the
// target.
- handlerArgsRange = EmulatedStackFrame.Range.of(target.type(), 0,
- (handler.type().parameterCount() - 1));
+ handlerArgsRange =
+ EmulatedStackFrame.Range.of(
+ target.type(), 0, (handler.type().parameterCount() - 1));
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
try {
- target.invoke(emulatedStackFrame);
+ invokeFromTransform(target, emulatedStackFrame);
} catch (Throwable th) {
if (th.getClass() == exType) {
// We've gotten an exception of the appropriate type, so we need to call
@@ -183,11 +199,14 @@
// We then copy other arguments that need to be passed through to the handler.
// Note that we might drop arguments at the end, if needed. Note that
// referencesStart == 1 because the first argument is the exception type.
- emulatedStackFrame.copyRangeTo(fallback, handlerArgsRange,
- 1 /* referencesStart */, 0 /* stackFrameStart */);
+ emulatedStackFrame.copyRangeTo(
+ fallback,
+ handlerArgsRange,
+ 1 /* referencesStart */,
+ 0 /* stackFrameStart */);
// Perform the invoke and return the appropriate value.
- handler.invoke(fallback);
+ invokeFromTransform(handler, fallback);
fallback.copyReturnValueTo(emulatedStackFrame);
} else {
// The exception is not of the expected type, we throw it.
@@ -197,9 +216,7 @@
}
}
- /**
- * Implements {@code MethodHandles.GuardWithTest}.
- */
+ /** Implements {@code MethodHandles.GuardWithTest}. */
public static class GuardWithTest extends Transformer {
private final MethodHandle test;
private final MethodHandle target;
@@ -215,7 +232,8 @@
this.fallback = fallback;
// The test method might have a subset of the arguments of the handle / target.
- testArgsRange = EmulatedStackFrame.Range.of(target.type(), 0, test.type().parameterCount());
+ testArgsRange =
+ EmulatedStackFrame.Range.of(target.type(), 0, test.type().parameterCount());
}
@Override
@@ -223,26 +241,28 @@
EmulatedStackFrame testFrame = EmulatedStackFrame.create(test.type());
emulatedStackFrame.copyRangeTo(testFrame, testArgsRange, 0, 0);
- // We know that the return value for test is going to be boolean.class, so we don't have
- // to do the copyReturnValue dance.
- final boolean value = (boolean) test.invoke(testFrame);
- if (value) {
- target.invoke(emulatedStackFrame);
+ // We know that the return value for test is going to be boolean.class.
+ StackFrameReader reader = new StackFrameReader();
+ reader.attach(testFrame);
+ reader.makeReturnValueAccessor();
+ invokeFromTransform(test, testFrame);
+ final boolean testResult = (boolean) reader.nextBoolean();
+ if (testResult) {
+ invokeFromTransform(target, emulatedStackFrame);
} else {
- fallback.invoke(emulatedStackFrame);
+ invokeFromTransform(fallback, emulatedStackFrame);
}
}
}
- /**
- * Implementation of MethodHandles.arrayElementGetter for reference types.
- */
+ /** Implementation of MethodHandles.arrayElementGetter for reference types. */
public static class ReferenceArrayElementGetter extends Transformer {
private final Class<?> arrayClass;
public ReferenceArrayElementGetter(Class<?> arrayClass) {
- super(MethodType.methodType(arrayClass.getComponentType(),
- new Class<?>[]{arrayClass, int.class}));
+ super(
+ MethodType.methodType(
+ arrayClass.getComponentType(), new Class<?>[] {arrayClass, int.class}));
this.arrayClass = arrayClass;
}
@@ -263,15 +283,15 @@
}
}
- /**
- * Implementation of MethodHandles.arrayElementSetter for reference types.
- */
+ /** Implementation of MethodHandles.arrayElementSetter for reference types. */
public static class ReferenceArrayElementSetter extends Transformer {
private final Class<?> arrayClass;
public ReferenceArrayElementSetter(Class<?> arrayClass) {
- super(MethodType.methodType(void.class,
- new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() }));
+ super(
+ MethodType.methodType(
+ void.class,
+ new Class<?>[] {arrayClass, int.class, arrayClass.getComponentType()}));
this.arrayClass = arrayClass;
}
@@ -289,9 +309,7 @@
}
}
- /**
- * Implementation of MethodHandles.identity() for reference types.
- */
+ /** Implementation of MethodHandles.identity() for reference types. */
public static class ReferenceIdentity extends Transformer {
private final Class<?> type;
@@ -312,9 +330,7 @@
}
}
- /**
- * Implementation of MethodHandles.constant.
- */
+ /** Implementation of MethodHandles.constant. */
public static class Constant extends Transformer {
private final Class<?> type;
@@ -335,36 +351,38 @@
public Constant(Class<?> type, Object value) {
super(MethodType.methodType(type));
this.type = type;
+ typeChar = Wrapper.basicTypeChar(type);
- if (!type.isPrimitive()) {
- asReference = value;
- typeChar = 'L';
- } else if (type == int.class) {
- asInt = (int) value;
- typeChar = 'I';
- } else if (type == char.class) {
- asInt = (int) (char) value;
- typeChar = 'C';
- } else if (type == short.class) {
- asInt = (int) (short) value;
- typeChar = 'S';
- } else if (type == byte.class) {
- asInt = (int) (byte) value;
- typeChar = 'B';
- } else if (type == boolean.class) {
- asInt = ((boolean) value) ? 1 : 0;
- typeChar = 'Z';
- } else if (type == long.class) {
- asLong = (long) value;
- typeChar = 'J';
- } else if (type == float.class) {
- asFloat = (float) value;
- typeChar = 'F';
- } else if (type == double.class) {
- asDouble = (double) value;
- typeChar = 'D';
- } else {
- throw new AssertionError("unknown type: " + typeChar);
+ switch (typeChar) {
+ case 'L':
+ asReference = value;
+ break;
+ case 'I':
+ asInt = (int) value;
+ break;
+ case 'C':
+ asInt = (int) (char) value;
+ break;
+ case 'S':
+ asInt = (int) (short) value;
+ break;
+ case 'B':
+ asInt = (int) (byte) value;
+ break;
+ case 'Z':
+ asInt = ((boolean) value) ? 1 : 0;
+ break;
+ case 'J':
+ asLong = (long) value;
+ break;
+ case 'F':
+ asFloat = (float) value;
+ break;
+ case 'D':
+ asDouble = (double) value;
+ break;
+ default:
+ throw new AssertionError("unknown type: " + typeChar);
}
}
@@ -375,15 +393,33 @@
writer.makeReturnValueAccessor();
switch (typeChar) {
- case 'L' : { writer.putNextReference(asReference, type); break; }
- case 'I' : { writer.putNextInt(asInt); break; }
- case 'C' : { writer.putNextChar((char) asInt); break; }
- case 'S' : { writer.putNextShort((short) asInt); break; }
- case 'B' : { writer.putNextByte((byte) asInt); break; }
- case 'Z' : { writer.putNextBoolean(asInt == 1); break; }
- case 'J' : { writer.putNextLong(asLong); break; }
- case 'F' : { writer.putNextFloat(asFloat); break; }
- case 'D' : { writer.putNextDouble(asDouble); break; }
+ case 'L':
+ writer.putNextReference(asReference, type);
+ break;
+ case 'I':
+ writer.putNextInt(asInt);
+ break;
+ case 'C':
+ writer.putNextChar((char) asInt);
+ break;
+ case 'S':
+ writer.putNextShort((short) asInt);
+ break;
+ case 'B':
+ writer.putNextByte((byte) asInt);
+ break;
+ case 'Z':
+ writer.putNextBoolean(asInt == 1);
+ break;
+ case 'J':
+ writer.putNextLong(asLong);
+ break;
+ case 'F':
+ writer.putNextFloat(asFloat);
+ break;
+ case 'D':
+ writer.putNextDouble(asDouble);
+ break;
default:
throw new AssertionError("Unexpected typeChar: " + typeChar);
}
@@ -432,7 +468,7 @@
EmulatedStackFrame.create(constructorHandle.type());
constructorFrame.setReference(0, receiver);
emulatedStackFrame.copyRangeTo(constructorFrame, callerRange, 1, 0);
- constructorHandle.invoke(constructorFrame);
+ invokeExactFromTransform(constructorHandle, constructorFrame);
// Set return result for caller.
emulatedStackFrame.setReturnValueTo(receiver);
@@ -468,18 +504,16 @@
// The first reference argument must be the receiver.
stackFrame.setReference(0, receiver);
// Copy all other arguments.
- emulatedStackFrame.copyRangeTo(stackFrame, range,
- 1 /* referencesStart */, 0 /* stackFrameStart */);
+ emulatedStackFrame.copyRangeTo(
+ stackFrame, range, 1 /* referencesStart */, 0 /* stackFrameStart */);
// Perform the invoke.
- delegate.invoke(stackFrame);
+ invokeFromTransform(delegate, stackFrame);
stackFrame.copyReturnValueTo(emulatedStackFrame);
}
}
- /**
- * Implements MethodHandle.filterReturnValue.
- */
+ /** Implements MethodHandle.filterReturnValue. */
public static class FilterReturnValue extends Transformer {
private final MethodHandle target;
private final MethodHandle filter;
@@ -502,7 +536,7 @@
// the same parameter shapes.
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0);
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
// Perform the invoke.
final StackFrameReader returnValueReader = new StackFrameReader();
@@ -515,29 +549,43 @@
filterWriter.attach(filterFrame);
final Class<?> returnType = target.type().rtype();
- if (!returnType.isPrimitive()) {
- filterWriter.putNextReference(returnValueReader.nextReference(returnType),
- returnType);
- } else if (returnType == boolean.class) {
- filterWriter.putNextBoolean(returnValueReader.nextBoolean());
- } else if (returnType == byte.class) {
- filterWriter.putNextByte(returnValueReader.nextByte());
- } else if (returnType == char.class) {
- filterWriter.putNextChar(returnValueReader.nextChar());
- } else if (returnType == short.class) {
- filterWriter.putNextShort(returnValueReader.nextShort());
- } else if (returnType == int.class) {
- filterWriter.putNextInt(returnValueReader.nextInt());
- } else if (returnType == long.class) {
- filterWriter.putNextLong(returnValueReader.nextLong());
- } else if (returnType == float.class) {
- filterWriter.putNextFloat(returnValueReader.nextFloat());
- } else if (returnType == double.class) {
- filterWriter.putNextDouble(returnValueReader.nextDouble());
+ switch (Wrapper.basicTypeChar(returnType)) {
+ case 'L':
+ filterWriter.putNextReference(
+ returnValueReader.nextReference(returnType), returnType);
+ break;
+ case 'Z':
+ filterWriter.putNextBoolean(returnValueReader.nextBoolean());
+ break;
+ case 'B':
+ filterWriter.putNextByte(returnValueReader.nextByte());
+ break;
+ case 'C':
+ filterWriter.putNextChar(returnValueReader.nextChar());
+ break;
+ case 'S':
+ filterWriter.putNextShort(returnValueReader.nextShort());
+ break;
+ case 'I':
+ filterWriter.putNextInt(returnValueReader.nextInt());
+ break;
+ case 'J':
+ filterWriter.putNextLong(returnValueReader.nextLong());
+ break;
+ case 'F':
+ filterWriter.putNextFloat(returnValueReader.nextFloat());
+ break;
+ case 'D':
+ filterWriter.putNextDouble(returnValueReader.nextDouble());
+ break;
+ case 'V':
+ break;
+ default:
+ throw new IllegalStateException("Unsupported type: " + returnType);
}
// Invoke the filter and copy its return value back to the original frame.
- filter.invoke(filterFrame);
+ invokeFromTransform(filter, filterFrame);
filterFrame.copyReturnValueTo(emulatedStackFrame);
}
}
@@ -571,26 +619,36 @@
final Class<?>[] ptypes = type().ptypes();
for (int i = 0; i < ptypes.length; ++i) {
final Class<?> ptype = ptypes[i];
- if (!ptype.isPrimitive()) {
- arguments[i] = reader.nextReference(ptype);
- } else if (ptype == boolean.class) {
- arguments[i] = reader.nextBoolean();
- } else if (ptype == byte.class) {
- arguments[i] = reader.nextByte();
- } else if (ptype == char.class) {
- arguments[i] = reader.nextChar();
- } else if (ptype == short.class) {
- arguments[i] = reader.nextShort();
- } else if (ptype == int.class) {
- arguments[i] = reader.nextInt();
- } else if (ptype == long.class) {
- arguments[i] = reader.nextLong();
- } else if (ptype == float.class) {
- arguments[i] = reader.nextFloat();
- } else if (ptype == double.class) {
- arguments[i] = reader.nextDouble();
- } else {
- throw new AssertionError("Unexpected type: " + ptype);
+ switch (Wrapper.basicTypeChar(ptype)) {
+ case 'L':
+ arguments[i] = reader.nextReference(ptype);
+ break;
+ case 'Z':
+ arguments[i] = reader.nextBoolean();
+ break;
+ case 'B':
+ arguments[i] = reader.nextByte();
+ break;
+ case 'C':
+ arguments[i] = reader.nextChar();
+ break;
+ case 'S':
+ arguments[i] = reader.nextShort();
+ break;
+ case 'I':
+ arguments[i] = reader.nextInt();
+ break;
+ case 'J':
+ arguments[i] = reader.nextLong();
+ break;
+ case 'F':
+ arguments[i] = reader.nextFloat();
+ break;
+ case 'D':
+ arguments[i] = reader.nextDouble();
+ break;
+ default:
+ throw new AssertionError("Unexpected type: " + ptype);
}
}
@@ -602,51 +660,62 @@
int idx = reorder[i];
final Class<?> ptype = ptypes[idx];
final Object argument = arguments[idx];
-
- if (!ptype.isPrimitive()) {
- writer.putNextReference(argument, ptype);
- } else if (ptype == boolean.class) {
- writer.putNextBoolean((boolean) argument);
- } else if (ptype == byte.class) {
- writer.putNextByte((byte) argument);
- } else if (ptype == char.class) {
- writer.putNextChar((char) argument);
- } else if (ptype == short.class) {
- writer.putNextShort((short) argument);
- } else if (ptype == int.class) {
- writer.putNextInt((int) argument);
- } else if (ptype == long.class) {
- writer.putNextLong((long) argument);
- } else if (ptype == float.class) {
- writer.putNextFloat((float) argument);
- } else if (ptype == double.class) {
- writer.putNextDouble((double) argument);
- } else {
- throw new AssertionError("Unexpected type: " + ptype);
+ switch (Wrapper.basicTypeChar(ptype)) {
+ case 'L':
+ writer.putNextReference(argument, ptype);
+ break;
+ case 'Z':
+ writer.putNextBoolean((boolean) argument);
+ break;
+ case 'B':
+ writer.putNextByte((byte) argument);
+ break;
+ case 'C':
+ writer.putNextChar((char) argument);
+ break;
+ case 'S':
+ writer.putNextShort((short) argument);
+ break;
+ case 'I':
+ writer.putNextInt((int) argument);
+ break;
+ case 'J':
+ writer.putNextLong((long) argument);
+ break;
+ case 'F':
+ writer.putNextFloat((float) argument);
+ break;
+ case 'D':
+ writer.putNextDouble((double) argument);
+ break;
+ default:
+ throw new AssertionError("Unexpected type: " + ptype);
}
}
- target.invoke(calleeFrame);
+ invokeFromTransform(target, calleeFrame);
calleeFrame.copyReturnValueTo(emulatedStackFrame);
}
}
/**
- * Converts methods with a trailing array argument to variable arity
- * methods. So (A,B,C[])R can be invoked with any number of convertible
- * arguments after B, e.g. (A,B)R or (A, B, C0)R or (A, B, C0...Cn)R.
+ * Makes a variable-arity adapter that groups trailing varargs arguments into an array.
*
* @hide
*/
/*package*/ static class VarargsCollector extends Transformer {
final MethodHandle target;
+ private final Class<?> arrayType;
/*package*/ VarargsCollector(MethodHandle target) {
super(target.type(), MethodHandle.INVOKE_CALLSITE_TRANSFORM);
- if (!lastParameterTypeIsAnArray(target.type().ptypes())) {
+
+ Class<?>[] parameterTypes = target.type().ptypes();
+ if (!lastParameterTypeIsAnArray(parameterTypes)) {
throw new IllegalArgumentException("target does not have array as last parameter");
}
this.target = target;
+ this.arrayType = parameterTypes[parameterTypes.length - 1];
}
private static boolean lastParameterTypeIsAnArray(Class<?>[] parameterTypes) {
@@ -655,10 +724,52 @@
}
@Override
- public boolean isVarargsCollector() { return true; }
+ public boolean isVarargsCollector() {
+ return true;
+ }
@Override
- public MethodHandle asFixedArity() { return target; }
+ public MethodHandle asFixedArity() {
+ return target;
+ }
+
+ @Override
+ MethodHandle asTypeUncached(MethodType newType) {
+ // asType() behavior is specialized per:
+ //
+ // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/MethodHandle.html#asVarargsCollector(java.lang.Class)
+ //
+ // "The behavior of asType is also specialized for variable arity adapters, to maintain
+ // the invariant that plain, inexact invoke is always equivalent to an asType call to
+ // adjust the target type, followed by invokeExact. Therefore, a variable arity
+ // adapter responds to an asType request by building a fixed arity collector, if and
+ // only if the adapter and requested type differ either in arity or trailing argument
+ // type. The resulting fixed arity collector has its type further adjusted
+ // (if necessary) to the requested type by pairwise conversion, as if by another
+ // application of asType."
+ final MethodType currentType = type();
+ final MethodHandle currentFixedArity = asFixedArity();
+ if (currentType.parameterCount() == newType.parameterCount()
+ && currentType
+ .lastParameterType()
+ .isAssignableFrom(newType.lastParameterType())) {
+ return asTypeCache = currentFixedArity.asType(newType);
+ }
+
+ final int arrayLength = newType.parameterCount() - currentType.parameterCount() + 1;
+ if (arrayLength < 0) {
+ // arrayType is definitely array per VarargsCollector constructor.
+ throwWrongMethodTypeException(currentType, newType);
+ }
+
+ MethodHandle collector = null;
+ try {
+ collector = currentFixedArity.asCollector(arrayType, arrayLength).asType(newType);
+ } catch (IllegalArgumentException ex) {
+ throwWrongMethodTypeException(currentType, newType);
+ }
+ return asTypeCache = collector;
+ }
@Override
public void transform(EmulatedStackFrame callerFrame) throws Throwable {
@@ -667,12 +778,13 @@
Class<?>[] targetPTypes = type().ptypes();
int lastTargetIndex = targetPTypes.length - 1;
- if (callerPTypes.length == targetPTypes.length &&
- targetPTypes[lastTargetIndex].isAssignableFrom(callerPTypes[lastTargetIndex])) {
+ if (callerPTypes.length == targetPTypes.length
+ && targetPTypes[lastTargetIndex].isAssignableFrom(
+ callerPTypes[lastTargetIndex])) {
// Caller frame matches target frame in the arity array parameter. Invoke
// immediately, and let the invoke() dispatch perform any necessary conversions
// on the other parameters present.
- target.invoke(callerFrame);
+ invokeFromTransform(target, callerFrame);
return;
}
@@ -698,7 +810,7 @@
prepareFrame(callerFrame, targetFrame);
// Invoke target.
- target.invoke(targetFrame);
+ invokeExactFromTransform(target, targetFrame);
// Copy return value to the caller's frame.
targetFrame.copyReturnValueTo(callerFrame);
@@ -708,11 +820,11 @@
throw new WrongMethodTypeException("Cannot convert " + from + " to " + to);
}
- private static boolean arityArgumentsConvertible(Class<?>[] ptypes, int arityStart,
- Class<?> elementType) {
+ private static boolean arityArgumentsConvertible(
+ Class<?>[] ptypes, int arityStart, Class<?> elementType) {
if (ptypes.length - 1 == arityStart) {
- if (ptypes[arityStart].isArray() &&
- ptypes[arityStart].getComponentType() == elementType) {
+ if (ptypes[arityStart].isArray()
+ && ptypes[arityStart].getComponentType() == elementType) {
// The last ptype is in the same position as the arity
// array and has the same type.
return true;
@@ -727,112 +839,162 @@
return true;
}
- private static Object referenceArray(StackFrameReader reader, Class<?>[] ptypes,
- Class<?> elementType, int offset, int length) {
+ private static Object referenceArray(
+ StackFrameReader reader,
+ Class<?>[] ptypes,
+ Class<?> elementType,
+ int offset,
+ int length) {
Object arityArray = Array.newInstance(elementType, length);
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
Object o = null;
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { o = reader.nextReference(argumentType); break; }
- case 'I': { o = reader.nextInt(); break; }
- case 'J': { o = reader.nextLong(); break; }
- case 'B': { o = reader.nextByte(); break; }
- case 'S': { o = reader.nextShort(); break; }
- case 'C': { o = reader.nextChar(); break; }
- case 'Z': { o = reader.nextBoolean(); break; }
- case 'F': { o = reader.nextFloat(); break; }
- case 'D': { o = reader.nextDouble(); break; }
+ case 'L':
+ o = reader.nextReference(argumentType);
+ break;
+ case 'I':
+ o = reader.nextInt();
+ break;
+ case 'J':
+ o = reader.nextLong();
+ break;
+ case 'B':
+ o = reader.nextByte();
+ break;
+ case 'S':
+ o = reader.nextShort();
+ break;
+ case 'C':
+ o = reader.nextChar();
+ break;
+ case 'Z':
+ o = reader.nextBoolean();
+ break;
+ case 'F':
+ o = reader.nextFloat();
+ break;
+ case 'D':
+ o = reader.nextDouble();
+ break;
}
Array.set(arityArray, i, elementType.cast(o));
}
return arityArray;
}
- private static Object intArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object intArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
int[] arityArray = new int[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'I': { arityArray[i] = reader.nextInt(); break; }
- case 'S': { arityArray[i] = reader.nextShort(); break; }
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: {
+ case 'I':
+ arityArray[i] = reader.nextInt();
+ break;
+ case 'S':
+ arityArray[i] = reader.nextShort();
+ break;
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
arityArray[i] = (Integer) reader.nextReference(argumentType);
break;
- }
}
}
return arityArray;
}
- private static Object longArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object longArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
long[] arityArray = new long[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'J': { arityArray[i] = reader.nextLong(); break; }
- case 'I': { arityArray[i] = reader.nextInt(); break; }
- case 'S': { arityArray[i] = reader.nextShort(); break; }
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: { arityArray[i] = (Long) reader.nextReference(argumentType); break; }
+ case 'J':
+ arityArray[i] = reader.nextLong();
+ break;
+ case 'I':
+ arityArray[i] = reader.nextInt();
+ break;
+ case 'S':
+ arityArray[i] = reader.nextShort();
+ break;
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
+ arityArray[i] = (Long) reader.nextReference(argumentType);
+ break;
}
}
return arityArray;
}
- private static Object byteArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object byteArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
byte[] arityArray = new byte[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: { arityArray[i] = (Byte) reader.nextReference(argumentType); break; }
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
+ arityArray[i] = (Byte) reader.nextReference(argumentType);
+ break;
}
}
return arityArray;
}
- private static Object shortArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object shortArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
short[] arityArray = new short[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'S': { arityArray[i] = reader.nextShort(); break; }
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: { arityArray[i] = (Short) reader.nextReference(argumentType); break; }
+ case 'S':
+ arityArray[i] = reader.nextShort();
+ break;
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
+ arityArray[i] = (Short) reader.nextReference(argumentType);
+ break;
}
}
return arityArray;
}
- private static Object charArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object charArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
char[] arityArray = new char[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'C': { arityArray[i] = reader.nextChar(); break; }
- default: {
+ case 'C':
+ arityArray[i] = reader.nextChar();
+ break;
+ default:
arityArray[i] = (Character) reader.nextReference(argumentType);
break;
- }
}
}
return arityArray;
}
- private static Object booleanArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object booleanArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
boolean[] arityArray = new boolean[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'Z': { arityArray[i] = reader.nextBoolean(); break; }
+ case 'Z':
+ arityArray[i] = reader.nextBoolean();
+ break;
default:
arityArray[i] = (Boolean) reader.nextReference(argumentType);
break;
@@ -841,114 +1003,188 @@
return arityArray;
}
- private static Object floatArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object floatArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
float[] arityArray = new float[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'F': { arityArray[i] = reader.nextFloat(); break; }
- case 'J': { arityArray[i] = reader.nextLong(); break; }
- case 'I': { arityArray[i] = reader.nextInt(); break; }
- case 'S': { arityArray[i] = reader.nextShort(); break; }
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: {
+ case 'F':
+ arityArray[i] = reader.nextFloat();
+ break;
+ case 'J':
+ arityArray[i] = reader.nextLong();
+ break;
+ case 'I':
+ arityArray[i] = reader.nextInt();
+ break;
+ case 'S':
+ arityArray[i] = reader.nextShort();
+ break;
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
arityArray[i] = (Float) reader.nextReference(argumentType);
break;
- }
}
}
return arityArray;
}
- private static Object doubleArray(StackFrameReader reader, Class<?> ptypes[],
- int offset, int length) {
+ private static Object doubleArray(
+ StackFrameReader reader, Class<?> ptypes[], int offset, int length) {
double[] arityArray = new double[length];
for (int i = 0; i < length; ++i) {
Class<?> argumentType = ptypes[i + offset];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'D': { arityArray[i] = reader.nextDouble(); break; }
- case 'F': { arityArray[i] = reader.nextFloat(); break; }
- case 'J': { arityArray[i] = reader.nextLong(); break; }
- case 'I': { arityArray[i] = reader.nextInt(); break; }
- case 'S': { arityArray[i] = reader.nextShort(); break; }
- case 'B': { arityArray[i] = reader.nextByte(); break; }
- default: {
+ case 'D':
+ arityArray[i] = reader.nextDouble();
+ break;
+ case 'F':
+ arityArray[i] = reader.nextFloat();
+ break;
+ case 'J':
+ arityArray[i] = reader.nextLong();
+ break;
+ case 'I':
+ arityArray[i] = reader.nextInt();
+ break;
+ case 'S':
+ arityArray[i] = reader.nextShort();
+ break;
+ case 'B':
+ arityArray[i] = reader.nextByte();
+ break;
+ default:
arityArray[i] = (Double) reader.nextReference(argumentType);
break;
- }
}
}
return arityArray;
}
- private static Object makeArityArray(MethodType callerFrameType,
- StackFrameReader callerFrameReader,
- int indexOfArityArray,
- Class<?> arityArrayType) {
+ private static Object makeArityArray(
+ MethodType callerFrameType,
+ StackFrameReader callerFrameReader,
+ int indexOfArityArray,
+ Class<?> arityArrayType) {
int arityArrayLength = callerFrameType.ptypes().length - indexOfArityArray;
Class<?> elementType = arityArrayType.getComponentType();
Class<?>[] callerPTypes = callerFrameType.ptypes();
char elementBasicType = Wrapper.basicTypeChar(elementType);
switch (elementBasicType) {
- case 'L': return referenceArray(callerFrameReader, callerPTypes, elementType,
- indexOfArityArray, arityArrayLength);
- case 'I': return intArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'J': return longArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'B': return byteArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'S': return shortArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'C': return charArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'Z': return booleanArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'F': return floatArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
- case 'D': return doubleArray(callerFrameReader, callerPTypes,
- indexOfArityArray, arityArrayLength);
+ case 'L':
+ return referenceArray(
+ callerFrameReader,
+ callerPTypes,
+ elementType,
+ indexOfArityArray,
+ arityArrayLength);
+ case 'I':
+ return intArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'J':
+ return longArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'B':
+ return byteArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'S':
+ return shortArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'C':
+ return charArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'Z':
+ return booleanArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'F':
+ return floatArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
+ case 'D':
+ return doubleArray(
+ callerFrameReader, callerPTypes,
+ indexOfArityArray, arityArrayLength);
}
throw new InternalError("Unexpected type: " + elementType);
}
- public static Object collectArguments(char basicComponentType, Class<?> componentType,
- StackFrameReader reader, Class<?>[] types,
- int startIdx, int length) {
+ public static Object collectArguments(
+ char basicComponentType,
+ Class<?> componentType,
+ StackFrameReader reader,
+ Class<?>[] types,
+ int startIdx,
+ int length) {
switch (basicComponentType) {
- case 'L': return referenceArray(reader, types, componentType, startIdx, length);
- case 'I': return intArray(reader, types, startIdx, length);
- case 'J': return longArray(reader, types, startIdx, length);
- case 'B': return byteArray(reader, types, startIdx, length);
- case 'S': return shortArray(reader, types, startIdx, length);
- case 'C': return charArray(reader, types, startIdx, length);
- case 'Z': return booleanArray(reader, types, startIdx, length);
- case 'F': return floatArray(reader, types, startIdx, length);
- case 'D': return doubleArray(reader, types, startIdx, length);
+ case 'L':
+ return referenceArray(reader, types, componentType, startIdx, length);
+ case 'I':
+ return intArray(reader, types, startIdx, length);
+ case 'J':
+ return longArray(reader, types, startIdx, length);
+ case 'B':
+ return byteArray(reader, types, startIdx, length);
+ case 'S':
+ return shortArray(reader, types, startIdx, length);
+ case 'C':
+ return charArray(reader, types, startIdx, length);
+ case 'Z':
+ return booleanArray(reader, types, startIdx, length);
+ case 'F':
+ return floatArray(reader, types, startIdx, length);
+ case 'D':
+ return doubleArray(reader, types, startIdx, length);
}
throw new InternalError("Unexpected type: " + basicComponentType);
}
- private static void copyParameter(StackFrameReader reader, StackFrameWriter writer,
- Class<?> ptype) {
+ private static void copyParameter(
+ StackFrameReader reader, StackFrameWriter writer, Class<?> ptype) {
switch (Wrapper.basicTypeChar(ptype)) {
- case 'L': { writer.putNextReference(reader.nextReference(ptype), ptype); break; }
- case 'I': { writer.putNextInt(reader.nextInt()); break; }
- case 'J': { writer.putNextLong(reader.nextLong()); break; }
- case 'B': { writer.putNextByte(reader.nextByte()); break; }
- case 'S': { writer.putNextShort(reader.nextShort()); break; }
- case 'C': { writer.putNextChar(reader.nextChar()); break; }
- case 'Z': { writer.putNextBoolean(reader.nextBoolean()); break; }
- case 'F': { writer.putNextFloat(reader.nextFloat()); break; }
- case 'D': { writer.putNextDouble(reader.nextDouble()); break; }
- default: throw new InternalError("Unexpected type: " + ptype);
+ case 'L':
+ writer.putNextReference(reader.nextReference(ptype), ptype);
+ break;
+ case 'I':
+ writer.putNextInt(reader.nextInt());
+ break;
+ case 'J':
+ writer.putNextLong(reader.nextLong());
+ break;
+ case 'B':
+ writer.putNextByte(reader.nextByte());
+ break;
+ case 'S':
+ writer.putNextShort(reader.nextShort());
+ break;
+ case 'C':
+ writer.putNextChar(reader.nextChar());
+ break;
+ case 'Z':
+ writer.putNextBoolean(reader.nextBoolean());
+ break;
+ case 'F':
+ writer.putNextFloat(reader.nextFloat());
+ break;
+ case 'D':
+ writer.putNextDouble(reader.nextDouble());
+ break;
+ default:
+ throw new InternalError("Unexpected type: " + ptype);
}
}
- private static void prepareFrame(EmulatedStackFrame callerFrame,
- EmulatedStackFrame targetFrame) {
+ private static void prepareFrame(
+ EmulatedStackFrame callerFrame, EmulatedStackFrame targetFrame) {
StackFrameWriter targetWriter = new StackFrameWriter();
targetWriter.attach(targetFrame);
StackFrameReader callerReader = new StackFrameReader();
@@ -964,22 +1200,25 @@
// Add arity array as last parameter in |targetFrame|.
Class<?> arityArrayType = targetMethodType.ptypes()[indexOfArityArray];
- Object arityArray = makeArityArray(callerFrame.getMethodType(), callerReader,
- indexOfArityArray, arityArrayType);
+ Object arityArray =
+ makeArityArray(
+ callerFrame.getMethodType(),
+ callerReader,
+ indexOfArityArray,
+ arityArrayType);
targetWriter.putNextReference(arityArray, arityArrayType);
}
/**
- * Computes the frame type to invoke the target method handle with. This
- * is the same as the caller frame type, but with the trailing argument
- * being the array type that is the trailing argument in the target method
- * handle.
+ * Computes the frame type to invoke the target method handle with. This is the same as the
+ * caller frame type, but with the trailing argument being the array type that is the
+ * trailing argument in the target method handle.
*
- * Suppose the targetType is (T0, T1, T2[])RT and the callerType is (C0, C1, C2, C3)RC
+ * <p>Suppose the targetType is (T0, T1, T2[])RT and the callerType is (C0, C1, C2, C3)RC
* then the constructed type is (C0, C1, T2[])RC.
*/
- private static MethodType makeTargetFrameType(MethodType callerType,
- MethodType targetType) {
+ private static MethodType makeTargetFrameType(
+ MethodType callerType, MethodType targetType) {
final int ptypesLength = targetType.ptypes().length;
final Class<?>[] ptypes = new Class<?>[ptypesLength];
// Copy types from caller types to new methodType.
@@ -991,9 +1230,7 @@
}
}
- /**
- * Implements MethodHandles.invoker & MethodHandles.exactInvoker.
- */
+ /** Implements MethodHandles.invoker & MethodHandles.exactInvoker. */
static class Invoker extends Transformer {
private final MethodType targetType;
private final boolean isExactInvoker;
@@ -1012,10 +1249,10 @@
// can't call invokeExact on the target inside the transformer.
if (isExactInvoker) {
MethodType callsiteType =
- emulatedStackFrame.getCallsiteType().dropParameterTypes(0, 1);
+ emulatedStackFrame.getCallsiteType().dropParameterTypes(0, 1);
if (!exactMatch(callsiteType, targetType)) {
- throw new WrongMethodTypeException("Wrong type, Expected: " + targetType
- + " was: " + callsiteType);
+ throw new WrongMethodTypeException(
+ "Wrong type, Expected: " + targetType + " was: " + callsiteType);
}
}
@@ -1027,43 +1264,45 @@
emulatedStackFrame.copyRangeTo(targetFrame, copyRange, 0, 0);
// Finally, invoke the handle and copy the return value.
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
targetFrame.copyReturnValueTo(emulatedStackFrame);
}
- /**
- * Checks whether two method types are compatible as an exact match. The exact match
- * is based on the erased form (all reference types treated as Object).
- * @param callsiteType the MethodType associated with the invocation.
- * @param targetType the MethodType of the MethodHandle being invoked.
- * @return true if {@code callsiteType} and {@code targetType} are an exact match.
- */
- static private boolean exactMatch(MethodType callsiteType, MethodType targetType) {
+ /**
+ * Checks whether two method types are compatible as an exact match. The exact match is
+ * based on the erased form (all reference types treated as Object).
+ *
+ * @param callsiteType the MethodType associated with the invocation.
+ * @param targetType the MethodType of the MethodHandle being invoked.
+ * @return true if {@code callsiteType} and {@code targetType} are an exact match.
+ */
+ private static boolean exactMatch(MethodType callsiteType, MethodType targetType) {
final int parameterCount = callsiteType.parameterCount();
if (callsiteType.parameterCount() != targetType.parameterCount()) {
return false;
}
for (int i = 0; i < parameterCount; ++i) {
- Class argumentType = callsiteType.parameterType(i);
+ Class argumentType = callsiteType.parameterType(i);
Class parameterType = targetType.parameterType(i);
if (!exactMatch(argumentType, parameterType)) {
return false;
}
}
// Check return type, noting it's always okay to discard the return value.
- return callsiteType.returnType() == Void.TYPE ||
- exactMatch(callsiteType.returnType(), targetType.returnType());
+ return callsiteType.returnType() == Void.TYPE
+ || exactMatch(callsiteType.returnType(), targetType.returnType());
}
/**
- * Checks whether two types are an exact match. The exact match is based on the erased
- * types so any two reference types match, but primitive types must be the same.
+ * Checks whether two types are an exact match. The exact match is based on the erased types
+ * so any two reference types match, but primitive types must be the same.
+ *
* @param lhs first class to compare.
* @param rhs second class to compare.
* @return true if both classes satisfy the exact match criteria.
*/
- static private boolean exactMatch(Class lhs, Class rhs) {
+ private static boolean exactMatch(Class lhs, Class rhs) {
if (lhs.isPrimitive() || rhs.isPrimitive()) {
return lhs == rhs;
}
@@ -1071,33 +1310,29 @@
}
}
- /**
- * Implements MethodHandle.asSpreader / MethodHandles.spreadInvoker.
- */
+ /** Implements MethodHandle.asSpreader / MethodHandles.spreadInvoker. */
static class Spreader extends Transformer {
/** The method handle we're delegating to. */
private final MethodHandle target;
/**
- * The offset of the trailing array argument in the list of arguments to
- * this transformer. The array argument is always the last argument.
+ * The offset of the trailing array argument in the list of arguments to this transformer.
+ * The array argument is always the last argument.
*/
private final int arrayOffset;
- /**
- * The type char of the component type of the array.
- */
+ /** The type char of the component type of the array. */
private final char arrayTypeChar;
/**
- * The number of input arguments that will be present in the array. In other words,
- * this is the expected array length.
+ * The number of input arguments that will be present in the array. In other words, this is
+ * the expected array length.
*/
private final int numArrayArgs;
/**
- * Range of arguments to copy verbatim from the input frame, This will cover all
- * arguments that aren't a part of the trailing array.
+ * Range of arguments to copy verbatim from the input frame, This will cover all arguments
+ * that aren't a part of the trailing array.
*/
private final Range copyRange;
@@ -1131,17 +1366,16 @@
// Attach the writer, prepare to spread the trailing array arguments into
// the callee frame.
StackFrameWriter writer = new StackFrameWriter();
- writer.attach(targetFrame,
- arrayOffset,
- copyRange.numReferences,
- copyRange.numBytes);
+ writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
// Get the array reference and check that its length is as expected.
- Object arrayObj = callerFrame.getReference(
- copyRange.numReferences, this.type().ptypes()[arrayOffset]);
+ Object arrayObj =
+ callerFrame.getReference(
+ copyRange.numReferences, this.type().ptypes()[arrayOffset]);
final int arrayLength = Array.getLength(arrayObj);
if (arrayLength != numArrayArgs) {
- throw new IllegalArgumentException("Invalid array length: " + arrayLength);
+ throw new IllegalArgumentException(
+ "Invalid array length " + arrayLength + " expected " + numArrayArgs);
}
final MethodType type = target.type();
@@ -1173,207 +1407,306 @@
case 'D':
spreadArray((double[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
break;
-
}
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
targetFrame.copyReturnValueTo(callerFrame);
}
- public static void spreadArray(Object[] array, StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ Object[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
Object o = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(o, argumentType); break; }
- case 'I': { writer.putNextInt((int) o); break; }
- case 'J': { writer.putNextLong((long) o); break; }
- case 'B': { writer.putNextByte((byte) o); break; }
- case 'S': { writer.putNextShort((short) o); break; }
- case 'C': { writer.putNextChar((char) o); break; }
- case 'Z': { writer.putNextBoolean((boolean) o); break; }
- case 'F': { writer.putNextFloat((float) o); break; }
- case 'D': { writer.putNextDouble((double) o); break; }
+ case 'L':
+ writer.putNextReference(o, argumentType);
+ break;
+ case 'I':
+ writer.putNextInt((int) o);
+ break;
+ case 'J':
+ writer.putNextLong((long) o);
+ break;
+ case 'B':
+ writer.putNextByte((byte) o);
+ break;
+ case 'S':
+ writer.putNextShort((short) o);
+ break;
+ case 'C':
+ writer.putNextChar((char) o);
+ break;
+ case 'Z':
+ writer.putNextBoolean((boolean) o);
+ break;
+ case 'F':
+ writer.putNextFloat((float) o);
+ break;
+ case 'D':
+ writer.putNextDouble((double) o);
+ break;
}
}
}
- public static void spreadArray(int[] array, StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ int[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
int j = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(j, argumentType); break; }
- case 'I': { writer.putNextInt(j); break; }
- case 'J': { writer.putNextLong(j); break; }
- case 'F': { writer.putNextFloat(j); break; }
- case 'D': { writer.putNextDouble(j); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(j, argumentType);
+ break;
+ case 'I':
+ writer.putNextInt(j);
+ break;
+ case 'J':
+ writer.putNextLong(j);
+ break;
+ case 'F':
+ writer.putNextFloat(j);
+ break;
+ case 'D':
+ writer.putNextDouble(j);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(long[] array, StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ long[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
long l = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(l, argumentType); break; }
- case 'J': { writer.putNextLong(l); break; }
- case 'F': { writer.putNextFloat((float) l); break; }
- case 'D': { writer.putNextDouble((double) l); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(l, argumentType);
+ break;
+ case 'J':
+ writer.putNextLong(l);
+ break;
+ case 'F':
+ writer.putNextFloat((float) l);
+ break;
+ case 'D':
+ writer.putNextDouble((double) l);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(byte[] array,
- StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ byte[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
byte b = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(b, argumentType); break; }
- case 'I': { writer.putNextInt(b); break; }
- case 'J': { writer.putNextLong(b); break; }
- case 'B': { writer.putNextByte(b); break; }
- case 'S': { writer.putNextShort(b); break; }
- case 'F': { writer.putNextFloat(b); break; }
- case 'D': { writer.putNextDouble(b); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(b, argumentType);
+ break;
+ case 'I':
+ writer.putNextInt(b);
+ break;
+ case 'J':
+ writer.putNextLong(b);
+ break;
+ case 'B':
+ writer.putNextByte(b);
+ break;
+ case 'S':
+ writer.putNextShort(b);
+ break;
+ case 'F':
+ writer.putNextFloat(b);
+ break;
+ case 'D':
+ writer.putNextDouble(b);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(short[] array,
- StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ short[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
short s = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(s, argumentType); break; }
- case 'I': { writer.putNextInt(s); break; }
- case 'J': { writer.putNextLong(s); break; }
- case 'S': { writer.putNextShort(s); break; }
- case 'F': { writer.putNextFloat(s); break; }
- case 'D': { writer.putNextDouble(s); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(s, argumentType);
+ break;
+ case 'I':
+ writer.putNextInt(s);
+ break;
+ case 'J':
+ writer.putNextLong(s);
+ break;
+ case 'S':
+ writer.putNextShort(s);
+ break;
+ case 'F':
+ writer.putNextFloat(s);
+ break;
+ case 'D':
+ writer.putNextDouble(s);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(char[] array,
- StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ char[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
char c = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(c, argumentType); break; }
- case 'I': { writer.putNextInt(c); break; }
- case 'J': { writer.putNextLong(c); break; }
- case 'C': { writer.putNextChar(c); break; }
- case 'F': { writer.putNextFloat(c); break; }
- case 'D': { writer.putNextDouble(c); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(c, argumentType);
+ break;
+ case 'I':
+ writer.putNextInt(c);
+ break;
+ case 'J':
+ writer.putNextLong(c);
+ break;
+ case 'C':
+ writer.putNextChar(c);
+ break;
+ case 'F':
+ writer.putNextFloat(c);
+ break;
+ case 'D':
+ writer.putNextDouble(c);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(boolean[] array,
- StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ boolean[] array,
+ StackFrameWriter writer,
+ MethodType type,
+ int numArgs,
+ int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
boolean z = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(z, argumentType); break; }
- case 'Z': { writer.putNextBoolean(z); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(z, argumentType);
+ break;
+ case 'Z':
+ writer.putNextBoolean(z);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(double[] array,
- StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ double[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
double d = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(d, argumentType); break; }
- case 'D': { writer.putNextDouble(d); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(d, argumentType);
+ break;
+ case 'D':
+ writer.putNextDouble(d);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
- public static void spreadArray(float[] array, StackFrameWriter writer, MethodType type,
- int numArgs, int offset) {
+ public static void spreadArray(
+ float[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
final Class<?>[] ptypes = type.ptypes();
for (int i = 0; i < numArgs; ++i) {
Class<?> argumentType = ptypes[i + offset];
float f = array[i];
switch (Wrapper.basicTypeChar(argumentType)) {
- case 'L': { writer.putNextReference(f, argumentType); break; }
- case 'D': { writer.putNextDouble((double) f); break; }
- case 'F': { writer.putNextFloat(f); break; }
- default : { throw new AssertionError(); }
+ case 'L':
+ writer.putNextReference(f, argumentType);
+ break;
+ case 'D':
+ writer.putNextDouble((double) f);
+ break;
+ case 'F':
+ writer.putNextFloat(f);
+ break;
+ default:
+ throw new AssertionError();
}
}
}
}
- /**
- * Implements MethodHandle.asCollector.
- */
+ /** Implements MethodHandle.asCollector. */
static class Collector extends Transformer {
private final MethodHandle target;
/**
- * The offset of the trailing array argument in the list of arguments to
- * this transformer. The array argument is always the last argument.
+ * The offset of the trailing array argument in the list of arguments to this transformer.
+ * The array argument is always the last argument.
*/
private final int arrayOffset;
/**
- * The number of input arguments that will be present in the array. In other words,
- * this is the expected array length.
+ * The number of input arguments that will be present in the array. In other words, this is
+ * the expected array length.
*/
private final int numArrayArgs;
- /**
- * The type char of the component type of the array.
- */
+ /** The component type of the array. */
+ private final Class arrayType;
+
+ /** The type char of the component type of the array. */
private final char arrayTypeChar;
/**
- * Range of arguments to copy verbatim from the input frame, This will cover all
- * arguments that aren't a part of the trailing array.
+ * Range of arguments to copy verbatim from the input frame, This will cover all arguments
+ * that aren't a part of the trailing array.
*/
private final Range copyRange;
- Collector(MethodHandle delegate, Class<?> arrayType, int length) {
+ Collector(MethodHandle delegate, final Class<?> arrayType, final int length) {
super(delegate.type().asCollectorType(arrayType, length));
+ final Class<?> componentType = arrayType.getComponentType();
+ if (componentType == null) {
+ throw new IllegalArgumentException("arrayType is not an array type");
+ }
+
target = delegate;
// Copy all arguments except the last argument (which is the trailing array argument
// that needs to be spread).
arrayOffset = delegate.type().parameterCount() - 1;
- arrayTypeChar = Wrapper.basicTypeChar(arrayType.getComponentType());
+ this.arrayType = arrayType;
+ arrayTypeChar = Wrapper.basicTypeChar(componentType);
numArrayArgs = length;
// Copy all args except for the last argument.
@@ -1396,89 +1729,98 @@
reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
switch (arrayTypeChar) {
- case 'L': {
- // Reference arrays are the only case where the component type of the
- // array we construct might differ from the type of the reference we read
- // from the stack frame.
- final Class<?> targetType = target.type().ptypes()[arrayOffset];
- final Class<?> targetComponentType = targetType.getComponentType();
- final Class<?> adapterComponentType = type().lastParameterType();
+ case 'L':
+ {
+ // Reference arrays are the only case where the component type of the
+ // array we construct might differ from the type of the reference we read
+ // from the stack frame.
+ final Class<?> targetType = target.type().ptypes()[arrayOffset];
+ final Class<?> adapterComponentType = arrayType.getComponentType();
- Object[] arr = (Object[]) Array.newInstance(targetComponentType, numArrayArgs);
- for (int i = 0; i < numArrayArgs; ++i) {
- arr[i] = reader.nextReference(adapterComponentType);
- }
+ Object[] arr =
+ (Object[]) Array.newInstance(adapterComponentType, numArrayArgs);
+ for (int i = 0; i < numArrayArgs; ++i) {
+ arr[i] = reader.nextReference(adapterComponentType);
+ }
- writer.putNextReference(arr, targetType);
- break;
- }
- case 'I': {
- int[] array = new int[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextInt();
+ writer.putNextReference(arr, targetType);
+ break;
}
- writer.putNextReference(array, int[].class);
- break;
- }
- case 'J': {
- long[] array = new long[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextLong();
+ case 'I':
+ {
+ int[] array = new int[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextInt();
+ }
+ writer.putNextReference(array, int[].class);
+ break;
}
- writer.putNextReference(array, long[].class);
- break;
- }
- case 'B': {
- byte[] array = new byte[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextByte();
+ case 'J':
+ {
+ long[] array = new long[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextLong();
+ }
+ writer.putNextReference(array, long[].class);
+ break;
}
- writer.putNextReference(array, byte[].class);
- break;
- }
- case 'S': {
- short[] array = new short[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextShort();
+ case 'B':
+ {
+ byte[] array = new byte[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextByte();
+ }
+ writer.putNextReference(array, byte[].class);
+ break;
}
- writer.putNextReference(array, short[].class);
- break;
- }
- case 'C': {
- char[] array = new char[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextChar();
+ case 'S':
+ {
+ short[] array = new short[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextShort();
+ }
+ writer.putNextReference(array, short[].class);
+ break;
}
- writer.putNextReference(array, char[].class);
- break;
- }
- case 'Z': {
- boolean[] array = new boolean[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextBoolean();
+ case 'C':
+ {
+ char[] array = new char[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextChar();
+ }
+ writer.putNextReference(array, char[].class);
+ break;
}
- writer.putNextReference(array, boolean[].class);
- break;
- }
- case 'F': {
- float[] array = new float[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextFloat();
+ case 'Z':
+ {
+ boolean[] array = new boolean[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextBoolean();
+ }
+ writer.putNextReference(array, boolean[].class);
+ break;
}
- writer.putNextReference(array, float[].class);
- break;
- }
- case 'D': {
- double[] array = new double[numArrayArgs];
- for (int i = 0; i < numArrayArgs; ++i) {
- array[i] = reader.nextDouble();
+ case 'F':
+ {
+ float[] array = new float[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextFloat();
+ }
+ writer.putNextReference(array, float[].class);
+ break;
}
- writer.putNextReference(array, double[].class);
- break;
- }
+ case 'D':
+ {
+ double[] array = new double[numArrayArgs];
+ for (int i = 0; i < numArrayArgs; ++i) {
+ array[i] = reader.nextDouble();
+ }
+ writer.putNextReference(array, double[].class);
+ break;
+ }
}
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
targetFrame.copyReturnValueTo(callerFrame);
}
}
@@ -1500,7 +1842,6 @@
this.target = target;
this.pos = pos;
this.filters = filters;
-
}
private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) {
@@ -1545,7 +1886,7 @@
filterWriter.attach(filterFrame);
copyNext(reader, filterWriter, filter.type().ptypes()[0]);
- filter.invoke(filterFrame);
+ invokeFromTransform(filter, filterFrame);
// Copy the argument back from the filter frame to the stack frame.
final StackFrameReader filterReader = new StackFrameReader();
@@ -1559,14 +1900,12 @@
}
}
- target.invoke(transformedFrame);
+ invokeFromTransform(target, transformedFrame);
transformedFrame.copyReturnValueTo(stackFrame);
}
}
- /**
- * Implements MethodHandles.collectArguments.
- */
+ /** Implements MethodHandles.collectArguments. */
static class CollectArguments extends Transformer {
private final MethodHandle target;
private final MethodHandle collector;
@@ -1576,8 +1915,8 @@
private final Range collectorRange;
/**
- * The first range of arguments we copy to the target. These are arguments
- * in the range [0, pos). Note that arg[pos] is the return value of the filter.
+ * The first range of arguments we copy to the target. These are arguments in the range [0,
+ * pos). Note that arg[pos] is the return value of the filter.
*/
private final Range range1;
@@ -1590,8 +1929,8 @@
private final int referencesOffset;
private final int stackFrameOffset;
- CollectArguments(MethodHandle target, MethodHandle collector, int pos,
- MethodType adapterType) {
+ CollectArguments(
+ MethodHandle target, MethodHandle collector, int pos, MethodType adapterType) {
super(adapterType);
this.target = target;
@@ -1629,7 +1968,7 @@
// First invoke the collector.
EmulatedStackFrame filterFrame = EmulatedStackFrame.create(collector.type());
stackFrame.copyRangeTo(filterFrame, collectorRange, 0, 0);
- collector.invoke(filterFrame);
+ invokeFromTransform(collector, filterFrame);
// Start constructing the target frame.
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
@@ -1645,19 +1984,19 @@
}
if (range2 != null) {
- stackFrame.copyRangeTo(targetFrame, range2,
+ stackFrame.copyRangeTo(
+ targetFrame,
+ range2,
range1.numReferences + referencesOffset,
range2.numBytes + stackFrameOffset);
}
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
targetFrame.copyReturnValueTo(stackFrame);
}
}
- /**
- * Implements MethodHandles.foldArguments.
- */
+ /** Implements MethodHandles.foldArguments. */
static class FoldArguments extends Transformer {
private final MethodHandle target;
private final MethodHandle combiner;
@@ -1695,7 +2034,7 @@
// First construct the combiner frame and invoke it.
EmulatedStackFrame combinerFrame = EmulatedStackFrame.create(combiner.type());
stackFrame.copyRangeTo(combinerFrame, combinerArgs, 0, 0);
- combiner.invoke(combinerFrame);
+ invokeFromTransform(combiner, combinerFrame);
// Create the stack frame for the target.
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
@@ -1710,7 +2049,7 @@
}
stackFrame.copyRangeTo(targetFrame, targetArgs, referencesOffset, stackFrameOffset);
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
targetFrame.copyReturnValueTo(stackFrame);
}
@@ -1724,9 +2063,7 @@
}
}
- /**
- * Implements MethodHandles.insertArguments.
- */
+ /** Implements MethodHandles.insertArguments. */
static class InsertArguments extends Transformer {
private final MethodHandle target;
private final int pos;
@@ -1764,48 +2101,445 @@
final Class<?>[] ptypes = target.type().ptypes();
for (int i = 0; i < values.length; ++i) {
final Class<?> ptype = ptypes[i + pos];
- if (ptype.isPrimitive()) {
- if (ptype == boolean.class) {
- writer.putNextBoolean((boolean) values[i]);
- } else if (ptype == byte.class) {
- writer.putNextByte((byte) values[i]);
- } else if (ptype == char.class) {
- writer.putNextChar((char) values[i]);
- } else if (ptype == short.class) {
- writer.putNextShort((short) values[i]);
- } else if (ptype == int.class) {
- writer.putNextInt((int) values[i]);
- } else if (ptype == long.class) {
- writer.putNextLong((long) values[i]);
- } else if (ptype == float.class) {
- writer.putNextFloat((float) values[i]);
- } else if (ptype == double.class) {
- writer.putNextDouble((double) values[i]);
- }
-
- bytesCopied += EmulatedStackFrame.getSize(ptype);
- } else {
+ final char typeChar = Wrapper.basicTypeChar(ptype);
+ if (typeChar == 'L') {
writer.putNextReference(values[i], ptype);
referencesCopied++;
+ } else {
+ switch (typeChar) {
+ case 'Z':
+ writer.putNextBoolean((boolean) values[i]);
+ break;
+ case 'B':
+ writer.putNextByte((byte) values[i]);
+ break;
+ case 'C':
+ writer.putNextChar((char) values[i]);
+ break;
+ case 'S':
+ writer.putNextShort((short) values[i]);
+ break;
+ case 'I':
+ writer.putNextInt((int) values[i]);
+ break;
+ case 'J':
+ writer.putNextLong((long) values[i]);
+ break;
+ case 'F':
+ writer.putNextFloat((float) values[i]);
+ break;
+ case 'D':
+ writer.putNextDouble((double) values[i]);
+ break;
+ }
+ bytesCopied += EmulatedStackFrame.getSize(ptype);
}
}
// Copy all remaining arguments.
if (range2 != null) {
- stackFrame.copyRangeTo(calleeFrame, range2,
+ stackFrame.copyRangeTo(
+ calleeFrame,
+ range2,
range1.numReferences + referencesCopied,
range1.numBytes + bytesCopied);
}
- target.invoke(calleeFrame);
+ invokeFromTransform(target, calleeFrame);
calleeFrame.copyReturnValueTo(stackFrame);
}
}
+ public static class AsTypeAdapter extends Transformer {
+ private final MethodHandle target;
- /**
- * Implements {@link java.lang.invokeMethodHandles#explicitCastArguments()}.
- */
+ public AsTypeAdapter(MethodHandle target, MethodType type) {
+ super(type);
+ this.target = target;
+ }
+
+ @Override
+ public void transform(EmulatedStackFrame callerFrame) throws Throwable {
+ final EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
+ final StackFrameReader reader = new StackFrameReader();
+ final StackFrameWriter writer = new StackFrameWriter();
+
+ // Adapt arguments
+ reader.attach(callerFrame);
+ writer.attach(targetFrame);
+ adaptArguments(reader, writer);
+
+ // Invoke target
+ invokeFromTransform(target, targetFrame);
+
+ if (callerFrame.getMethodType().rtype() != void.class) {
+ // Adapt return value
+ reader.attach(targetFrame).makeReturnValueAccessor();
+ writer.attach(callerFrame).makeReturnValueAccessor();
+ adaptReturnValue(reader, writer);
+ }
+ }
+
+ private void adaptArguments(final StackFrameReader reader, final StackFrameWriter writer) {
+ final Class<?>[] fromTypes = type().ptypes();
+ final Class<?>[] toTypes = target.type().ptypes();
+ for (int i = 0; i < fromTypes.length; ++i) {
+ adaptArgument(reader, fromTypes[i], writer, toTypes[i]);
+ }
+ }
+
+ private void adaptReturnValue(
+ final StackFrameReader reader, final StackFrameWriter writer) {
+ final Class<?> fromType = target.type().rtype();
+ final Class<?> toType = type().rtype();
+ adaptArgument(reader, fromType, writer, toType);
+ }
+
+ private void throwWrongMethodTypeException() throws WrongMethodTypeException {
+ throw new WrongMethodTypeException(
+ "Cannot convert from " + type() + " to " + target.type());
+ }
+
+ private static void throwClassCastException(Class from, Class to)
+ throws ClassCastException {
+ throw new ClassCastException("Cannot cast from " + from + " to " + to);
+ }
+
+ private void writePrimitiveByteAs(final StackFrameWriter writer, char baseType, byte value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'B':
+ writer.putNextByte(value);
+ return;
+ case 'S':
+ writer.putNextShort((short) value);
+ return;
+ case 'I':
+ writer.putNextInt((int) value);
+ return;
+ case 'J':
+ writer.putNextLong((long) value);
+ return;
+ case 'F':
+ writer.putNextFloat((float) value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveShortAs(
+ final StackFrameWriter writer, char baseType, short value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'S':
+ writer.putNextShort(value);
+ return;
+ case 'I':
+ writer.putNextInt((int) value);
+ return;
+ case 'J':
+ writer.putNextLong((long) value);
+ return;
+ case 'F':
+ writer.putNextFloat((float) value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveCharAs(final StackFrameWriter writer, char baseType, char value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'C':
+ writer.putNextChar(value);
+ return;
+ case 'I':
+ writer.putNextInt((int) value);
+ return;
+ case 'J':
+ writer.putNextLong((long) value);
+ return;
+ case 'F':
+ writer.putNextFloat((float) value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveIntAs(final StackFrameWriter writer, char baseType, int value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'I':
+ writer.putNextInt(value);
+ return;
+ case 'J':
+ writer.putNextLong((long) value);
+ return;
+ case 'F':
+ writer.putNextFloat((float) value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ throwWrongMethodTypeException();
+ }
+
+ private void writePrimitiveLongAs(final StackFrameWriter writer, char baseType, long value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'J':
+ writer.putNextLong(value);
+ return;
+ case 'F':
+ writer.putNextFloat((float) value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveFloatAs(
+ final StackFrameWriter writer, char baseType, float value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'F':
+ writer.putNextFloat(value);
+ return;
+ case 'D':
+ writer.putNextDouble((double) value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveDoubleAs(
+ final StackFrameWriter writer, char baseType, double value)
+ throws WrongMethodTypeException {
+ switch (baseType) {
+ case 'D':
+ writer.putNextDouble(value);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private void writePrimitiveVoidAs(final StackFrameWriter writer, char baseType) {
+ switch (baseType) {
+ case 'Z':
+ writer.putNextBoolean(false);
+ return;
+ case 'B':
+ writer.putNextByte((byte) 0);
+ return;
+ case 'S':
+ writer.putNextShort((short) 0);
+ return;
+ case 'C':
+ writer.putNextChar((char) 0);
+ return;
+ case 'I':
+ writer.putNextInt(0);
+ return;
+ case 'J':
+ writer.putNextLong(0L);
+ return;
+ case 'F':
+ writer.putNextFloat(0.0f);
+ return;
+ case 'D':
+ writer.putNextDouble(0.0);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ }
+
+ private static Class getBoxedPrimitiveClass(char baseType) {
+ switch (baseType) {
+ case 'Z':
+ return Boolean.class;
+ case 'B':
+ return Byte.class;
+ case 'S':
+ return Short.class;
+ case 'C':
+ return Character.class;
+ case 'I':
+ return Integer.class;
+ case 'J':
+ return Long.class;
+ case 'F':
+ return Float.class;
+ case 'D':
+ return Double.class;
+ default:
+ return null;
+ }
+ }
+
+ private void adaptArgument(
+ final StackFrameReader reader,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
+ if (from.equals(to)) {
+ StackFrameAccessor.copyNext(reader, writer, from);
+ return;
+ }
+
+ if (to.isPrimitive()) {
+ if (from.isPrimitive()) {
+ final char fromBaseType = Wrapper.basicTypeChar(from);
+ final char toBaseType = Wrapper.basicTypeChar(to);
+ switch (fromBaseType) {
+ case 'B':
+ writePrimitiveByteAs(writer, toBaseType, reader.nextByte());
+ return;
+ case 'S':
+ writePrimitiveShortAs(writer, toBaseType, reader.nextShort());
+ return;
+ case 'C':
+ writePrimitiveCharAs(writer, toBaseType, reader.nextChar());
+ return;
+ case 'I':
+ writePrimitiveIntAs(writer, toBaseType, reader.nextInt());
+ return;
+ case 'J':
+ writePrimitiveLongAs(writer, toBaseType, reader.nextLong());
+ return;
+ case 'F':
+ writePrimitiveFloatAs(writer, toBaseType, reader.nextFloat());
+ return;
+ case 'V':
+ writePrimitiveVoidAs(writer, toBaseType);
+ return;
+ default:
+ throwWrongMethodTypeException();
+ }
+ } else {
+ final Object value = reader.nextReference(Object.class);
+ if (to == void.class) {
+ return;
+ }
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ if (!Wrapper.isWrapperType(value.getClass())) {
+ throwClassCastException(value.getClass(), to);
+ }
+ final Wrapper fromWrapper = Wrapper.forWrapperType(value.getClass());
+ final Wrapper toWrapper = Wrapper.forPrimitiveType(to);
+ if (!toWrapper.isConvertibleFrom(fromWrapper)) {
+ throwClassCastException(from, to);
+ }
+
+ final char toChar = toWrapper.basicTypeChar();
+ switch (fromWrapper.basicTypeChar()) {
+ case 'Z':
+ writer.putNextBoolean(((Boolean) value).booleanValue());
+ return;
+ case 'B':
+ writePrimitiveByteAs(writer, toChar, ((Byte) value).byteValue());
+ return;
+ case 'S':
+ writePrimitiveShortAs(writer, toChar, ((Short) value).shortValue());
+ return;
+ case 'C':
+ writePrimitiveCharAs(writer, toChar, ((Character) value).charValue());
+ return;
+ case 'I':
+ writePrimitiveIntAs(writer, toChar, ((Integer) value).intValue());
+ return;
+ case 'J':
+ writePrimitiveLongAs(writer, toChar, ((Long) value).longValue());
+ return;
+ case 'F':
+ writePrimitiveFloatAs(writer, toChar, ((Float) value).floatValue());
+ return;
+ case 'D':
+ writePrimitiveDoubleAs(writer, toChar, ((Double) value).doubleValue());
+ return;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ } else {
+ if (from.isPrimitive()) {
+ // Boxing conversion
+ final char fromBaseType = Wrapper.basicTypeChar(from);
+ final Class fromBoxed = getBoxedPrimitiveClass(fromBaseType);
+ // 'to' maybe a super class of the boxed `from` type, e.g. Number.
+ if (fromBoxed != null && !to.isAssignableFrom(fromBoxed)) {
+ throwWrongMethodTypeException();
+ }
+
+ Object boxed;
+ switch (fromBaseType) {
+ case 'Z':
+ boxed = Boolean.valueOf(reader.nextBoolean());
+ break;
+ case 'B':
+ boxed = Byte.valueOf(reader.nextByte());
+ break;
+ case 'S':
+ boxed = Short.valueOf(reader.nextShort());
+ break;
+ case 'C':
+ boxed = Character.valueOf(reader.nextChar());
+ break;
+ case 'I':
+ boxed = Integer.valueOf(reader.nextInt());
+ break;
+ case 'J':
+ boxed = Long.valueOf(reader.nextLong());
+ break;
+ case 'F':
+ boxed = Float.valueOf(reader.nextFloat());
+ break;
+ case 'D':
+ boxed = Double.valueOf(reader.nextDouble());
+ break;
+ case 'V':
+ boxed = null;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ writer.putNextReference(boxed, to);
+ return;
+ } else {
+ // Cast
+ Object value = reader.nextReference(Object.class);
+ if (value != null && !to.isAssignableFrom(value.getClass())) {
+ throwClassCastException(value.getClass(), to);
+ }
+ writer.putNextReference(value, to);
+ }
+ }
+ }
+ }
+
+ /** Implements {@link java.lang.invokeMethodHandles#explicitCastArguments()}. */
public static class ExplicitCastArguments extends Transformer {
private final MethodHandle target;
@@ -1820,12 +2554,12 @@
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
explicitCastArguments(callerFrame, targetFrame);
- target.invoke(targetFrame);
+ invokeFromTransform(target, targetFrame);
explicitCastReturnValue(callerFrame, targetFrame);
}
- private void explicitCastArguments(final EmulatedStackFrame callerFrame,
- final EmulatedStackFrame targetFrame) {
+ private void explicitCastArguments(
+ final EmulatedStackFrame callerFrame, final EmulatedStackFrame targetFrame) {
final StackFrameReader reader = new StackFrameReader();
reader.attach(callerFrame);
final StackFrameWriter writer = new StackFrameWriter();
@@ -1838,8 +2572,8 @@
}
}
- private void explicitCastReturnValue(final EmulatedStackFrame callerFrame,
- final EmulatedStackFrame targetFrame) {
+ private void explicitCastReturnValue(
+ final EmulatedStackFrame callerFrame, final EmulatedStackFrame targetFrame) {
Class<?> from = target.type().rtype();
Class<?> to = type().rtype();
if (to != void.class) {
@@ -1871,413 +2605,532 @@
}
/**
- * Converts byte value to boolean according to
- * {@link java.lang.invoke.MethodHandles#explicitCast()}
+ * Converts byte value to boolean according to {@link
+ * java.lang.invoke.MethodHandles#explicitCast()}
*/
private static boolean toBoolean(byte value) {
return (value & 1) == 1;
}
- private static byte readPrimitiveAsByte(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (byte) reader.nextByte();
- } else if (from == char.class) {
- return (byte) reader.nextChar();
- } else if (from == short.class) {
- return (byte) reader.nextShort();
- } else if (from == int.class) {
- return (byte) reader.nextInt();
- } else if (from == long.class) {
- return (byte) reader.nextLong();
- } else if (from == float.class) {
- return (byte) reader.nextFloat();
- } else if (from == double.class) {
- return (byte) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? (byte) 1 : (byte) 0;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static byte readPrimitiveAsByte(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (byte) reader.nextByte();
+ case 'C':
+ return (byte) reader.nextChar();
+ case 'S':
+ return (byte) reader.nextShort();
+ case 'I':
+ return (byte) reader.nextInt();
+ case 'J':
+ return (byte) reader.nextLong();
+ case 'F':
+ return (byte) reader.nextFloat();
+ case 'D':
+ return (byte) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? (byte) 1 : (byte) 0;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static char readPrimitiveAsChar(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (char) reader.nextByte();
- } else if (from == char.class) {
- return (char) reader.nextChar();
- } else if (from == short.class) {
- return (char) reader.nextShort();
- } else if (from == int.class) {
- return (char) reader.nextInt();
- } else if (from == long.class) {
- return (char) reader.nextLong();
- } else if (from == float.class) {
- return (char) reader.nextFloat();
- } else if (from == double.class) {
- return (char) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? (char) 1 : (char) 0;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static char readPrimitiveAsChar(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (char) reader.nextByte();
+ case 'C':
+ return (char) reader.nextChar();
+ case 'S':
+ return (char) reader.nextShort();
+ case 'I':
+ return (char) reader.nextInt();
+ case 'J':
+ return (char) reader.nextLong();
+ case 'F':
+ return (char) reader.nextFloat();
+ case 'D':
+ return (char) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? (char) 1 : (char) 0;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static short readPrimitiveAsShort(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (short) reader.nextByte();
- } else if (from == char.class) {
- return (short) reader.nextChar();
- } else if (from == short.class) {
- return (short) reader.nextShort();
- } else if (from == int.class) {
- return (short) reader.nextInt();
- } else if (from == long.class) {
- return (short) reader.nextLong();
- } else if (from == float.class) {
- return (short) reader.nextFloat();
- } else if (from == double.class) {
- return (short) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? (short) 1 : (short) 0;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static short readPrimitiveAsShort(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (short) reader.nextByte();
+ case 'C':
+ return (short) reader.nextChar();
+ case 'S':
+ return (short) reader.nextShort();
+ case 'I':
+ return (short) reader.nextInt();
+ case 'J':
+ return (short) reader.nextLong();
+ case 'F':
+ return (short) reader.nextFloat();
+ case 'D':
+ return (short) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? (short) 1 : (short) 0;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static int readPrimitiveAsInt(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (int) reader.nextByte();
- } else if (from == char.class) {
- return (int) reader.nextChar();
- } else if (from == short.class) {
- return (int) reader.nextShort();
- } else if (from == int.class) {
- return (int) reader.nextInt();
- } else if (from == long.class) {
- return (int) reader.nextLong();
- } else if (from == float.class) {
- return (int) reader.nextFloat();
- } else if (from == double.class) {
- return (int) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? 1 : 0;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static int readPrimitiveAsInt(final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (int) reader.nextByte();
+ case 'C':
+ return (int) reader.nextChar();
+ case 'S':
+ return (int) reader.nextShort();
+ case 'I':
+ return (int) reader.nextInt();
+ case 'J':
+ return (int) reader.nextLong();
+ case 'F':
+ return (int) reader.nextFloat();
+ case 'D':
+ return (int) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? 1 : 0;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static long readPrimitiveAsLong(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (long) reader.nextByte();
- } else if (from == char.class) {
- return (long) reader.nextChar();
- } else if (from == short.class) {
- return (long) reader.nextShort();
- } else if (from == int.class) {
- return (long) reader.nextInt();
- } else if (from == long.class) {
- return (long) reader.nextLong();
- } else if (from == float.class) {
- return (long) reader.nextFloat();
- } else if (from == double.class) {
- return (long) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? 1L : 0L;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static long readPrimitiveAsLong(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (long) reader.nextByte();
+ case 'C':
+ return (long) reader.nextChar();
+ case 'S':
+ return (long) reader.nextShort();
+ case 'I':
+ return (long) reader.nextInt();
+ case 'J':
+ return (long) reader.nextLong();
+ case 'F':
+ return (long) reader.nextFloat();
+ case 'D':
+ return (long) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? 1L : 0L;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static float readPrimitiveAsFloat(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (float) reader.nextByte();
- } else if (from == char.class) {
- return (float) reader.nextChar();
- } else if (from == short.class) {
- return (float) reader.nextShort();
- } else if (from == int.class) {
- return (float) reader.nextInt();
- } else if (from == long.class) {
- return (float) reader.nextLong();
- } else if (from == float.class) {
- return (float) reader.nextFloat();
- } else if (from == double.class) {
- return (float) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? 1.0f : 0.0f;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static float readPrimitiveAsFloat(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (float) reader.nextByte();
+ case 'C':
+ return (float) reader.nextChar();
+ case 'S':
+ return (float) reader.nextShort();
+ case 'I':
+ return (float) reader.nextInt();
+ case 'J':
+ return (float) reader.nextLong();
+ case 'F':
+ return (float) reader.nextFloat();
+ case 'D':
+ return (float) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? 1.0f : 0.0f;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static double readPrimitiveAsDouble(final StackFrameReader reader,
- final Class<?> from) {
- if (from == byte.class) {
- return (double) reader.nextByte();
- } else if (from == char.class) {
- return (double) reader.nextChar();
- } else if (from == short.class) {
- return (double) reader.nextShort();
- } else if (from == int.class) {
- return (double) reader.nextInt();
- } else if (from == long.class) {
- return (double) reader.nextLong();
- } else if (from == float.class) {
- return (double) reader.nextFloat();
- } else if (from == double.class) {
- return (double) reader.nextDouble();
- } else if (from == boolean.class) {
- return reader.nextBoolean() ? 1.0 : 0.0;
- } else {
- throwUnexpectedType(from);
- return 0;
+ private static double readPrimitiveAsDouble(
+ final StackFrameReader reader, final Class<?> from) {
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'B':
+ return (double) reader.nextByte();
+ case 'C':
+ return (double) reader.nextChar();
+ case 'S':
+ return (double) reader.nextShort();
+ case 'I':
+ return (double) reader.nextInt();
+ case 'J':
+ return (double) reader.nextLong();
+ case 'F':
+ return (double) reader.nextFloat();
+ case 'D':
+ return (double) reader.nextDouble();
+ case 'Z':
+ return reader.nextBoolean() ? 1.0 : 0.0;
+ default:
+ throwUnexpectedType(from);
+ return 0;
}
}
- private static void explicitCastPrimitives(final StackFrameReader reader,
- final Class<?> from,
- final StackFrameWriter writer,
- final Class<?> to) {
- if (to == byte.class) {
- byte value = readPrimitiveAsByte(reader, from);
- writer.putNextByte(value);
- } else if (to == char.class) {
- char value = readPrimitiveAsChar(reader, from);
- writer.putNextChar(value);
- } else if (to == short.class) {
- short value = readPrimitiveAsShort(reader, from);
- writer.putNextShort(value);
- } else if (to == int.class) {
- int value = readPrimitiveAsInt(reader, from);
- writer.putNextInt(value);
- } else if (to == long.class) {
- long value = readPrimitiveAsLong(reader, from);
- writer.putNextLong(value);
- } else if (to == float.class) {
- float value = readPrimitiveAsFloat(reader, from);
- writer.putNextFloat(value);
- } else if (to == double.class) {
- double value = readPrimitiveAsDouble(reader, from);
- writer.putNextDouble(value);
- } else if (to == boolean.class) {
- byte byteValue = readPrimitiveAsByte(reader, from);
- writer.putNextBoolean(toBoolean(byteValue));
- } else {
- throwUnexpectedType(to);
+ private static void explicitCastPrimitives(
+ final StackFrameReader reader,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'B':
+ writer.putNextByte(readPrimitiveAsByte(reader, from));
+ break;
+ case 'C':
+ writer.putNextChar(readPrimitiveAsChar(reader, from));
+ break;
+ case 'S':
+ writer.putNextShort(readPrimitiveAsShort(reader, from));
+ break;
+ case 'I':
+ writer.putNextInt(readPrimitiveAsInt(reader, from));
+ break;
+ case 'J':
+ writer.putNextLong(readPrimitiveAsLong(reader, from));
+ break;
+ case 'F':
+ writer.putNextFloat(readPrimitiveAsFloat(reader, from));
+ break;
+ case 'D':
+ writer.putNextDouble(readPrimitiveAsDouble(reader, from));
+ break;
+ case 'Z':
+ writer.putNextBoolean(toBoolean(readPrimitiveAsByte(reader, from)));
+ break;
+ default:
+ throwUnexpectedType(to);
+ break;
}
}
private static void unboxNull(final StackFrameWriter writer, final Class<?> to) {
- if (to == boolean.class) {
- writer.putNextBoolean(false);
- } else if (to == byte.class) {
- writer.putNextByte((byte) 0);
- } else if (to == char.class) {
- writer.putNextChar((char) 0);
- } else if (to == short.class) {
- writer.putNextShort((short) 0);
- } else if (to == int.class) {
- writer.putNextInt((int) 0);
- } else if (to == long.class) {
- writer.putNextLong((long) 0);
- } else if (to == float.class) {
- writer.putNextFloat((float) 0);
- } else if (to == double.class) {
- writer.putNextDouble((double) 0);
- } else {
- throwUnexpectedType(to);
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean(false);
+ break;
+ case 'B':
+ writer.putNextByte((byte) 0);
+ break;
+ case 'C':
+ writer.putNextChar((char) 0);
+ break;
+ case 'S':
+ writer.putNextShort((short) 0);
+ break;
+ case 'I':
+ writer.putNextInt((int) 0);
+ break;
+ case 'J':
+ writer.putNextLong((long) 0);
+ break;
+ case 'F':
+ writer.putNextFloat((float) 0);
+ break;
+ case 'D':
+ writer.putNextDouble((double) 0);
+ break;
+ default:
+ throwUnexpectedType(to);
+ break;
}
}
- private static void unboxNonNull(final Object ref, final Class<?> from,
- final StackFrameWriter writer, final Class<?> to) {
- if (from == Boolean.class) {
- boolean z = (boolean) ref;
- if (to == boolean.class) {
- writer.putNextBoolean(z);
- } else if (to == byte.class) {
- writer.putNextByte(z ? (byte) 1 : (byte) 0);
- } else if (to == short.class) {
- writer.putNextShort(z ? (short) 1 : (short) 0);
- } else if (to == char.class) {
- writer.putNextChar(z ? (char) 1 : (char) 0);
- } else if (to == int.class) {
- writer.putNextInt(z ? 1 : 0);
- } else if (to == long.class) {
- writer.putNextLong(z ? 1l : 0l);
- } else if (to == float.class) {
- writer.putNextFloat(z ? 1.0f : 0.0f);
- } else if (to == double.class) {
- writer.putNextDouble(z ? 1.0 : 0.0);
- } else {
- badCast(from, to);
- }
- } else if (from == Byte.class) {
- byte b = (byte) ref;
- if (to == byte.class) {
- writer.putNextByte(b);
- } else if (to == boolean.class) {
- writer.putNextBoolean(toBoolean(b));
- } else if (to == short.class) {
- writer.putNextShort((short) b);
- } else if (to == char.class) {
- writer.putNextChar((char) b);
- } else if (to == int.class) {
- writer.putNextInt((int) b);
- } else if (to == long.class) {
- writer.putNextLong((long) b);
- } else if (to == float.class) {
- writer.putNextFloat((float) b);
- } else if (to == double.class) {
- writer.putNextDouble((double) b);
- } else {
- badCast(from, to);
- }
- } else if (from == Short.class) {
- short s = (short) ref;
- if (to == boolean.class) {
- writer.putNextBoolean((s & 1) == 1);
- } else if (to == byte.class) {
- writer.putNextByte((byte) s);
- } else if (to == short.class) {
- writer.putNextShort(s);
- } else if (to == char.class) {
- writer.putNextChar((char) s);
- } else if (to == int.class) {
- writer.putNextInt((int) s);
- } else if (to == long.class) {
- writer.putNextLong((long) s);
- } else if (to == float.class) {
- writer.putNextFloat((float) s);
- } else if (to == double.class) {
- writer.putNextDouble((double) s);
- } else {
- badCast(from, to);
- }
- } else if (from == Character.class) {
- char c = (char) ref;
- if (to == boolean.class) {
- writer.putNextBoolean((c & (char) 1) == (char) 1);
- } else if (to == byte.class) {
- writer.putNextByte((byte) c);
- } else if (to == short.class) {
- writer.putNextShort((short) c);
- } else if (to == char.class) {
- writer.putNextChar(c);
- } else if (to == int.class) {
- writer.putNextInt((int) c);
- } else if (to == long.class) {
- writer.putNextLong((long) c);
- } else if (to == float.class) {
- writer.putNextFloat((float) c);
- } else if (to == double.class) {
- writer.putNextDouble((double) c);
- } else {
- badCast(from, to);
- }
- } else if (from == Integer.class) {
- int i = (int) ref;
- if (to == boolean.class) {
- writer.putNextBoolean((i & 1) == 1);
- } else if (to == byte.class) {
- writer.putNextByte((byte) i);
- } else if (to == short.class) {
- writer.putNextShort((short) i);
- } else if (to == char.class) {
- writer.putNextChar((char) i);
- } else if (to == int.class) {
- writer.putNextInt(i);
- } else if (to == long.class) {
- writer.putNextLong((long) i);
- } else if (to == float.class) {
- writer.putNextFloat((float) i);
- } else if (to == double.class) {
- writer.putNextDouble((double) i);
- } else {
- badCast(from, to);
- }
- } else if (from == Long.class) {
- long j = (long) ref;
- if (to == boolean.class) {
- writer.putNextBoolean((j & 1l) == 1l);
- } else if (to == byte.class) {
- writer.putNextByte((byte) j);
- } else if (to == short.class) {
- writer.putNextShort((short) j);
- } else if (to == char.class) {
- writer.putNextChar((char) j);
- } else if (to == int.class) {
- writer.putNextInt((int) j);
- } else if (to == long.class) {
- writer.putNextLong(j);
- } else if (to == float.class) {
- writer.putNextFloat((float) j);
- } else if (to == double.class) {
- writer.putNextDouble((double) j);
- } else {
- badCast(from, to);
- }
- } else if (from == Float.class) {
- float f = (float) ref;
- if (to == boolean.class) {
- writer.putNextBoolean(((byte) f & 1) != 0);
- } else if (to == byte.class) {
- writer.putNextByte((byte) f);
- } else if (to == short.class) {
- writer.putNextShort((short) f);
- } else if (to == char.class) {
- writer.putNextChar((char) f);
- } else if (to == int.class) {
- writer.putNextInt((int) f);
- } else if (to == long.class) {
- writer.putNextLong((long) f);
- } else if (to == float.class) {
- writer.putNextFloat(f);
- } else if (to == double.class) {
- writer.putNextDouble((double) f);
- } else {
- badCast(from, to);
- }
- } else if (from == Double.class) {
- double d = (double) ref;
- if (to == boolean.class) {
- writer.putNextBoolean(((byte) d & 1) != 0);
- } else if (to == byte.class) {
- writer.putNextByte((byte) d);
- } else if (to == short.class) {
- writer.putNextShort((short) d);
- } else if (to == char.class) {
- writer.putNextChar((char) d);
- } else if (to == int.class) {
- writer.putNextInt((int) d);
- } else if (to == long.class) {
- writer.putNextLong((long) d);
- } else if (to == float.class) {
- writer.putNextFloat((float) d);
- } else if (to == double.class) {
- writer.putNextDouble(d);
- } else {
- badCast(from, to);
- }
- } else {
+ private static void unboxNonNull(
+ final Object ref,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
+ final Class<?> unboxedFromType = Wrapper.asPrimitiveType(from);
+ if (unboxedFromType == from) {
badCast(from, to);
+ return;
+ }
+ switch (Wrapper.basicTypeChar(unboxedFromType)) {
+ case 'Z':
+ boolean z = (boolean) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean(z);
+ break;
+ case 'B':
+ writer.putNextByte(z ? (byte) 1 : (byte) 0);
+ break;
+ case 'S':
+ writer.putNextShort(z ? (short) 1 : (short) 0);
+ break;
+ case 'C':
+ writer.putNextChar(z ? (char) 1 : (char) 0);
+ break;
+ case 'I':
+ writer.putNextInt(z ? 1 : 0);
+ break;
+ case 'J':
+ writer.putNextLong(z ? 1l : 0l);
+ break;
+ case 'F':
+ writer.putNextFloat(z ? 1.0f : 0.0f);
+ break;
+ case 'D':
+ writer.putNextDouble(z ? 1.0 : 0.0);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'B':
+ byte b = (byte) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'B':
+ writer.putNextByte(b);
+ break;
+ case 'Z':
+ writer.putNextBoolean(toBoolean(b));
+ break;
+ case 'S':
+ writer.putNextShort((short) b);
+ break;
+ case 'C':
+ writer.putNextChar((char) b);
+ break;
+ case 'I':
+ writer.putNextInt((int) b);
+ break;
+ case 'J':
+ writer.putNextLong((long) b);
+ break;
+ case 'F':
+ writer.putNextFloat((float) b);
+ break;
+ case 'D':
+ writer.putNextDouble((double) b);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'S':
+ short s = (short) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean((s & 1) == 1);
+ break;
+ case 'B':
+ writer.putNextByte((byte) s);
+ break;
+ case 'S':
+ writer.putNextShort(s);
+ break;
+ case 'C':
+ writer.putNextChar((char) s);
+ break;
+ case 'I':
+ writer.putNextInt((int) s);
+ break;
+ case 'J':
+ writer.putNextLong((long) s);
+ break;
+ case 'F':
+ writer.putNextFloat((float) s);
+ break;
+ case 'D':
+ writer.putNextDouble((double) s);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'C':
+ char c = (char) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean((c & (char) 1) == (char) 1);
+ break;
+ case 'B':
+ writer.putNextByte((byte) c);
+ break;
+ case 'S':
+ writer.putNextShort((short) c);
+ break;
+ case 'C':
+ writer.putNextChar(c);
+ break;
+ case 'I':
+ writer.putNextInt((int) c);
+ break;
+ case 'J':
+ writer.putNextLong((long) c);
+ break;
+ case 'F':
+ writer.putNextFloat((float) c);
+ break;
+ case 'D':
+ writer.putNextDouble((double) c);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'I':
+ int i = (int) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean((i & 1) == 1);
+ break;
+ case 'B':
+ writer.putNextByte((byte) i);
+ break;
+ case 'S':
+ writer.putNextShort((short) i);
+ break;
+ case 'C':
+ writer.putNextChar((char) i);
+ break;
+ case 'I':
+ writer.putNextInt(i);
+ break;
+ case 'J':
+ writer.putNextLong((long) i);
+ break;
+ case 'F':
+ writer.putNextFloat((float) i);
+ break;
+ case 'D':
+ writer.putNextDouble((double) i);
+ break;
+ default:
+ badCast(from, to);
+ }
+ break;
+ case 'J':
+ long j = (long) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean((j & 1l) == 1l);
+ break;
+ case 'B':
+ writer.putNextByte((byte) j);
+ break;
+ case 'S':
+ writer.putNextShort((short) j);
+ break;
+ case 'C':
+ writer.putNextChar((char) j);
+ break;
+ case 'I':
+ writer.putNextInt((int) j);
+ break;
+ case 'J':
+ writer.putNextLong(j);
+ break;
+ case 'F':
+ writer.putNextFloat((float) j);
+ break;
+ case 'D':
+ writer.putNextDouble((double) j);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'F':
+ float f = (float) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean(((byte) f & 1) != 0);
+ break;
+ case 'B':
+ writer.putNextByte((byte) f);
+ break;
+ case 'S':
+ writer.putNextShort((short) f);
+ break;
+ case 'C':
+ writer.putNextChar((char) f);
+ break;
+ case 'I':
+ writer.putNextInt((int) f);
+ break;
+ case 'J':
+ writer.putNextLong((long) f);
+ break;
+ case 'F':
+ writer.putNextFloat(f);
+ break;
+ case 'D':
+ writer.putNextDouble((double) f);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ case 'D':
+ double d = (double) ref;
+ switch (Wrapper.basicTypeChar(to)) {
+ case 'Z':
+ writer.putNextBoolean(((byte) d & 1) != 0);
+ break;
+ case 'B':
+ writer.putNextByte((byte) d);
+ break;
+ case 'S':
+ writer.putNextShort((short) d);
+ break;
+ case 'C':
+ writer.putNextChar((char) d);
+ break;
+ case 'I':
+ writer.putNextInt((int) d);
+ break;
+ case 'J':
+ writer.putNextLong((long) d);
+ break;
+ case 'F':
+ writer.putNextFloat((float) d);
+ break;
+ case 'D':
+ writer.putNextDouble(d);
+ break;
+ default:
+ badCast(from, to);
+ break;
+ }
+ break;
+ default:
+ badCast(from, to);
+ break;
}
}
- private static void unbox(final Object ref, final Class<?> from,
- final StackFrameWriter writer, final Class<?> to) {
+ private static void unbox(
+ final Object ref,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
if (ref == null) {
unboxNull(writer, to);
} else {
@@ -2285,33 +3138,49 @@
}
}
- private static void box(final StackFrameReader reader, final Class<?> from,
- final StackFrameWriter writer, final Class<?> to) {
+ private static void box(
+ final StackFrameReader reader,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
Object boxed = null;
- if (from == boolean.class) {
- boxed = Boolean.valueOf(reader.nextBoolean());
- } else if (from == byte.class) {
- boxed = Byte.valueOf(reader.nextByte());
- } else if (from == char.class) {
- boxed = Character.valueOf(reader.nextChar());
- } else if (from == short.class) {
- boxed = Short.valueOf(reader.nextShort());
- } else if (from == int.class) {
- boxed = Integer.valueOf(reader.nextInt());
- } else if (from == long.class) {
- boxed = Long.valueOf(reader.nextLong());
- } else if (from == float.class) {
- boxed = Float.valueOf(reader.nextFloat());
- } else if (from == double.class) {
- boxed = Double.valueOf(reader.nextDouble());
- } else {
- throwUnexpectedType(from);
+ switch (Wrapper.basicTypeChar(from)) {
+ case 'Z':
+ boxed = Boolean.valueOf(reader.nextBoolean());
+ break;
+ case 'B':
+ boxed = Byte.valueOf(reader.nextByte());
+ break;
+ case 'C':
+ boxed = Character.valueOf(reader.nextChar());
+ break;
+ case 'S':
+ boxed = Short.valueOf(reader.nextShort());
+ break;
+ case 'I':
+ boxed = Integer.valueOf(reader.nextInt());
+ break;
+ case 'J':
+ boxed = Long.valueOf(reader.nextLong());
+ break;
+ case 'F':
+ boxed = Float.valueOf(reader.nextFloat());
+ break;
+ case 'D':
+ boxed = Double.valueOf(reader.nextDouble());
+ break;
+ default:
+ throwUnexpectedType(from);
+ break;
}
writer.putNextReference(to.cast(boxed), to);
}
- private static void explicitCast(final StackFrameReader reader, final Class<?> from,
- final StackFrameWriter writer, final Class<?> to) {
+ private static void explicitCast(
+ final StackFrameReader reader,
+ final Class<?> from,
+ final StackFrameWriter writer,
+ final Class<?> to) {
if (from.equals(to)) {
StackFrameAccessor.copyNext(reader, writer, from);
return;
diff --git a/ojluni/src/main/java/java/net/SocketOptions.java b/ojluni/src/main/java/java/net/SocketOptions.java
index 7e1b0fc..b9eac7b 100644
--- a/ojluni/src/main/java/java/net/SocketOptions.java
+++ b/ojluni/src/main/java/java/net/SocketOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,8 +38,9 @@
* DatagramSocketImpl, <B>you won't use these directly.</B> There are
* type-safe methods to get/set each of these options in Socket, ServerSocket,
* DatagramSocket and MulticastSocket.
- * <P>
+ *
* @author David Brown
+ * @since 1.1
*/
@@ -61,21 +62,21 @@
* If the requested option is binary, it can be set using this method by
* a java.lang.Boolean:
* <BR><PRE>
- * s.setOption(TCP_NODELAY, new Boolean(true));
+ * s.setOption(TCP_NODELAY, Boolean.TRUE);
* // OK - enables TCP_NODELAY, a binary option
* </PRE>
* <BR>
- * Any option can be disabled using this method with a Boolean(false):
+ * Any option can be disabled using this method with a Boolean.FALSE:
* <BR><PRE>
- * s.setOption(TCP_NODELAY, new Boolean(false));
+ * s.setOption(TCP_NODELAY, Boolean.FALSE);
* // OK - disables TCP_NODELAY
- * s.setOption(SO_LINGER, new Boolean(false));
+ * s.setOption(SO_LINGER, Boolean.FALSE);
* // OK - disables SO_LINGER
* </PRE>
* <BR>
* For an option that has a notion of on and off, and requires
* a non-boolean parameter, setting its value to anything other than
- * <I>Boolean(false)</I> implicitly enables it.
+ * <I>Boolean.FALSE</I> implicitly enables it.
* <BR>
* Throws SocketException if the option is unrecognized,
* the socket is closed, or some low-level error occurred
@@ -91,8 +92,8 @@
/**
* Fetch the value of an option.
- * Binary options will return java.lang.Boolean(true)
- * if enabled, java.lang.Boolean(false) if disabled, e.g.:
+ * Binary options will return java.lang.Boolean.TRUE
+ * if enabled, java.lang.Boolean.FALSE if disabled, e.g.:
* <BR><PRE>
* SocketImpl s;
* ...
@@ -105,13 +106,13 @@
* <P>
* For options that take a particular type as a parameter,
* getOption(int) will return the parameter's value, else
- * it will return java.lang.Boolean(false):
+ * it will return java.lang.Boolean.FALSE:
* <PRE>
* Object o = s.getOption(SO_LINGER);
* if (o instanceof Integer) {
* System.out.print("Linger time is " + ((Integer)o).intValue());
* } else {
- * // the true type of o is java.lang.Boolean(false);
+ * // the true type of o is java.lang.Boolean.FALSE;
* }
* </PRE>
*
@@ -139,7 +140,7 @@
* @see Socket#getTcpNoDelay
*/
- @Native public final static int TCP_NODELAY = 0x0001;
+ @Native public static final int TCP_NODELAY = 0x0001;
/**
* Fetch the local address binding of a socket (this option cannot
@@ -160,7 +161,7 @@
* @see DatagramSocket#getLocalAddress
*/
- @Native public final static int SO_BINDADDR = 0x000F;
+ @Native public static final int SO_BINDADDR = 0x000F;
/** Sets SO_REUSEADDR for a socket. This is used only for MulticastSockets
* in java, and it is set by default for MulticastSockets.
@@ -168,7 +169,18 @@
* Valid for: DatagramSocketImpl
*/
- @Native public final static int SO_REUSEADDR = 0x04;
+ @Native public static final int SO_REUSEADDR = 0x04;
+
+ /** Sets SO_REUSEPORT for a socket. This option enables and disables
+ * the ability to have multiple sockets listen to the same address
+ * and port.
+ * <P>
+ * Valid for: SocketImpl, DatagramSocketImpl
+ *
+ * @since 9
+ * @see StandardSocketOptions#SO_REUSEPORT
+ */
+ @Native public static final int SO_REUSEPORT = 0x0E;
/**
* Sets SO_BROADCAST for a socket. This option enables and disables
@@ -179,7 +191,7 @@
* @since 1.4
*/
- @Native public final static int SO_BROADCAST = 0x0020;
+ @Native public static final int SO_BROADCAST = 0x0020;
/** Set which outgoing interface on which to send multicast packets.
* Useful on hosts with multiple network interfaces, where applications
@@ -191,7 +203,7 @@
* @see MulticastSocket#getInterface()
*/
- @Native public final static int IP_MULTICAST_IF = 0x10;
+ @Native public static final int IP_MULTICAST_IF = 0x10;
/** Same as above. This option is introduced so that the behaviour
* with IP_MULTICAST_IF will be kept the same as before, while
@@ -203,7 +215,7 @@
* @see MulticastSocket#getNetworkInterface()
* @since 1.4
*/
- @Native public final static int IP_MULTICAST_IF2 = 0x1f;
+ @Native public static final int IP_MULTICAST_IF2 = 0x1f;
/**
* This option enables or disables local loopback of multicast datagrams.
@@ -211,7 +223,7 @@
* @since 1.4
*/
- @Native public final static int IP_MULTICAST_LOOP = 0x12;
+ @Native public static final int IP_MULTICAST_LOOP = 0x12;
/**
* This option sets the type-of-service or traffic class field
@@ -219,7 +231,7 @@
* @since 1.4
*/
- @Native public final static int IP_TOS = 0x3;
+ @Native public static final int IP_TOS = 0x3;
/**
* Specify a linger-on-close timeout. This option disables/enables
@@ -237,7 +249,7 @@
* @see Socket#setSoLinger
* @see Socket#getSoLinger
*/
- @Native public final static int SO_LINGER = 0x0080;
+ @Native public static final int SO_LINGER = 0x0080;
/** Set a timeout on blocking Socket operations:
* <PRE>
@@ -293,7 +305,7 @@
* @see DatagramSocket#setReceiveBufferSize
* @see DatagramSocket#getReceiveBufferSize
*/
- @Native public final static int SO_RCVBUF = 0x1002;
+ @Native public static final int SO_RCVBUF = 0x1002;
/**
* When the keepalive option is set for a TCP socket and no data
@@ -316,7 +328,7 @@
* @see Socket#setKeepAlive
* @see Socket#getKeepAlive
*/
- @Native public final static int SO_KEEPALIVE = 0x0008;
+ @Native public static final int SO_KEEPALIVE = 0x0008;
/**
* When the OOBINLINE option is set, any TCP urgent data received on
@@ -327,5 +339,5 @@
* @see Socket#setOOBInline
* @see Socket#getOOBInline
*/
- @Native public final static int SO_OOBINLINE = 0x1003;
+ @Native public static final int SO_OOBINLINE = 0x1003;
}
diff --git a/ojluni/src/main/java/java/net/StandardSocketOptions.java b/ojluni/src/main/java/java/net/StandardSocketOptions.java
index 7fdd5f0..ae47845 100644
--- a/ojluni/src/main/java/java/net/StandardSocketOptions.java
+++ b/ojluni/src/main/java/java/net/StandardSocketOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -187,6 +187,29 @@
new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
/**
+ * Re-use port.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. The exact semantics of this
+ * socket option are socket type and system dependent.
+ *
+ * <p> In the case of stream-oriented sockets, this socket option usually allows
+ * multiple listening sockets to be bound to both same address
+ * and same port.
+ *
+ * <p> For datagram-oriented sockets the socket option usually allows
+ * multiple UDP sockets to be bound to the same address and port.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Changing the value of this socket option
+ * after the socket is bound has no effect.
+ *
+ * @since 9
+ */
+ public static final SocketOption<Boolean> SO_REUSEPORT =
+ new StdSocketOption<Boolean>("SO_REUSEPORT", Boolean.class);
+
+ /**
* Linger on close if data is present.
*
* <p> The value of this socket option is an {@code Integer} that controls
diff --git a/ojluni/src/main/java/java/security/AccessControlException.java b/ojluni/src/main/java/java/security/AccessControlException.java
index a4f2a78..3d654ee 100644
--- a/ojluni/src/main/java/java/security/AccessControlException.java
+++ b/ojluni/src/main/java/java/security/AccessControlException.java
@@ -38,6 +38,7 @@
*
* @author Li Gong
* @author Roland Schemers
+ * @since 1.2
*/
public class AccessControlException extends SecurityException {
diff --git a/ojluni/src/main/java/java/security/Certificate.java b/ojluni/src/main/java/java/security/Certificate.java
index 489c6d6..28690e2 100644
--- a/ojluni/src/main/java/java/security/Certificate.java
+++ b/ojluni/src/main/java/java/security/Certificate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,13 +56,13 @@
* the certificate and satisfy itself of its validity.
*
* @author Benjamin Renaud
- * @deprecated A new certificate handling package is created in the Java platform.
- * This Certificate interface is entirely deprecated and
- * is here to allow for a smooth transition to the new
- * package.
+ * @since 1.1
+ * @deprecated This class is deprecated and subject to removal in a future
+ * version of Java SE. It has been replaced by
+ * {@code java.security.cert.Certificate} and related classes.
* @see java.security.cert.Certificate
*/
-@Deprecated
+@Deprecated(since="1.2", forRemoval=true)
public interface Certificate {
/**
diff --git a/ojluni/src/main/java/java/security/DigestInputStream.java b/ojluni/src/main/java/java/security/DigestInputStream.java
index a1bf55a..a76ebda 100644
--- a/ojluni/src/main/java/java/security/DigestInputStream.java
+++ b/ojluni/src/main/java/java/security/DigestInputStream.java
@@ -52,13 +52,14 @@
* {@link MessageDigest}),
* so that in order to compute intermediate digests, a caller should
* retain a handle onto the digest object, and clone it for each
- * digest to be computed, leaving the orginal digest untouched.
+ * digest to be computed, leaving the original digest untouched.
*
* @see MessageDigest
*
* @see DigestOutputStream
*
* @author Benjamin Renaud
+ * @since 1.2
*/
public class DigestInputStream extends FilterInputStream {
diff --git a/ojluni/src/main/java/java/security/DigestOutputStream.java b/ojluni/src/main/java/java/security/DigestOutputStream.java
index 619faec..e941673 100644
--- a/ojluni/src/main/java/java/security/DigestOutputStream.java
+++ b/ojluni/src/main/java/java/security/DigestOutputStream.java
@@ -51,6 +51,7 @@
* @see DigestInputStream
*
* @author Benjamin Renaud
+ * @since 1.2
*/
public class DigestOutputStream extends FilterOutputStream {
diff --git a/ojluni/src/main/java/java/security/DomainCombiner.java b/ojluni/src/main/java/java/security/DomainCombiner.java
index e9c010f..d844939 100644
--- a/ojluni/src/main/java/java/security/DomainCombiner.java
+++ b/ojluni/src/main/java/java/security/DomainCombiner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/java/security/GeneralSecurityException.java b/ojluni/src/main/java/java/security/GeneralSecurityException.java
index dc9ea06..69ee8ea 100644
--- a/ojluni/src/main/java/java/security/GeneralSecurityException.java
+++ b/ojluni/src/main/java/java/security/GeneralSecurityException.java
@@ -31,6 +31,7 @@
* security-related exception classes that extend from it.
*
* @author Jan Luehe
+ * @since 1.2
*/
public class GeneralSecurityException extends Exception {
diff --git a/ojluni/src/main/java/java/security/Guard.java b/ojluni/src/main/java/java/security/Guard.java
index abafb58..f64a0d9 100644
--- a/ojluni/src/main/java/java/security/Guard.java
+++ b/ojluni/src/main/java/java/security/Guard.java
@@ -38,6 +38,7 @@
*
* @author Roland Schemers
* @author Li Gong
+ * @since 1.2
*/
public interface Guard {
diff --git a/ojluni/src/main/java/java/security/GuardedObject.java b/ojluni/src/main/java/java/security/GuardedObject.java
index a275ddf..a2bf3f7 100644
--- a/ojluni/src/main/java/java/security/GuardedObject.java
+++ b/ojluni/src/main/java/java/security/GuardedObject.java
@@ -44,6 +44,7 @@
*
* @author Roland Schemers
* @author Li Gong
+ * @since 1.2
*/
public class GuardedObject implements java.io.Serializable {
diff --git a/ojluni/src/main/java/java/security/Identity.java b/ojluni/src/main/java/java/security/Identity.java
index e63131e..e6b2e80 100644
--- a/ojluni/src/main/java/java/security/Identity.java
+++ b/ojluni/src/main/java/java/security/Identity.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -52,12 +52,14 @@
* @see Principal
*
* @author Benjamin Renaud
- * @deprecated This class is no longer used. Its functionality has been
- * replaced by {@code java.security.KeyStore}, the
- * {@code java.security.cert} package, and
- * {@code java.security.Principal}.
+ * @since 1.1
+ * @deprecated This class is deprecated and subject to removal in a future
+ * version of Java SE. It has been replaced by
+ * {@code java.security.KeyStore}, the {@code java.security.cert} package,
+ * and {@code java.security.Principal}.
*/
-@Deprecated
+@Deprecated(since="1.2", forRemoval=true)
+@SuppressWarnings("removal")
public abstract class Identity implements Principal, Serializable {
/** use serialVersionUID from JDK 1.1.x for interoperability */
@@ -186,7 +188,7 @@
check("setIdentityPublicKey");
this.publicKey = key;
- certificates = new Vector<Certificate>();
+ certificates = new Vector<>();
}
/**
@@ -249,7 +251,7 @@
check("addIdentityCertificate");
if (certificates == null) {
- certificates = new Vector<Certificate>();
+ certificates = new Vector<>();
}
if (publicKey != null) {
if (!keyEquals(publicKey, certificate.getPublicKey())) {
diff --git a/ojluni/src/main/java/java/security/InvalidAlgorithmParameterException.java b/ojluni/src/main/java/java/security/InvalidAlgorithmParameterException.java
index 559a8be..9636fcb 100644
--- a/ojluni/src/main/java/java/security/InvalidAlgorithmParameterException.java
+++ b/ojluni/src/main/java/java/security/InvalidAlgorithmParameterException.java
@@ -65,7 +65,7 @@
}
/**
- * Creates a {@code InvalidAlgorithmParameterException} with the
+ * Creates an {@code InvalidAlgorithmParameterException} with the
* specified detail message and cause.
*
* @param message the detail message (which is saved for later retrieval
@@ -80,7 +80,7 @@
}
/**
- * Creates a {@code InvalidAlgorithmParameterException} with the
+ * Creates an {@code InvalidAlgorithmParameterException} with the
* specified cause and a detail message of
* {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of
diff --git a/ojluni/src/main/java/java/security/InvalidKeyException.java b/ojluni/src/main/java/java/security/InvalidKeyException.java
index 35fc64c..5349796 100644
--- a/ojluni/src/main/java/java/security/InvalidKeyException.java
+++ b/ojluni/src/main/java/java/security/InvalidKeyException.java
@@ -31,6 +31,7 @@
* length, uninitialized, etc).
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class InvalidKeyException extends KeyException {
@@ -58,7 +59,7 @@
}
/**
- * Creates a {@code InvalidKeyException} with the specified
+ * Creates an {@code InvalidKeyException} with the specified
* detail message and cause.
*
* @param message the detail message (which is saved for later retrieval
@@ -73,7 +74,7 @@
}
/**
- * Creates a {@code InvalidKeyException} with the specified cause
+ * Creates an {@code InvalidKeyException} with the specified cause
* and a detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of
* {@code cause}).
diff --git a/ojluni/src/main/java/java/security/InvalidParameterException.java b/ojluni/src/main/java/java/security/InvalidParameterException.java
index a095f90..18d413e 100644
--- a/ojluni/src/main/java/java/security/InvalidParameterException.java
+++ b/ojluni/src/main/java/java/security/InvalidParameterException.java
@@ -31,6 +31,7 @@
* to a method.
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class InvalidParameterException extends IllegalArgumentException {
diff --git a/ojluni/src/main/java/java/security/Key.java b/ojluni/src/main/java/java/security/Key.java
index c0c63d7..ff5611e 100644
--- a/ojluni/src/main/java/java/security/Key.java
+++ b/ojluni/src/main/java/java/security/Key.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,7 +63,7 @@
* </pre>
*
* For more information, see
- * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280:
+ * <a href="http://tools.ietf.org/html/rfc5280">RFC 5280:
* Internet X.509 Public Key Infrastructure Certificate and CRL Profile</a>.
*
* <LI>A Format
@@ -82,7 +82,7 @@
* <p> A Key should use KeyRep as its serialized representation.
* Note that a serialized Key may contain sensitive information
* which should not be exposed in untrusted environments. See the
- * <a href="../../../platform/serialization/spec/security.html">
+ * <a href="{@docRoot}/../specs/serialization/security.html">
* Security Appendix</a>
* of the Serialization Specification for more information.
*
@@ -97,6 +97,7 @@
* @see Signer
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public interface Key extends java.io.Serializable {
@@ -113,10 +114,10 @@
/**
* Returns the standard algorithm name for this key. For
* example, "DSA" would indicate that this key is a DSA key.
- * See Appendix A in the <a href=
- * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA">
- * Java Cryptography Architecture API Specification & Reference </a>
- * for information about standard algorithm names.
+ * See the <a href=
+ * "{@docRoot}/../specs/security/standard-names.html">
+ * Java Security Standard Algorithm Names</a> document
+ * for more information.
*
* @return the name of the algorithm associated with this key.
*/
diff --git a/ojluni/src/main/java/java/security/KeyException.java b/ojluni/src/main/java/java/security/KeyException.java
index 59cdd6f..b8b87d9 100644
--- a/ojluni/src/main/java/java/security/KeyException.java
+++ b/ojluni/src/main/java/java/security/KeyException.java
@@ -33,6 +33,7 @@
* @see KeyManagementException
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class KeyException extends GeneralSecurityException {
diff --git a/ojluni/src/main/java/java/security/KeyManagementException.java b/ojluni/src/main/java/java/security/KeyManagementException.java
index be212b9..fe1ab3e 100644
--- a/ojluni/src/main/java/java/security/KeyManagementException.java
+++ b/ojluni/src/main/java/java/security/KeyManagementException.java
@@ -38,6 +38,7 @@
* </ul>
*
* @author Benjamin Renaud
+ * @since 1.1
*
* @see Key
* @see KeyException
diff --git a/ojluni/src/main/java/java/security/KeyPair.java b/ojluni/src/main/java/java/security/KeyPair.java
index 6147a16..1d9e164 100644
--- a/ojluni/src/main/java/java/security/KeyPair.java
+++ b/ojluni/src/main/java/java/security/KeyPair.java
@@ -36,6 +36,7 @@
* @see PrivateKey
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public final class KeyPair implements java.io.Serializable {
diff --git a/ojluni/src/main/java/java/security/KeyRep.java b/ojluni/src/main/java/java/security/KeyRep.java
index 0b1412c..9d53635 100644
--- a/ojluni/src/main/java/java/security/KeyRep.java
+++ b/ojluni/src/main/java/java/security/KeyRep.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,7 @@
*
* Note that a serialized Key may contain sensitive information
* which should not be exposed in untrusted environments. See the
- * <a href="../../../platform/serialization/spec/security.html">
+ * <a href="{@docRoot}/../specs/serialization/security.html">
* Security Appendix</a>
* of the Serialization Specification for more information.
*
@@ -112,8 +112,6 @@
/**
* Construct the alternate Key class.
*
- * <p>
- *
* @param type either one of Type.SECRET, Type.PUBLIC, or Type.PRIVATE
* @param algorithm the algorithm returned from
* {@code Key.getAlgorithm()}
@@ -157,8 +155,6 @@
* encoded key bytes, and generates a private key from the spec
* </ul>
*
- * <p>
- *
* @return the resolved Key object
*
* @exception ObjectStreamException if the Type/format
diff --git a/ojluni/src/main/java/java/security/NoSuchAlgorithmException.java b/ojluni/src/main/java/java/security/NoSuchAlgorithmException.java
index 951e44e..24455d3 100644
--- a/ojluni/src/main/java/java/security/NoSuchAlgorithmException.java
+++ b/ojluni/src/main/java/java/security/NoSuchAlgorithmException.java
@@ -30,6 +30,7 @@
* requested but is not available in the environment.
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class NoSuchAlgorithmException extends GeneralSecurityException {
diff --git a/ojluni/src/main/java/java/security/PKCS12Attribute.java b/ojluni/src/main/java/java/security/PKCS12Attribute.java
index e389862..4a8ebfb 100644
--- a/ojluni/src/main/java/java/security/PKCS12Attribute.java
+++ b/ojluni/src/main/java/java/security/PKCS12Attribute.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -44,7 +44,7 @@
Pattern.compile("^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+$");
private String name;
private String value;
- private byte[] encoded;
+ private final byte[] encoded;
private int hashValue = -1;
/**
@@ -85,7 +85,8 @@
// Validate value
int length = value.length();
String[] values;
- if (value.charAt(0) == '[' && value.charAt(length - 1) == ']') {
+ if (length > 1 &&
+ value.charAt(0) == '[' && value.charAt(length - 1) == ']') {
values = value.substring(1, length - 1).split(", ");
} else {
values = new String[]{ value };
@@ -198,7 +199,7 @@
if (!(obj instanceof PKCS12Attribute)) {
return false;
}
- return Arrays.equals(encoded, ((PKCS12Attribute) obj).getEncoded());
+ return Arrays.equals(encoded, ((PKCS12Attribute) obj).encoded);
}
/**
@@ -209,10 +210,11 @@
*/
@Override
public int hashCode() {
- if (hashValue == -1) {
- Arrays.hashCode(encoded);
+ int h = hashValue;
+ if (h == -1) {
+ hashValue = h = Arrays.hashCode(encoded);
}
- return hashValue;
+ return h;
}
/**
@@ -252,6 +254,9 @@
private void parse(byte[] encoded) throws IOException {
DerInputStream attributeValue = new DerInputStream(encoded);
DerValue[] attrSeq = attributeValue.getSequence(2);
+ if (attrSeq.length != 2) {
+ throw new IOException("Invalid length for PKCS12Attribute");
+ }
ObjectIdentifier type = attrSeq[0].getOID();
DerInputStream attrContent =
new DerInputStream(attrSeq[1].toByteArray());
diff --git a/ojluni/src/main/java/java/security/Principal.java b/ojluni/src/main/java/java/security/Principal.java
index a538e70..40ee260 100644
--- a/ojluni/src/main/java/java/security/Principal.java
+++ b/ojluni/src/main/java/java/security/Principal.java
@@ -35,6 +35,7 @@
* @see java.security.cert.X509Certificate
*
* @author Li Gong
+ * @since 1.1
*/
public interface Principal {
@@ -74,7 +75,8 @@
/**
* Returns true if the specified subject is implied by this principal.
*
- * <p>The default implementation of this method returns true if
+ * @implSpec
+ * The default implementation of this method returns true if
* {@code subject} is non-null and contains at least one principal that
* is equal to this principal.
*
diff --git a/ojluni/src/main/java/java/security/PrivateKey.java b/ojluni/src/main/java/java/security/PrivateKey.java
index 7d8a7ea..0bc933b 100644
--- a/ojluni/src/main/java/java/security/PrivateKey.java
+++ b/ojluni/src/main/java/java/security/PrivateKey.java
@@ -54,6 +54,7 @@
*
* @author Benjamin Renaud
* @author Josh Bloch
+ * @since 1.1
*/
public interface PrivateKey extends Key, javax.security.auth.Destroyable {
diff --git a/ojluni/src/main/java/java/security/PrivilegedActionException.java b/ojluni/src/main/java/java/security/PrivilegedActionException.java
index b1eb28f..ddfb89d 100644
--- a/ojluni/src/main/java/java/security/PrivilegedActionException.java
+++ b/ojluni/src/main/java/java/security/PrivilegedActionException.java
@@ -47,6 +47,7 @@
* <i>cause</i>, and may be accessed via the {@link Throwable#getCause()}
* method, as well as the aforementioned "legacy method."
*
+ * @since 1.2
* @see PrivilegedExceptionAction
* @see AccessController#doPrivileged(PrivilegedExceptionAction)
* @see AccessController#doPrivileged(PrivilegedExceptionAction,AccessControlContext)
diff --git a/ojluni/src/main/java/java/security/ProtectionDomain.java b/ojluni/src/main/java/java/security/ProtectionDomain.java
index 9e117a6..47e9fb1 100644
--- a/ojluni/src/main/java/java/security/ProtectionDomain.java
+++ b/ojluni/src/main/java/java/security/ProtectionDomain.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/java/security/ProviderException.java b/ojluni/src/main/java/java/security/ProviderException.java
index b372ee7..48c3638 100644
--- a/ojluni/src/main/java/java/security/ProviderException.java
+++ b/ojluni/src/main/java/java/security/ProviderException.java
@@ -32,6 +32,7 @@
* throw specialized, provider-specific runtime errors.
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class ProviderException extends RuntimeException {
diff --git a/ojluni/src/main/java/java/security/PublicKey.java b/ojluni/src/main/java/java/security/PublicKey.java
index df49807..986f9d0 100644
--- a/ojluni/src/main/java/java/security/PublicKey.java
+++ b/ojluni/src/main/java/java/security/PublicKey.java
@@ -34,6 +34,7 @@
* See, for example, the DSAPublicKey interface in
* {@code java.security.interfaces}.
*
+ * @since 1.1
* @see Key
* @see PrivateKey
* @see Certificate
diff --git a/ojluni/src/main/java/java/security/SecurityPermission.java b/ojluni/src/main/java/java/security/SecurityPermission.java
index f02755c..a4eefcc 100644
--- a/ojluni/src/main/java/java/security/SecurityPermission.java
+++ b/ojluni/src/main/java/java/security/SecurityPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/java/security/SignatureException.java b/ojluni/src/main/java/java/security/SignatureException.java
index 2e1fa59..7788e12 100644
--- a/ojluni/src/main/java/java/security/SignatureException.java
+++ b/ojluni/src/main/java/java/security/SignatureException.java
@@ -29,6 +29,7 @@
* This is the generic Signature exception.
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public class SignatureException extends GeneralSecurityException {
diff --git a/ojluni/src/main/java/java/security/SignedObject.java b/ojluni/src/main/java/java/security/SignedObject.java
index 9ac864e..e14c0fa 100644
--- a/ojluni/src/main/java/java/security/SignedObject.java
+++ b/ojluni/src/main/java/java/security/SignedObject.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -81,13 +81,12 @@
* verification in an attempt to bypass a security check.
*
* <p> The signature algorithm can be, among others, the NIST standard
- * DSA, using DSA and SHA-1. The algorithm is specified using the
+ * DSA, using DSA and SHA-256. The algorithm is specified using the
* same convention as that for signatures. The DSA algorithm using the
- * SHA-1 message digest algorithm can be specified, for example, as
- * "SHA/DSA" or "SHA-1/DSA" (they are equivalent). In the case of
- * RSA, there are multiple choices for the message digest algorithm,
- * so the signing algorithm could be specified as, for example,
- * "MD2/RSA", "MD5/RSA" or "SHA-1/RSA". The algorithm name must be
+ * SHA-256 message digest algorithm can be specified, for example, as
+ * "SHA256withDSA". In the case of
+ * RSA the signing algorithm could be specified as, for example,
+ * "SHA256withRSA". The algorithm name must be
* specified, as there is no default.
*
* <p> The name of the Cryptography Package Provider is designated
@@ -114,6 +113,7 @@
* @see Signature
*
* @author Li Gong
+ * @since 1.2
*/
public final class SignedObject implements Serializable {
diff --git a/ojluni/src/main/java/java/security/Signer.java b/ojluni/src/main/java/java/security/Signer.java
index 077538d..56d3780 100644
--- a/ojluni/src/main/java/java/security/Signer.java
+++ b/ojluni/src/main/java/java/security/Signer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,13 +38,15 @@
* @see Identity
*
* @author Benjamin Renaud
+ * @since 1.1
*
- * @deprecated This class is no longer used. Its functionality has been
- * replaced by {@code java.security.KeyStore}, the
- * {@code java.security.cert} package, and
- * {@code java.security.Principal}.
+ * @deprecated This class is deprecated and subject to removal in a future
+ * version of Java SE. It has been replaced by
+ * {@code java.security.KeyStore}, the {@code java.security.cert} package,
+ * and {@code java.security.Principal}.
*/
-@Deprecated
+@Deprecated(since="1.2", forRemoval=true)
+@SuppressWarnings("removal")
public abstract class Signer extends Identity {
private static final long serialVersionUID = -1763464102261361480L;
diff --git a/ojluni/src/main/java/java/security/Timestamp.java b/ojluni/src/main/java/java/security/Timestamp.java
index f66d288..668a611 100644
--- a/ojluni/src/main/java/java/security/Timestamp.java
+++ b/ojluni/src/main/java/java/security/Timestamp.java
@@ -141,7 +141,7 @@
* its signer's certificate.
*/
public String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("(");
sb.append("timestamp: " + timestamp);
List<? extends Certificate> certs = signerCertPath.getCertificates();
diff --git a/ojluni/src/main/java/java/security/UnresolvedPermission.java b/ojluni/src/main/java/java/security/UnresolvedPermission.java
index 6f3bf4a..7e535da 100644
--- a/ojluni/src/main/java/java/security/UnresolvedPermission.java
+++ b/ojluni/src/main/java/java/security/UnresolvedPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/java/security/interfaces/DSAKey.java b/ojluni/src/main/java/java/security/interfaces/DSAKey.java
index d78b3e1..64ac8c4 100644
--- a/ojluni/src/main/java/java/security/interfaces/DSAKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/DSAKey.java
@@ -35,6 +35,7 @@
*
* @author Benjamin Renaud
* @author Josh Bloch
+ * @since 1.1
*/
public interface DSAKey {
diff --git a/ojluni/src/main/java/java/security/interfaces/DSAKeyPairGenerator.java b/ojluni/src/main/java/java/security/interfaces/DSAKeyPairGenerator.java
index e50cfd2..96e55eb 100644
--- a/ojluni/src/main/java/java/security/interfaces/DSAKeyPairGenerator.java
+++ b/ojluni/src/main/java/java/security/interfaces/DSAKeyPairGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,9 +32,12 @@
*
* <p>The {@code initialize} methods may each be called any number
* of times. If no {@code initialize} method is called on a
- * DSAKeyPairGenerator, the default is to generate 1024-bit keys, using
- * precomputed p, q and g parameters and an instance of SecureRandom as
- * the random bit source.
+ * DSAKeyPairGenerator, each provider that implements this interface
+ * should supply (and document) a default initialization. Note that
+ * defaults may vary across different providers. Additionally, the default
+ * value for a provider may change in a future version. Therefore, it is
+ * recommended to explicitly initialize the DSAKeyPairGenerator instead
+ * of relying on provider-specific defaults.
*
* <p>Users wishing to indicate DSA-specific parameters, and to generate a key
* pair suitable for use with the DSA algorithm typically
@@ -45,16 +48,17 @@
* KeyPairGenerator {@code getInstance} method with "DSA"
* as its argument.
*
- * <li>Initialize the generator by casting the result to a DSAKeyPairGenerator
- * and calling one of the
- * {@code initialize} methods from this DSAKeyPairGenerator interface.
+ * <li>Check if the returned key pair generator is an instance of
+ * DSAKeyPairGenerator before casting the result to a DSAKeyPairGenerator
+ * and calling one of the {@code initialize} methods from this
+ * DSAKeyPairGenerator interface.
*
* <li>Generate a key pair by calling the {@code generateKeyPair}
- * method from the KeyPairGenerator class.
+ * method of the KeyPairGenerator class.
*
* </ol>
*
- * <p>Note: it is not always necessary to do do algorithm-specific
+ * <p>Note: it is not always necessary to do algorithm-specific
* initialization for a DSA key pair generator. That is, it is not always
* necessary to call an {@code initialize} method in this interface.
* Algorithm-independent initialization using the {@code initialize} method
@@ -63,8 +67,9 @@
* parameters.
*
* <p>Note: Some earlier implementations of this interface may not support
- * larger sizes of DSA parameters such as 2048 and 3072-bit.
+ * larger values of DSA parameters such as 3072-bit.
*
+ * @since 1.1
* @see java.security.KeyPairGenerator
*/
public interface DSAKeyPairGenerator {
@@ -96,8 +101,7 @@
* p, q and g parameters. If it is false, the method uses precomputed
* parameters for the modulus length requested. If there are no
* precomputed parameters for that modulus length, an exception will be
- * thrown. It is guaranteed that there will always be
- * default parameters for modulus lengths of 512 and 1024 bits.
+ * thrown.
*
* @param modlen the modulus length in bits. Valid values are any
* multiple of 64 between 512 and 1024, inclusive, 2048, and 3072.
diff --git a/ojluni/src/main/java/java/security/interfaces/DSAParams.java b/ojluni/src/main/java/java/security/interfaces/DSAParams.java
index 8c46ed5..2eafe87 100644
--- a/ojluni/src/main/java/java/security/interfaces/DSAParams.java
+++ b/ojluni/src/main/java/java/security/interfaces/DSAParams.java
@@ -38,6 +38,7 @@
*
* @author Benjamin Renaud
* @author Josh Bloch
+ * @since 1.1
*/
public interface DSAParams {
diff --git a/ojluni/src/main/java/java/security/interfaces/DSAPrivateKey.java b/ojluni/src/main/java/java/security/interfaces/DSAPrivateKey.java
index 81ab358..b23a5c1 100644
--- a/ojluni/src/main/java/java/security/interfaces/DSAPrivateKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/DSAPrivateKey.java
@@ -37,6 +37,7 @@
* @see DSAPublicKey
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public interface DSAPrivateKey extends DSAKey, java.security.PrivateKey {
diff --git a/ojluni/src/main/java/java/security/interfaces/DSAPublicKey.java b/ojluni/src/main/java/java/security/interfaces/DSAPublicKey.java
index e56b795..fb4a2f3 100644
--- a/ojluni/src/main/java/java/security/interfaces/DSAPublicKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/DSAPublicKey.java
@@ -37,6 +37,7 @@
* @see DSAPrivateKey
*
* @author Benjamin Renaud
+ * @since 1.1
*/
public interface DSAPublicKey extends DSAKey, java.security.PublicKey {
diff --git a/ojluni/src/main/java/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java b/ojluni/src/main/java/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
index f85d96a..fd42c5c 100644
--- a/ojluni/src/main/java/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,8 +30,8 @@
/**
* The interface to an RSA multi-prime private key, as defined in the
- * PKCS#1 v2.1, using the <i>Chinese Remainder Theorem</i>
- * (CRT) information values.
+ * <a href="https://tools.ietf.org/rfc/rfc8017.txt">PKCS#1 v2.2</a> standard,
+ * using the <i>Chinese Remainder Theorem</i> (CRT) information values.
*
* @author Valerie Peng
*
diff --git a/ojluni/src/main/java/java/security/interfaces/RSAPrivateCrtKey.java b/ojluni/src/main/java/java/security/interfaces/RSAPrivateCrtKey.java
index 0408fea..e6acef1 100644
--- a/ojluni/src/main/java/java/security/interfaces/RSAPrivateCrtKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/RSAPrivateCrtKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,10 +28,12 @@
import java.math.BigInteger;
/**
- * The interface to an RSA private key, as defined in the PKCS#1 standard,
+ * The interface to an RSA private key, as defined in the
+ * <a href="https://tools.ietf.org/rfc/rfc8017.txt">PKCS#1 v2.2</a> standard,
* using the <i>Chinese Remainder Theorem</i> (CRT) information values.
*
* @author Jan Luehe
+ * @since 1.2
*
*
* @see RSAPrivateKey
diff --git a/ojluni/src/main/java/java/security/interfaces/RSAPrivateKey.java b/ojluni/src/main/java/java/security/interfaces/RSAPrivateKey.java
index 5d69ad6..390da4e 100644
--- a/ojluni/src/main/java/java/security/interfaces/RSAPrivateKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/RSAPrivateKey.java
@@ -31,6 +31,7 @@
* The interface to an RSA private key.
*
* @author Jan Luehe
+ * @since 1.2
*
*
* @see RSAPrivateCrtKey
diff --git a/ojluni/src/main/java/java/security/interfaces/RSAPublicKey.java b/ojluni/src/main/java/java/security/interfaces/RSAPublicKey.java
index a698c05..f195306 100644
--- a/ojluni/src/main/java/java/security/interfaces/RSAPublicKey.java
+++ b/ojluni/src/main/java/java/security/interfaces/RSAPublicKey.java
@@ -31,6 +31,7 @@
* The interface to an RSA public key.
*
* @author Jan Luehe
+ * @since 1.2
*
*/
diff --git a/ojluni/src/main/java/java/security/interfaces/XECKey.java b/ojluni/src/main/java/java/security/interfaces/XECKey.java
new file mode 100644
index 0000000..7412daa
--- /dev/null
+++ b/ojluni/src/main/java/java/security/interfaces/XECKey.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.security.interfaces;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * An interface for an elliptic curve public/private key as defined by
+ * RFC 7748. These keys are distinct from the keys represented by
+ * {@code ECKey}, and they are intended for use with algorithms based on RFC
+ * 7748 such as the XDH {@code KeyAgreement} algorithm. This interface allows
+ * access to the algorithm parameters associated with the key.
+ *
+ * @since 11
+ */
+public interface XECKey {
+ /**
+ * Returns the algorithm parameters associated
+ * with the key.
+ *
+ * @return the associated algorithm parameters
+ */
+ AlgorithmParameterSpec getParams();
+}
+
diff --git a/ojluni/src/main/java/java/security/interfaces/XECPrivateKey.java b/ojluni/src/main/java/java/security/interfaces/XECPrivateKey.java
new file mode 100644
index 0000000..5476777
--- /dev/null
+++ b/ojluni/src/main/java/java/security/interfaces/XECPrivateKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.security.interfaces;
+
+import java.security.PrivateKey;
+import java.util.Optional;
+
+/**
+ * An interface for an elliptic curve private key as defined by RFC 7748.
+ * These keys are distinct from the keys represented by {@code ECPrivateKey},
+ * and they are intended for use with algorithms based on RFC 7748 such as the
+ * XDH {@code KeyAgreement} algorithm.
+ *
+ * An XEC private key is an encoded scalar value as described in RFC 7748.
+ * The decoding procedure defined in this RFC includes an operation that forces
+ * certain bits of the key to either 1 or 0. This operation is known as
+ * "pruning" or "clamping" the private key. Arrays returned by this interface
+ * are unpruned, and implementations will need to prune the array before
+ * using it in any numerical operations.
+ *
+ * @since 11
+ */
+public interface XECPrivateKey extends XECKey, PrivateKey {
+
+ /**
+ * Get the scalar value encoded as an unpruned byte array. A new copy of
+ * the array is returned each time this method is called.
+ *
+ * @return the unpruned encoded scalar value, or an empty Optional if the
+ * scalar cannot be extracted (e.g. if the provider is a hardware token
+ * and the private key is not allowed to leave the crypto boundary).
+ */
+ Optional<byte[]> getScalar();
+}
+
diff --git a/ojluni/src/main/java/java/security/interfaces/XECPublicKey.java b/ojluni/src/main/java/java/security/interfaces/XECPublicKey.java
new file mode 100644
index 0000000..6ec200b
--- /dev/null
+++ b/ojluni/src/main/java/java/security/interfaces/XECPublicKey.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.security.interfaces;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+/**
+ * An interface for an elliptic curve public key as defined by RFC 7748.
+ * These keys are distinct from the keys represented by {@code ECPublicKey},
+ * and they are intended for use with algorithms based on RFC 7748 such as the
+ * XDH {@code KeyAgreement} algorithm.
+ *
+ * An XEC public key is a particular point on the curve, which is represented
+ * using only its u-coordinate as described in RFC 7748. A u-coordinate is an
+ * element of the field of integers modulo some value that is determined by
+ * the algorithm parameters. This field element is represented by a BigInteger
+ * which may hold any value. That is, the BigInteger is not restricted to the
+ * range of canonical field elements.
+ *
+ * @since 11
+ */
+public interface XECPublicKey extends XECKey, PublicKey {
+
+ /**
+ * Get the u coordinate of the point.
+ *
+ * @return the u-coordinate, represented using a BigInteger which may hold
+ * any value
+ */
+ BigInteger getU();
+
+}
+
diff --git a/ojluni/src/main/java/java/security/spec/XECPrivateKeySpec.java b/ojluni/src/main/java/java/security/spec/XECPrivateKeySpec.java
new file mode 100644
index 0000000..02687b5
--- /dev/null
+++ b/ojluni/src/main/java/java/security/spec/XECPrivateKeySpec.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.security.spec;
+
+import java.util.Objects;
+
+/**
+ * A class representing elliptic curve private keys as defined in RFC 7748,
+ * including the curve and other algorithm parameters. The private key is
+ * represented as an encoded scalar value. The decoding procedure defined in
+ * the RFC includes an operation that forces certain bits of the key to either
+ * 1 or 0. This operation is known as "pruning" or "clamping" the private key.
+ * All arrays in this spec are unpruned, and implementations will need to prune
+ * the array before using it in any numerical operations.
+ *
+ * @since 11
+ */
+public class XECPrivateKeySpec implements KeySpec {
+
+ private final AlgorithmParameterSpec params;
+ private final byte[] scalar;
+
+ /**
+ * Construct a private key spec using the supplied parameters and
+ * encoded scalar value.
+ *
+ * @param params the algorithm parameters
+ * @param scalar the unpruned encoded scalar value. This array is copied
+ * to protect against subsequent modification.
+ *
+ * @throws NullPointerException if {@code params} or {@code scalar}
+ * is null.
+ */
+ public XECPrivateKeySpec(AlgorithmParameterSpec params, byte[] scalar) {
+ Objects.requireNonNull(params, "params must not be null");
+ Objects.requireNonNull(scalar, "scalar must not be null");
+
+ this.params = params;
+ this.scalar = scalar.clone();
+ }
+
+ /**
+ * Get the algorithm parameters that define the curve and other settings.
+ *
+ * @return the algorithm parameters
+ */
+ public AlgorithmParameterSpec getParams() {
+ return params;
+ }
+
+ /**
+ * Get the scalar value encoded as an unpruned byte array. A new copy of
+ * the array is returned each time this method is called.
+ *
+ * @return the unpruned encoded scalar value
+ */
+ public byte[] getScalar() {
+ return scalar.clone();
+ }
+}
diff --git a/ojluni/src/main/java/java/security/spec/XECPublicKeySpec.java b/ojluni/src/main/java/java/security/spec/XECPublicKeySpec.java
new file mode 100644
index 0000000..162cf10
--- /dev/null
+++ b/ojluni/src/main/java/java/security/spec/XECPublicKeySpec.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.security.spec;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * A class representing elliptic curve public keys as defined in RFC 7748,
+ * including the curve and other algorithm parameters. The public key is a
+ * particular point on the curve, which is represented using only its
+ * u-coordinate. A u-coordinate is an element of the field of integers modulo
+ * some value that is determined by the algorithm parameters. This field
+ * element is represented by a BigInteger which may hold any value. That is,
+ * the BigInteger is not restricted to the range of canonical field elements.
+ *
+ * @since 11
+ */
+public class XECPublicKeySpec implements KeySpec {
+
+ private final AlgorithmParameterSpec params;
+ private final BigInteger u;
+
+ /**
+ * Construct a public key spec using the supplied parameters and
+ * u coordinate.
+ *
+ * @param params the algorithm parameters
+ * @param u the u-coordinate of the point, represented using a BigInteger
+ * which may hold any value
+ *
+ * @throws NullPointerException if {@code params} or {@code u}
+ * is null.
+ */
+ public XECPublicKeySpec(AlgorithmParameterSpec params, BigInteger u) {
+ Objects.requireNonNull(params, "params must not be null");
+ Objects.requireNonNull(u, "u must not be null");
+
+ this.params = params;
+ this.u = u;
+ }
+
+ /**
+ * Get the algorithm parameters that define the curve and other settings.
+ *
+ * @return the parameters
+ */
+ public AlgorithmParameterSpec getParams() {
+ return params;
+ }
+
+ /**
+ * Get the u coordinate of the point.
+ *
+ * @return the u-coordinate, represented using a BigInteger which may hold
+ * any value
+ */
+ public BigInteger getU() {
+ return u;
+ }
+}
diff --git a/ojluni/src/test/java/lang/invoke/InvokeGenericTest.java b/ojluni/src/test/java/lang/invoke/InvokeGenericTest.java
new file mode 100644
index 0000000..ef201ca
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/InvokeGenericTest.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for java.lang.invoke.MethodHandle.invoke
+ * @compile InvokeGenericTest.java
+ * @run testng/othervm test.java.lang.invoke.InvokeGenericTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import java.lang.reflect.*;
+import java.util.*;
+import org.testng.*;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.*;
+
+/**
+ *
+ * @author jrose
+ */
+@SuppressWarnings("cast") // various casts help emphasize arguments to invokeExact
+public class InvokeGenericTest {
+ // How much output?
+ static int verbosity = 0;
+ static {
+ String vstr = System.getProperty("test.java.lang.invoke.InvokeGenericTest.verbosity");
+ if (vstr != null) verbosity = Integer.parseInt(vstr);
+ }
+
+// public static void main(String... av) throws Throwable {
+// new InvokeGenericTest().testFirst();
+// }
+
+ @Test
+ public void testFirst() throws Throwable {
+ verbosity += 9; try {
+ // left blank for debugging
+ } finally { printCounts(); verbosity -= 9; }
+ }
+
+ public InvokeGenericTest() {
+ }
+
+ String testName;
+ static int allPosTests, allNegTests;
+ int posTests, negTests;
+ @AfterMethod
+ public void printCounts() {
+ if (verbosity >= 2 && (posTests | negTests) != 0) {
+ System.out.println();
+ if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run");
+ if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run");
+ allPosTests += posTests;
+ allNegTests += negTests;
+ posTests = negTests = 0;
+ }
+ }
+ void countTest(boolean positive) {
+ if (positive) ++posTests;
+ else ++negTests;
+ }
+ void countTest() { countTest(true); }
+ void startTest(String name) {
+ if (testName != null) printCounts();
+ if (verbosity >= 1)
+ System.out.println("["+name+"]");
+ posTests = negTests = 0;
+ testName = name;
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ calledLog.clear();
+ calledLog.add(null);
+ nextArgVal = INITIAL_ARG_VAL;
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ int posTests = allPosTests, negTests = allNegTests;
+ if (verbosity >= 2 && (posTests | negTests) != 0) {
+ System.out.println();
+ if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases");
+ if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases");
+ }
+ }
+
+ static List<Object> calledLog = new ArrayList<>();
+ static Object logEntry(String name, Object... args) {
+ return Arrays.asList(name, Arrays.asList(args));
+ }
+ static Object called(String name, Object... args) {
+ Object entry = logEntry(name, args);
+ calledLog.add(entry);
+ return entry;
+ }
+ static void assertCalled(String name, Object... args) {
+ Object expected = logEntry(name, args);
+ Object actual = calledLog.get(calledLog.size() - 1);
+ if (expected.equals(actual) && verbosity < 9) return;
+ System.out.println("assertCalled "+name+":");
+ System.out.println("expected: "+expected);
+ System.out.println("actual: "+actual);
+ System.out.println("ex. types: "+getClasses(expected));
+ System.out.println("act. types: "+getClasses(actual));
+ assertEquals("previous method call", expected, actual);
+ }
+ static void printCalled(MethodHandle target, String name, Object... args) {
+ if (verbosity >= 3)
+ System.out.println("calling MH="+target+" to "+name+Arrays.toString(args));
+ }
+
+ static Object castToWrapper(Object value, Class<?> dst) {
+ Object wrap = null;
+ if (value instanceof Number)
+ wrap = castToWrapperOrNull(((Number)value).longValue(), dst);
+ if (value instanceof Character)
+ wrap = castToWrapperOrNull((char)(Character)value, dst);
+ if (wrap != null) return wrap;
+ return dst.cast(value);
+ }
+
+ static Object castToWrapperOrNull(long value, Class<?> dst) {
+ if (dst == int.class || dst == Integer.class)
+ return (int)(value);
+ if (dst == long.class || dst == Long.class)
+ return (long)(value);
+ if (dst == char.class || dst == Character.class)
+ return (char)(value);
+ if (dst == short.class || dst == Short.class)
+ return (short)(value);
+ if (dst == float.class || dst == Float.class)
+ return (float)(value);
+ if (dst == double.class || dst == Double.class)
+ return (double)(value);
+ if (dst == byte.class || dst == Byte.class)
+ return (byte)(value);
+ if (dst == boolean.class || dst == boolean.class)
+ return ((value % 29) & 1) == 0;
+ return null;
+ }
+
+ static final int ONE_MILLION = (1000*1000), // first int value
+ TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits
+ INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit;
+ static long nextArgVal;
+ static long nextArg(boolean moreBits) {
+ long val = nextArgVal++;
+ long sign = -(val & 1); // alternate signs
+ val >>= 1;
+ if (moreBits)
+ // Guarantee some bits in the high word.
+ // In any case keep the decimal representation simple-looking,
+ // with lots of zeroes, so as not to make the printed decimal
+ // strings unnecessarily noisy.
+ val += (val % ONE_MILLION) * TEN_BILLION;
+ return val ^ sign;
+ }
+ static int nextArg() {
+ // Produce a 32-bit result something like ONE_MILLION+(smallint).
+ // Example: 1_000_042.
+ return (int) nextArg(false);
+ }
+ static long nextArg(Class<?> kind) {
+ if (kind == long.class || kind == Long.class ||
+ kind == double.class || kind == Double.class)
+ // produce a 64-bit result something like
+ // ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
+ // Example: 10_000_420_001_000_042.
+ return nextArg(true);
+ return (long) nextArg();
+ }
+
+ static Object randomArg(Class<?> param) {
+ Object wrap = castToWrapperOrNull(nextArg(param), param);
+ if (wrap != null) {
+ return wrap;
+ }
+// import sun.invoke.util.Wrapper;
+// Wrapper wrap = Wrapper.forBasicType(dst);
+// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
+// wrap = Wrapper.forWrapperType(dst);
+// if (wrap != Wrapper.OBJECT)
+// return wrap.wrap(nextArg++);
+ if (param.isInterface()) {
+ for (Class<?> c : param.getClasses()) {
+ if (param.isAssignableFrom(c) && !c.isInterface())
+ { param = c; break; }
+ }
+ }
+ if (param.isInterface() || param.isAssignableFrom(String.class))
+ return "#"+nextArg();
+ else
+ try {
+ return param.newInstance();
+ } catch (InstantiationException | IllegalAccessException ex) {
+ }
+ return null; // random class not Object, String, Integer, etc.
+ }
+ static Object[] randomArgs(Class<?>... params) {
+ Object[] args = new Object[params.length];
+ for (int i = 0; i < args.length; i++)
+ args[i] = randomArg(params[i]);
+ return args;
+ }
+ static Object[] randomArgs(int nargs, Class<?> param) {
+ Object[] args = new Object[nargs];
+ for (int i = 0; i < args.length; i++)
+ args[i] = randomArg(param);
+ return args;
+ }
+
+ static final Object ANON_OBJ = new Object();
+ static Object zeroArg(Class<?> param) {
+ Object x = castToWrapperOrNull(0L, param);
+ if (x != null) return x;
+ if (param.isInterface() || param.isAssignableFrom(String.class)) return "\"\"";
+ if (param == Object.class) return ANON_OBJ;
+ if (param.getComponentType() != null) return Array.newInstance(param.getComponentType(), 0);
+ return null;
+ }
+ static Object[] zeroArgs(Class<?>... params) {
+ Object[] args = new Object[params.length];
+ for (int i = 0; i < args.length; i++)
+ args[i] = zeroArg(params[i]);
+ return args;
+ }
+ static Object[] zeroArgs(List<Class<?>> params) {
+ return zeroArgs(params.toArray(new Class<?>[0]));
+ }
+
+ @SafeVarargs @SuppressWarnings("varargs")
+ static <T, E extends T> T[] array(Class<T[]> atype, E... a) {
+ return Arrays.copyOf(a, a.length, atype);
+ }
+ @SafeVarargs @SuppressWarnings("varargs")
+ static <T> T[] cat(T[] a, T... b) {
+ int alen = a.length, blen = b.length;
+ if (blen == 0) return a;
+ T[] c = Arrays.copyOf(a, alen + blen);
+ System.arraycopy(b, 0, c, alen, blen);
+ return c;
+ }
+ static Integer[] boxAll(int... vx) {
+ Integer[] res = new Integer[vx.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = vx[i];
+ }
+ return res;
+ }
+ static Object getClasses(Object x) {
+ if (x == null) return x;
+ if (x instanceof String) return x; // keep the name
+ if (x instanceof List) {
+ // recursively report classes of the list elements
+ Object[] xa = ((List)x).toArray();
+ for (int i = 0; i < xa.length; i++)
+ xa[i] = getClasses(xa[i]);
+ return Arrays.asList(xa);
+ }
+ return x.getClass().getSimpleName();
+ }
+
+ static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
+ return changeArgTypes(target, 0, 999, argType);
+ }
+ static MethodHandle changeArgTypes(MethodHandle target,
+ int beg, int end, Class<?> argType) {
+ MethodType targetType = target.type();
+ end = Math.min(end, targetType.parameterCount());
+ ArrayList<Class<?>> argTypes = new ArrayList<>(targetType.parameterList());
+ Collections.fill(argTypes.subList(beg, end), argType);
+ MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
+ return target.asType(ttype2);
+ }
+
+ // This lookup is good for all members in and under InvokeGenericTest.
+ static final Lookup LOOKUP = MethodHandles.lookup();
+
+ Map<List<Class<?>>, MethodHandle> CALLABLES = new HashMap<>();
+ MethodHandle callable(List<Class<?>> params) {
+ MethodHandle mh = CALLABLES.get(params);
+ if (mh == null) {
+ mh = collector_MH.asType(methodType(Object.class, params));
+ CALLABLES.put(params, mh);
+ }
+ return mh;
+ }
+ MethodHandle callable(Class<?>... params) {
+ return callable(Arrays.asList(params));
+ }
+ private static Object collector(Object... args) {
+ return Arrays.asList(args);
+ }
+ private static final MethodHandle collector_MH;
+ static {
+ try {
+ collector_MH
+ = LOOKUP.findStatic(LOOKUP.lookupClass(),
+ "collector",
+ methodType(Object.class, Object[].class));
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Test
+ public void testSimple() throws Throwable {
+ startTest("testSimple");
+ countTest();
+ String[] args = { "one", "two" };
+ MethodHandle mh = callable(Object.class, String.class);
+ Object res; List<?> resl;
+ res = resl = (List<?>) mh.invoke((String)args[0], (Object)args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.asList(args), res);
+ }
+
+ @Test
+ public void testSimplePrims() throws Throwable {
+ startTest("testSimplePrims");
+ countTest();
+ int[] args = { 1, 2 };
+ MethodHandle mh = callable(Object.class, Object.class);
+ Object res; List<?> resl;
+ res = resl = (List<?>) mh.invoke(args[0], args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.toString(args), res.toString());
+ }
+
+ @Test
+ public void testAlternateName() throws Throwable {
+ startTest("testAlternateName");
+ countTest();
+ String[] args = { "one", "two" };
+ MethodHandle mh = callable(Object.class, String.class);
+ Object res; List<?> resl;
+ res = resl = (List<?>) mh.invoke((String)args[0], (Object)args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.asList(args), res);
+ }
+
+ @Test
+ public void testWrongArgumentCount() throws Throwable {
+ startTest("testWrongArgumentCount");
+ for (int i = 0; i <= 10; i++) {
+ testWrongArgumentCount(Collections.<Class<?>>nCopies(i, Integer.class));
+ if (i <= 4) {
+ testWrongArgumentCount(Collections.<Class<?>>nCopies(i, int.class));
+ testWrongArgumentCount(Collections.<Class<?>>nCopies(i, long.class));
+ }
+ }
+ }
+ public void testWrongArgumentCount(List<Class<?>> params) throws Throwable {
+ int max = params.size();
+ for (int i = 0; i < max; i++) {
+ List<Class<?>> params2 = params.subList(0, i);
+ for (int k = 0; k <= 2; k++) {
+ if (k == 1) params = methodType(Object.class, params).generic().parameterList();
+ if (k == 2) params2 = methodType(Object.class, params2).generic().parameterList();
+ testWrongArgumentCount(params, params2);
+ testWrongArgumentCount(params2, params);
+ }
+ }
+ }
+ public void testWrongArgumentCount(List<Class<?>> expect, List<Class<?>> observe) throws Throwable {
+ countTest(false);
+ if (expect.equals(observe))
+ assert(false);
+ MethodHandle target = callable(expect);
+ Object[] args = zeroArgs(observe);
+ Object junk;
+ try {
+ switch (args.length) {
+ case 0:
+ junk = target.invoke(); break;
+ case 1:
+ junk = target.invoke(args[0]); break;
+ case 2:
+ junk = target.invoke(args[0], args[1]); break;
+ case 3:
+ junk = target.invoke(args[0], args[1], args[2]); break;
+ case 4:
+ junk = target.invoke(args[0], args[1], args[2], args[3]); break;
+ default:
+ junk = target.invokeWithArguments(args); break;
+ }
+ } catch (WrongMethodTypeException ex) {
+ return;
+ } catch (Exception ex) {
+ throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
+ }
+ throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
+ }
+
+ /** Make a list of all combinations of the given types, with the given arities.
+ * A void return type is possible iff the first type is void.class.
+ */
+ static List<MethodType> allMethodTypes(int minargc, int maxargc, Class<?>... types) {
+ ArrayList<MethodType> result = new ArrayList<>();
+ if (types.length > 0) {
+ ArrayList<MethodType> argcTypes = new ArrayList<>();
+ // build arity-zero types first
+ for (Class<?> rtype : types) {
+ argcTypes.add(MethodType.methodType(rtype));
+ }
+ if (types[0] == void.class)
+ // void is not an argument type
+ types = Arrays.copyOfRange(types, 1, types.length);
+ for (int argc = 0; argc <= maxargc; argc++) {
+ if (argc >= minargc)
+ result.addAll(argcTypes);
+ if (argc >= maxargc)
+ break;
+ ArrayList<MethodType> prevTypes = argcTypes;
+ argcTypes = new ArrayList<>();
+ for (MethodType prevType : prevTypes) {
+ for (Class<?> ptype : types) {
+ argcTypes.add(prevType.insertParameterTypes(argc, ptype));
+ }
+ }
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+ static List<MethodType> allMethodTypes(int argc, Class<?>... types) {
+ return allMethodTypes(argc, argc, types);
+ }
+
+ MethodHandle toString_MH;
+
+ @Test
+ public void testReferenceConversions() throws Throwable {
+ startTest("testReferenceConversions");
+ toString_MH = LOOKUP.
+ findVirtual(Object.class, "toString", MethodType.methodType(String.class));
+ Object[] args = { "one", "two" };
+ for (MethodType type : allMethodTypes(2, Object.class, String.class, CharSequence.class)) {
+ testReferenceConversions(type, args);
+ }
+ }
+ public void testReferenceConversions(MethodType type, Object... args) throws Throwable {
+ countTest();
+ int nargs = args.length;
+ List<Object> argList = Arrays.asList(args);
+ String expectString = argList.toString();
+ if (verbosity > 3) System.out.println("target type: "+type+expectString);
+ MethodHandle mh = callable(type.parameterList());
+ mh = MethodHandles.filterReturnValue(mh, toString_MH);
+ mh = mh.asType(type);
+ Object res = null;
+ if (nargs == 2) {
+ res = mh.invoke((Object)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ res = mh.invoke((String)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ res = mh.invoke((Object)args[0], (String)args[1]);
+ assertEquals(expectString, res);
+ res = mh.invoke((String)args[0], (String)args[1]);
+ assertEquals(expectString, res);
+ res = mh.invoke((String)args[0], (CharSequence)args[1]);
+ assertEquals(expectString, res);
+ res = mh.invoke((CharSequence)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ res = (String) mh.invoke((Object)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ res = (String) mh.invoke((String)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ res = (CharSequence) mh.invoke((String)args[0], (Object)args[1]);
+ assertEquals(expectString, res);
+ } else {
+ assert(false); // write this code
+ }
+ //System.out.println(res);
+ }
+
+
+ @Test
+ public void testBoxConversions() throws Throwable {
+ startTest("testBoxConversions");
+ countTest();
+ Object[] args = { 1, 2 };
+ MethodHandle mh = callable(Object.class, int.class);
+ Object res; List<?> resl; int resi;
+ res = resl = (List<?>) mh.invoke((int)args[0], (Object)args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.asList(args), res);
+ mh = MethodHandles.identity(int.class);
+ mh = MethodHandles.dropArguments(mh, 1, int.class);
+ res = resi = (int) mh.invoke((Object) args[0], (Object) args[1]);
+ assertEquals(args[0], res);
+ res = resi = (int) mh.invoke((int) args[0], (Object) args[1]);
+ assertEquals(args[0], res);
+ }
+
+}
diff --git a/ojluni/src/test/java/lang/invoke/InvokeWithArgumentsTest.java b/ojluni/src/test/java/lang/invoke/InvokeWithArgumentsTest.java
new file mode 100644
index 0000000..63a2101
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/InvokeWithArgumentsTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary basic tests for MethodHandle.invokeWithArguments
+ * @run testng test.java.lang.invoke.InvokeWithArgumentsTest
+ */
+
+package test.java.lang.invoke;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.WrongMethodTypeException;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class InvokeWithArgumentsTest {
+ static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+ static Object[] arity(Object o1, Object o2, Object... a) {
+ return a;
+ }
+
+ @Test
+ public void testArity() throws Throwable {
+ MethodHandle mh = L.findStatic(L.lookupClass(), "arity",
+ methodType(Object[].class, Object.class, Object.class, Object[].class));
+
+ try {
+ mh.invokeWithArguments("");
+ Assert.fail("WrongMethodTypeException expected");
+ } catch (WrongMethodTypeException e) {}
+ }
+
+ static Object[] passThrough(String... a) {
+ return a;
+ }
+
+ static Object[] pack(Object o, Object... a) {
+ return a;
+ }
+
+ @Test
+ public void testArrayNoPassThrough() throws Throwable {
+ String[] actual = {"A", "B"};
+
+ MethodHandle mh = L.findStatic(L.lookupClass(), "passThrough",
+ methodType(Object[].class, String[].class));
+
+ // Note: the actual array is not preserved, the elements will be
+ // unpacked and then packed into a new array before invoking the method
+ String[] expected = (String[]) mh.invokeWithArguments(actual);
+
+ Assert.assertTrue(actual != expected, "Array should not pass through");
+ Assert.assertEquals(actual, expected, "Array contents should be equal");
+ }
+
+ @Test
+ public void testArrayPack() throws Throwable {
+ String[] actual = new String[]{"A", "B"};
+
+ MethodHandle mh = L.findStatic(L.lookupClass(), "pack",
+ methodType(Object[].class, Object.class, Object[].class));
+
+ // Note: since String[] can be cast to Object, the actual String[] array
+ // will cast to Object become the single element of a new Object[] array
+ Object[] expected = (Object[]) mh.invokeWithArguments("", actual);
+
+ Assert.assertEquals(1, expected.length, "Array should contain just one element");
+ Assert.assertTrue(actual == expected[0], "Array should pass through");
+ }
+
+ static void intArray(int... a) {
+ }
+
+ @Test
+ public void testPrimitiveArrayWithNull() throws Throwable {
+ MethodHandle mh = L.findStatic(L.lookupClass(), "intArray",
+ methodType(void.class, int[].class));
+ try {
+ mh.invokeWithArguments(null, null);
+ Assert.fail("NullPointerException expected");
+ } catch (NullPointerException e) {}
+ }
+
+ @Test
+ public void testPrimitiveArrayWithRef() throws Throwable {
+ MethodHandle mh = L.findStatic(L.lookupClass(), "intArray",
+ methodType(void.class, int[].class));
+ try {
+ mh.invokeWithArguments("A", "B");
+ Assert.fail("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ }
+
+
+ static void numberArray(Number... a) {
+ }
+
+ @Test
+ public void testRefArrayWithCast() throws Throwable {
+ MethodHandle mh = L.findStatic(L.lookupClass(), "numberArray",
+ methodType(void.class, Number[].class));
+ // All numbers, should not throw
+ mh.invokeWithArguments(1, 1.0, 1.0F, 1L);
+
+ try {
+ mh.invokeWithArguments("A");
+ Assert.fail("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ }
+}
diff --git a/ojluni/src/test/java/lang/invoke/MethodHandlesTest.java b/ojluni/src/test/java/lang/invoke/MethodHandlesTest.java
new file mode 100644
index 0000000..314d2a2
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/MethodHandlesTest.java
@@ -0,0 +1,948 @@
+/*
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.remote.RemoteExample;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jrose
+ */
+public abstract class MethodHandlesTest {
+
+ static final Class<?> THIS_CLASS = MethodHandlesTest.class;
+ // How much output?
+ static int verbosity = 0;
+
+ static {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");
+ if (vstr != null) verbosity = Integer.parseInt(vstr);
+ }
+
+ // Set this true during development if you want to fast-forward to
+ // a particular new, non-working test. Tests which are known to
+ // work (or have recently worked) test this flag and return on true.
+ static final boolean CAN_SKIP_WORKING;
+
+ static {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".CAN_SKIP_WORKING");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".CAN_SKIP_WORKING");
+ CAN_SKIP_WORKING = Boolean.parseBoolean(vstr);
+ }
+
+ // Set 'true' to do about 15x fewer tests, especially those redundant with RicochetTest.
+ // This might be useful with -Xcomp stress tests that compile all method handles.
+ static boolean CAN_TEST_LIGHTLY = Boolean.getBoolean(THIS_CLASS.getName()+".CAN_TEST_LIGHTLY");
+
+ static final int MAX_ARG_INCREASE = 3;
+
+ String testName;
+ static int allPosTests, allNegTests;
+ int posTests, negTests;
+
+ @After
+ public void printCounts() {
+ if (verbosity >= 2 && (posTests | negTests) != 0) {
+ System.out.println();
+ if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run");
+ if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run");
+ allPosTests += posTests;
+ allNegTests += negTests;
+ posTests = negTests = 0;
+ }
+ }
+
+ void countTest(boolean positive) {
+ if (positive) ++posTests;
+ else ++negTests;
+ }
+
+ void countTest() { countTest(true); }
+
+ void startTest(String name) {
+ if (testName != null) printCounts();
+ if (verbosity >= 1)
+ System.out.println(name);
+ posTests = negTests = 0;
+ testName = name;
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ calledLog.clear();
+ calledLog.add(null);
+ nextArgVal = INITIAL_ARG_VAL;
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ int posTests = allPosTests, negTests = allNegTests;
+ if (verbosity >= 0 && (posTests | negTests) != 0) {
+ System.out.println();
+ if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases");
+ if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases");
+ }
+ }
+
+ static List<Object> calledLog = new ArrayList<>();
+
+ static Object logEntry(String name, Object... args) {
+ return Arrays.asList(name, Arrays.asList(args));
+ }
+
+ public static Object called(String name, Object... args) {
+ Object entry = logEntry(name, args);
+ calledLog.add(entry);
+ return entry;
+ }
+
+ static void assertCalled(String name, Object... args) {
+ Object expected = logEntry(name, args);
+ Object actual = calledLog.get(calledLog.size() - 1);
+ if (expected.equals(actual) && verbosity < 9) return;
+ System.out.println("assertCalled "+name+":");
+ System.out.println("expected: "+deepToString(expected));
+ System.out.println("actual: "+actual);
+ System.out.println("ex. types: "+getClasses(expected));
+ System.out.println("act. types: "+getClasses(actual));
+ assertEquals("previous method call", expected, actual);
+ }
+
+ static void printCalled(MethodHandle target, String name, Object... args) {
+ if (verbosity >= 3)
+ System.out.println("calling MH="+target+" to "+name+deepToString(args));
+ }
+
+ static String deepToString(Object x) {
+ if (x == null) return "null";
+ if (x instanceof Collection)
+ x = ((Collection)x).toArray();
+ if (x instanceof Object[]) {
+ Object[] ax = (Object[]) x;
+ ax = Arrays.copyOf(ax, ax.length, Object[].class);
+ for (int i = 0; i < ax.length; i++)
+ ax[i] = deepToString(ax[i]);
+ x = Arrays.deepToString(ax);
+ }
+ if (x.getClass().isArray())
+ try {
+ x = Arrays.class.getMethod("toString", x.getClass()).invoke(null, x);
+ } catch (ReflectiveOperationException ex) { throw new Error(ex); }
+ assert(!(x instanceof Object[]));
+ return x.toString();
+ }
+
+ static Object castToWrapper(Object value, Class<?> dst) {
+ Object wrap = null;
+ if (value instanceof Number)
+ wrap = castToWrapperOrNull(((Number)value).longValue(), dst);
+ if (value instanceof Character)
+ wrap = castToWrapperOrNull((char)(Character)value, dst);
+ if (wrap != null) return wrap;
+ return dst.cast(value);
+ }
+
+ @SuppressWarnings("cast") // primitive cast to (long) is part of the pattern
+ static Object castToWrapperOrNull(long value, Class<?> dst) {
+ if (dst == int.class || dst == Integer.class)
+ return (int)(value);
+ if (dst == long.class || dst == Long.class)
+ return (long)(value);
+ if (dst == char.class || dst == Character.class)
+ return (char)(value);
+ if (dst == short.class || dst == Short.class)
+ return (short)(value);
+ if (dst == float.class || dst == Float.class)
+ return (float)(value);
+ if (dst == double.class || dst == Double.class)
+ return (double)(value);
+ if (dst == byte.class || dst == Byte.class)
+ return (byte)(value);
+ if (dst == boolean.class || dst == boolean.class)
+ return ((value % 29) & 1) == 0;
+ return null;
+ }
+
+ static final int ONE_MILLION = (1000*1000), // first int value
+ TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits
+ INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit;
+ static long nextArgVal;
+
+ static long nextArg(boolean moreBits) {
+ long val = nextArgVal++;
+ long sign = -(val & 1); // alternate signs
+ val >>= 1;
+ if (moreBits)
+ // Guarantee some bits in the high word.
+ // In any case keep the decimal representation simple-looking,
+ // with lots of zeroes, so as not to make the printed decimal
+ // strings unnecessarily noisy.
+ val += (val % ONE_MILLION) * TEN_BILLION;
+ return val ^ sign;
+ }
+
+ static int nextArg() {
+ // Produce a 32-bit result something like ONE_MILLION+(smallint).
+ // Example: 1_000_042.
+ return (int) nextArg(false);
+ }
+
+ static long nextArg(Class<?> kind) {
+ if (kind == long.class || kind == Long.class ||
+ kind == double.class || kind == Double.class)
+ // produce a 64-bit result something like
+ // ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
+ // Example: 10_000_420_001_000_042.
+ return nextArg(true);
+ return (long) nextArg();
+ }
+
+ static Object randomArg(Class<?> param) {
+ Object wrap = castToWrapperOrNull(nextArg(param), param);
+ if (wrap != null) {
+ return wrap;
+ }
+ //import sun.invoke.util.Wrapper;
+ //Wrapper wrap = Wrapper.forBasicType(dst);
+ //if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
+ // wrap = Wrapper.forWrapperType(dst);
+ // if (wrap != Wrapper.OBJECT)
+ // return wrap.wrap(nextArg++);
+ if (param.isInterface()) {
+ for (Class<?> c : param.getClasses()) {
+ if (param.isAssignableFrom(c) && !c.isInterface())
+ { param = c; break; }
+ }
+ }
+ if (param.isArray()) {
+ Class<?> ctype = param.getComponentType();
+ Object arg = Array.newInstance(ctype, 2);
+ Array.set(arg, 0, randomArg(ctype));
+ return arg;
+ }
+ if (param.isInterface() && param.isAssignableFrom(List.class))
+ return Arrays.asList("#"+nextArg());
+ if (param.isInterface() || param.isAssignableFrom(String.class))
+ return "#"+nextArg();
+ else
+ try {
+ return param.newInstance();
+ } catch (InstantiationException | IllegalAccessException ex) {
+ }
+ return null; // random class not Object, String, Integer, etc.
+ }
+
+ static Object[] randomArgs(Class<?>... params) {
+ Object[] args = new Object[params.length];
+ for (int i = 0; i < args.length; i++)
+ args[i] = randomArg(params[i]);
+ return args;
+ }
+
+ static Object[] randomArgs(int nargs, Class<?> param) {
+ Object[] args = new Object[nargs];
+ for (int i = 0; i < args.length; i++)
+ args[i] = randomArg(param);
+ return args;
+ }
+
+ static Object[] randomArgs(List<Class<?>> params) {
+ return randomArgs(params.toArray(new Class<?>[params.size()]));
+ }
+
+ @SafeVarargs @SuppressWarnings("varargs")
+ static <T, E extends T> T[] array(Class<T[]> atype, E... a) {
+ return Arrays.copyOf(a, a.length, atype);
+ }
+
+ @SafeVarargs @SuppressWarnings("varargs")
+ static <T> T[] cat(T[] a, T... b) {
+ int alen = a.length, blen = b.length;
+ if (blen == 0) return a;
+ T[] c = Arrays.copyOf(a, alen + blen);
+ System.arraycopy(b, 0, c, alen, blen);
+ return c;
+ }
+
+ static Integer[] boxAll(int... vx) {
+ Integer[] res = new Integer[vx.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = vx[i];
+ }
+ return res;
+ }
+
+ static Object getClasses(Object x) {
+ if (x == null) return x;
+ if (x instanceof String) return x; // keep the name
+ if (x instanceof List) {
+ // recursively report classes of the list elements
+ Object[] xa = ((List)x).toArray();
+ for (int i = 0; i < xa.length; i++)
+ xa[i] = getClasses(xa[i]);
+ return Arrays.asList(xa);
+ }
+ return x.getClass().getSimpleName();
+ }
+
+ /** Return lambda(arg...[arity]) { new Object[]{ arg... } } */
+ static MethodHandle varargsList(int arity) {
+ return ValueConversions.varargsList(arity);
+ }
+
+ /** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
+ static MethodHandle varargsArray(int arity) {
+ return ValueConversions.varargsArray(arity);
+ }
+
+ static MethodHandle varargsArray(Class<?> arrayType, int arity) {
+ return ValueConversions.varargsArray(arrayType, arity);
+ }
+
+ /** Variation of varargsList, but with the given rtype. */
+ static MethodHandle varargsList(int arity, Class<?> rtype) {
+ MethodHandle list = varargsList(arity);
+ MethodType listType = list.type().changeReturnType(rtype);
+ if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) {
+ // OK
+ } else if (rtype.isAssignableFrom(String.class)) {
+ if (LIST_TO_STRING == null)
+ try {
+ LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString",
+ MethodType.methodType(String.class, List.class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) { throw new RuntimeException(ex); }
+ list = MethodHandles.filterReturnValue(list, LIST_TO_STRING);
+ } else if (rtype.isPrimitive()) {
+ if (LIST_TO_INT == null)
+ try {
+ LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt",
+ MethodType.methodType(int.class, List.class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) { throw new RuntimeException(ex); }
+ list = MethodHandles.filterReturnValue(list, LIST_TO_INT);
+ list = MethodHandles.explicitCastArguments(list, listType);
+ } else {
+ throw new RuntimeException("varargsList: "+rtype);
+ }
+ return list.asType(listType);
+ }
+
+ /** Variation of varargsList, but with the given ptypes and rtype. */
+ static MethodHandle varargsList(List<Class<?>> ptypes, Class<?> rtype) {
+ MethodHandle list = varargsList(ptypes.size(), rtype);
+ return list.asType(MethodType.methodType(rtype, ptypes));
+ }
+
+ private static MethodHandle LIST_TO_STRING, LIST_TO_INT;
+ private static String listToString(List<?> x) { return x.toString(); }
+ private static int listToInt(List<?> x) { return x.toString().hashCode(); }
+
+ static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
+ return changeArgTypes(target, 0, 999, argType);
+ }
+
+ static MethodHandle changeArgTypes(MethodHandle target,
+ int beg, int end, Class<?> argType) {
+ MethodType targetType = target.type();
+ end = Math.min(end, targetType.parameterCount());
+ ArrayList<Class<?>> argTypes = new ArrayList<>(targetType.parameterList());
+ Collections.fill(argTypes.subList(beg, end), argType);
+ MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
+ return target.asType(ttype2);
+ }
+
+ static MethodHandle addTrailingArgs(MethodHandle target, int nargs, Class<?> argClass) {
+ int targetLen = target.type().parameterCount();
+ int extra = (nargs - targetLen);
+ if (extra <= 0) return target;
+ List<Class<?>> fakeArgs = Collections.<Class<?>>nCopies(extra, argClass);
+ return MethodHandles.dropArguments(target, targetLen, fakeArgs);
+ }
+
+ // This lookup is good for all members in and under MethodHandlesTest.
+ static final Lookup PRIVATE = MethodHandles.lookup();
+ // This lookup is good for package-private members but not private ones.
+ static final Lookup PACKAGE = PackageSibling.lookup();
+ // This lookup is good for public members and protected members of PubExample
+ static final Lookup SUBCLASS = RemoteExample.lookup();
+ // This lookup is good only for public members in exported packages.
+ static final Lookup PUBLIC = MethodHandles.publicLookup();
+
+ // Subject methods...
+ static class Example implements IntExample {
+ final String name;
+ public Example() { name = "Example#"+nextArg(); }
+ protected Example(String name) { this.name = name; }
+ @SuppressWarnings("LeakingThisInConstructor")
+ protected Example(int x) { this(); called("protected <init>", this, x); }
+ //Example(Void x) { does not exist; lookup elicts NoSuchMethodException }
+ @Override public String toString() { return name; }
+
+ public void v0() { called("v0", this); }
+ protected void pro_v0() { called("pro_v0", this); }
+ void pkg_v0() { called("pkg_v0", this); }
+ private void pri_v0() { called("pri_v0", this); }
+ public static void s0() { called("s0"); }
+ protected static void pro_s0() { called("pro_s0"); }
+ static void pkg_s0() { called("pkg_s0"); }
+ private static void pri_s0() { called("pri_s0"); }
+
+ public Object v1(Object x) { return called("v1", this, x); }
+ public Object v2(Object x, Object y) { return called("v2", this, x, y); }
+ public Object v2(Object x, int y) { return called("v2", this, x, y); }
+ public Object v2(int x, Object y) { return called("v2", this, x, y); }
+ public Object v2(int x, int y) { return called("v2", this, x, y); }
+ public static Object s1(Object x) { return called("s1", x); }
+ public static Object s2(int x) { return called("s2", x); }
+ public static Object s3(long x) { return called("s3", x); }
+ public static Object s4(int x, int y) { return called("s4", x, y); }
+ public static Object s5(long x, int y) { return called("s5", x, y); }
+ public static Object s6(int x, long y) { return called("s6", x, y); }
+ public static Object s7(float x, double y) { return called("s7", x, y); }
+
+ // for testing findConstructor:
+ public Example(String x, int y) { this.name = x+y; called("Example.<init>", x, y); }
+ public Example(int x, String y) { this.name = x+y; called("Example.<init>", x, y); }
+ public Example(int x, int y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, long y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, float y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, double y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, int y, int z) { this.name = x+""+y+""+z; called("Example.<init>", x, y, z); }
+ public Example(int x, int y, int z, int a) { this.name = x+""+y+""+z+""+a; called("Example.<init>", x, y, z, a); }
+
+ static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial
+ }
+
+ static final Lookup EXAMPLE = Example.EXAMPLE;
+ public static class PubExample extends Example {
+ public PubExample() { this("PubExample"); }
+ protected PubExample(String prefix) { super(prefix+"#"+nextArg()); }
+ protected void pro_v0() { called("Pub/pro_v0", this); }
+ protected static void pro_s0() { called("Pub/pro_s0"); }
+ }
+
+ static class SubExample extends Example {
+ @Override public void v0() { called("Sub/v0", this); }
+ @Override void pkg_v0() { called("Sub/pkg_v0", this); }
+ @SuppressWarnings("LeakingThisInConstructor")
+ private SubExample(int x) { called("<init>", this, x); }
+ public SubExample() { super("SubExample#"+nextArg()); }
+ }
+
+ public static interface IntExample {
+ public void v0();
+ public default void vd() { called("vd", this); }
+ public static class Impl implements IntExample {
+ public void v0() { called("Int/v0", this); }
+ final String name;
+ public Impl() { name = "Impl#"+nextArg(); }
+ @Override public String toString() { return name; }
+ }
+ }
+
+ static interface SubIntExample extends IntExample { }
+
+ static final Object[][][] ACCESS_CASES = {
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
+ { { false, PUBLIC }, { false, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
+ { { false, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: subclass OK
+ { { true, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[4]: all true
+ };
+
+ static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) {
+ Object[][] cases;
+ if (name.contains("pri_") || isSpecial) {
+ cases = ACCESS_CASES[1]; // PRIVATE only
+ } else if (name.contains("pkg_") || !Modifier.isPublic(defc.getModifiers())) {
+ cases = ACCESS_CASES[2]; // not PUBLIC
+ } else if (name.contains("pro_")) {
+ cases = ACCESS_CASES[3]; // PUBLIC class, protected member
+ } else {
+ assertTrue(name.indexOf('_') < 0 || name.contains("fin_"));
+ boolean pubc = Modifier.isPublic(defc.getModifiers());
+ if (pubc)
+ cases = ACCESS_CASES[4]; // all access levels
+ else
+ cases = ACCESS_CASES[2]; // PACKAGE but not PUBLIC
+ }
+ if (defc != Example.class && cases[cases.length-1][1] == EXAMPLE)
+ cases = Arrays.copyOfRange(cases, 0, cases.length-1);
+ return cases;
+ }
+
+ static Object[][] accessCases(Class<?> defc, String name) {
+ return accessCases(defc, name, false);
+ }
+
+ static Lookup maybeMoveIn(Lookup lookup, Class<?> defc) {
+ if (lookup == PUBLIC || lookup == SUBCLASS || lookup == PACKAGE)
+ // external views stay external
+ return lookup;
+ return lookup.in(defc);
+ }
+
+ /** Is findVirtual (etc.) of "<init<" supposed to elicit a NoSuchMethodException? */
+ static final boolean INIT_REF_CAUSES_NSME = true;
+
+ static void assertExceptionClass(Class<? extends Throwable> expected,
+ Throwable actual) {
+ if (expected.isInstance(actual)) return;
+ actual.printStackTrace();
+ assertEquals(expected, actual.getClass());
+ }
+
+ static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+
+ // rough check of name string
+ static void assertNameStringContains(MethodHandle x, String s) {
+ if (!DEBUG_METHOD_HANDLE_NAMES) {
+ // ignore s
+ assertEquals("MethodHandle"+x.type(), x.toString());
+ return;
+ }
+ if (x.toString().contains(s)) return;
+ assertEquals(s, x);
+ }
+
+ public static class HasFields {
+ boolean fZ = false;
+ byte fB = (byte)'B';
+ short fS = (short)'S';
+ char fC = 'C';
+ int fI = 'I';
+ long fJ = 'J';
+ float fF = 'F';
+ double fD = 'D';
+ static boolean sZ = true;
+ static byte sB = 1+(byte)'B';
+ static short sS = 1+(short)'S';
+ static char sC = 1+'C';
+ static int sI = 1+'I';
+ static long sJ = 1+'J';
+ static float sF = 1+'F';
+ static double sD = 1+'D';
+
+ Object fL = 'L';
+ String fR = "R";
+ static Object sL = 'M';
+ static String sR = "S";
+
+ static final Object[][] CASES;
+ static {
+ ArrayList<Object[]> cases = new ArrayList<>();
+ Object types[][] = {
+ {'L',Object.class}, {'R',String.class},
+ {'I',int.class}, {'J',long.class},
+ {'F',float.class}, {'D',double.class},
+ {'Z',boolean.class}, {'B',byte.class},
+ {'S',short.class}, {'C',char.class},
+ };
+ HasFields fields = new HasFields();
+ for (Object[] t : types) {
+ for (int kind = 0; kind <= 1; kind++) {
+ boolean isStatic = (kind != 0);
+ char btc = (Character)t[0];
+ String name = (isStatic ? "s" : "f") + btc;
+ Class<?> type = (Class<?>) t[1];
+ Object value;
+ Field field;
+ try {
+ field = HasFields.class.getDeclaredField(name);
+ } catch (NoSuchFieldException | SecurityException ex) {
+ throw new InternalError("no field HasFields."+name);
+ }
+ try {
+ value = field.get(fields);
+ } catch (IllegalArgumentException | IllegalAccessException ex) {
+ throw new InternalError("cannot fetch field HasFields."+name);
+ }
+ if (type == float.class) {
+ float v = 'F';
+ if (isStatic) v++;
+ assertTrue(value.equals(v));
+ }
+ assertTrue(name.equals(field.getName()));
+ assertTrue(type.equals(field.getType()));
+ assertTrue(isStatic == (Modifier.isStatic(field.getModifiers())));
+ cases.add(new Object[]{ field, value });
+ }
+ }
+ cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
+ cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class });
+ CASES = cases.toArray(new Object[0][]);
+ }
+ }
+
+ static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10, TEST_BOUND = 0x20, TEST_NPE = 0x40;
+
+ static boolean testModeMatches(int testMode, boolean isStatic) {
+ switch (testMode) {
+ case TEST_FIND_STATIC: return isStatic;
+ case TEST_FIND_FIELD: return !isStatic;
+ case TEST_UNREFLECT: return true; // unreflect matches both
+ }
+ throw new InternalError("testMode="+testMode);
+ }
+
+ static class Callee {
+ static Object id() { return called("id"); }
+ static Object id(Object x) { return called("id", x); }
+ static Object id(Object x, Object y) { return called("id", x, y); }
+ static Object id(Object x, Object y, Object z) { return called("id", x, y, z); }
+ static Object id(Object... vx) { return called("id", vx); }
+ static MethodHandle ofType(int n) {
+ return ofType(Object.class, n);
+ }
+ static MethodHandle ofType(Class<?> rtype, int n) {
+ if (n == -1)
+ return ofType(MethodType.methodType(rtype, Object[].class));
+ return ofType(MethodType.genericMethodType(n).changeReturnType(rtype));
+ }
+ static MethodHandle ofType(Class<?> rtype, Class<?>... ptypes) {
+ return ofType(MethodType.methodType(rtype, ptypes));
+ }
+ static MethodHandle ofType(MethodType type) {
+ Class<?> rtype = type.returnType();
+ String pfx = "";
+ if (rtype != Object.class)
+ pfx = rtype.getSimpleName().substring(0, 1).toLowerCase();
+ String name = pfx+"id";
+ try {
+ return PRIVATE.findStatic(Callee.class, name, type);
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ static Object invokee(Object... args) {
+ return called("invokee", args).hashCode();
+ }
+
+ protected static final String MISSING_ARG = "missingArg";
+ protected static final String MISSING_ARG_2 = "missingArg#2";
+
+ static Object targetIfEquals() {
+ return called("targetIfEquals");
+ }
+
+ static Object fallbackIfNotEquals() {
+ return called("fallbackIfNotEquals");
+ }
+
+ static Object targetIfEquals(Object x) {
+ assertEquals(x, MISSING_ARG);
+ return called("targetIfEquals", x);
+ }
+
+ static Object fallbackIfNotEquals(Object x) {
+ assertFalse(x.toString(), x.equals(MISSING_ARG));
+ return called("fallbackIfNotEquals", x);
+ }
+
+ static Object targetIfEquals(Object x, Object y) {
+ assertEquals(x, y);
+ return called("targetIfEquals", x, y);
+ }
+
+ static Object fallbackIfNotEquals(Object x, Object y) {
+ assertFalse(x.toString(), x.equals(y));
+ return called("fallbackIfNotEquals", x, y);
+ }
+
+ static Object targetIfEquals(Object x, Object y, Object z) {
+ assertEquals(x, y);
+ return called("targetIfEquals", x, y, z);
+ }
+
+ static Object fallbackIfNotEquals(Object x, Object y, Object z) {
+ assertFalse(x.toString(), x.equals(y));
+ return called("fallbackIfNotEquals", x, y, z);
+ }
+
+ static boolean loopIntPred(int a) {
+ if (verbosity >= 5) {
+ System.out.println("int pred " + a + " -> " + (a < 7));
+ }
+ return a < 7;
+ }
+
+ static boolean loopDoublePred(int a, double b) {
+ if (verbosity >= 5) {
+ System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5));
+ }
+ return b > 0.5;
+ }
+
+ static boolean loopStringPred(int a, double b, String c) {
+ if (verbosity >= 5) {
+ System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9));
+ }
+ return c.length() <= 9;
+ }
+
+ static int loopIntStep(int a) {
+ if (verbosity >= 5) {
+ System.out.println("int step " + a + " -> " + (a + 1));
+ }
+ return a + 1;
+ }
+
+ static double loopDoubleStep(int a, double b) {
+ if (verbosity >= 5) {
+ System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0));
+ }
+ return b / 2.0;
+ }
+
+ static String loopStringStep(int a, double b, String c) {
+ if (verbosity >= 5) {
+ System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a));
+ }
+ return c + a;
+ }
+
+ static void vtarget(String[] a) {
+ // naught, akin to identity
+ }
+
+ static void vtargetThrow(String[] a) throws Exception {
+ throw new Exception("thrown");
+ }
+
+ static void vcleanupPassThrough(Throwable t, String[] a) {
+ assertNull(t);
+ // naught, akin to identity
+ }
+
+ static void vcleanupAugment(Throwable t, String[] a) {
+ assertNull(t);
+ a[0] = "augmented";
+ }
+
+ static void vcleanupCatch(Throwable t, String[] a) {
+ assertNotNull(t);
+ a[0] = "caught";
+ }
+
+ static void vcleanupThrow(Throwable t, String[] a) throws Exception {
+ assertNotNull(t);
+ throw new Exception("rethrown");
+ }
+}
+// Local abbreviated copy of sun.invoke.util.ValueConversions
+// This guy tests access from outside the same package member, but inside
+// the package itself.
+class ValueConversions {
+ private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
+ private static final Object[] NO_ARGS_ARRAY = {};
+ private static Object[] makeArray(Object... args) { return args; }
+ private static Object[] array() { return NO_ARGS_ARRAY; }
+ private static Object[] array(Object a0)
+ { return makeArray(a0); }
+ private static Object[] array(Object a0, Object a1)
+ { return makeArray(a0, a1); }
+ private static Object[] array(Object a0, Object a1, Object a2)
+ { return makeArray(a0, a1, a2); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3)
+ { return makeArray(a0, a1, a2, a3); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4)
+ { return makeArray(a0, a1, a2, a3, a4); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5)
+ { return makeArray(a0, a1, a2, a3, a4, a5); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6)
+ { return makeArray(a0, a1, a2, a3, a4, a5, a6); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7)
+ { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8)
+ { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ private static Object[] array(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9)
+ { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+
+ static MethodHandle[] makeArrays() {
+ ArrayList<MethodHandle> arrays = new ArrayList<>();
+ MethodHandles.Lookup lookup = IMPL_LOOKUP;
+ for (;;) {
+ int nargs = arrays.size();
+ MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
+ String name = "array";
+ MethodHandle array = null;
+ try {
+ array = lookup.findStatic(ValueConversions.class, name, type);
+ } catch (ReflectiveOperationException ex) {
+ // break from loop!
+ }
+ if (array == null) break;
+ arrays.add(array);
+ }
+ assertTrue(arrays.size() == 11); // current number of methods
+ return arrays.toArray(new MethodHandle[0]);
+ }
+
+ static final MethodHandle[] ARRAYS = makeArrays();
+
+ /** Return a method handle that takes the indicated number of Object
+ * arguments and returns an Object array of them, as if for varargs.
+ */
+ public static MethodHandle varargsArray(int nargs) {
+ if (nargs < ARRAYS.length)
+ return ARRAYS[nargs];
+ return MethodHandles.identity(Object[].class).asCollector(Object[].class, nargs);
+ }
+
+ public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
+ Class<?> elemType = arrayType.getComponentType();
+ MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
+ MethodHandle mh = varargsArray(nargs);
+ if (arrayType != Object[].class)
+ mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
+ return mh.asType(vaType);
+ }
+
+ static Object changeArrayType(Class<?> arrayType, Object[] a) {
+ Class<?> elemType = arrayType.getComponentType();
+ if (!elemType.isPrimitive())
+ return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class));
+ Object b = java.lang.reflect.Array.newInstance(elemType, a.length);
+ for (int i = 0; i < a.length; i++)
+ java.lang.reflect.Array.set(b, i, a[i]);
+ return b;
+ }
+
+ private static final MethodHandle CHANGE_ARRAY_TYPE;
+ static {
+ try {
+ CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType",
+ MethodType.methodType(Object.class, Class.class, Object[].class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ Error err = new InternalError("uncaught exception");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+
+ private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
+ private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
+ private static List<Object> list() { return NO_ARGS_LIST; }
+ private static List<Object> list(Object a0)
+ { return makeList(a0); }
+ private static List<Object> list(Object a0, Object a1)
+ { return makeList(a0, a1); }
+ private static List<Object> list(Object a0, Object a1, Object a2)
+ { return makeList(a0, a1, a2); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3)
+ { return makeList(a0, a1, a2, a3); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4)
+ { return makeList(a0, a1, a2, a3, a4); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5)
+ { return makeList(a0, a1, a2, a3, a4, a5); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6)
+ { return makeList(a0, a1, a2, a3, a4, a5, a6); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7)
+ { return makeList(a0, a1, a2, a3, a4, a5, a6, a7); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8)
+ { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
+ private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9)
+ { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+
+ static MethodHandle[] makeLists() {
+ ArrayList<MethodHandle> lists = new ArrayList<>();
+ MethodHandles.Lookup lookup = IMPL_LOOKUP;
+ for (;;) {
+ int nargs = lists.size();
+ MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
+ String name = "list";
+ MethodHandle list = null;
+ try {
+ list = lookup.findStatic(ValueConversions.class, name, type);
+ } catch (ReflectiveOperationException ex) {
+ // break from loop!
+ }
+ if (list == null) break;
+ lists.add(list);
+ }
+ assertTrue(lists.size() == 11); // current number of methods
+ return lists.toArray(new MethodHandle[0]);
+ }
+
+ static final MethodHandle[] LISTS = makeLists();
+ static final MethodHandle AS_LIST;
+
+ static {
+ try {
+ AS_LIST = IMPL_LOOKUP.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) { throw new RuntimeException(ex); }
+ }
+
+ /** Return a method handle that takes the indicated number of Object
+ * arguments and returns List.
+ */
+ public static MethodHandle varargsList(int nargs) {
+ if (nargs < LISTS.length)
+ return LISTS[nargs];
+ return AS_LIST.asCollector(Object[].class, nargs);
+ }
+}
+// This guy tests access from outside the same package member, but inside
+// the package itself.
+class PackageSibling {
+ static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
diff --git a/ojluni/src/test/java/lang/invoke/MethodTypeTest.java b/ojluni/src/test/java/lang/invoke/MethodTypeTest.java
new file mode 100644
index 0000000..c2ae4e0
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/MethodTypeTest.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for java.lang.invoke.MethodType
+ * @compile MethodTypeTest.java
+ * @run testng/othervm test.java.lang.invoke.MethodTypeTest
+ */
+
+package test.java.lang.invoke;
+
+import java.io.IOException;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+
+import java.util.*;
+import org.testng.*;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.*;
+
+/**
+ *
+ * @author jrose
+ */
+public class MethodTypeTest {
+
+ private Class<?> rtype;
+ private Class<?>[] ptypes;
+ private MethodType mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov;
+ private MethodType mt_iSI, mt_ISi, mt_ISI, mt_iSi;
+ private MethodType mt_viO, mt_iO2, mt_OOi, mt_iOi;
+ private MethodType mt_VIO, mt_IO2, mt_OOI, mt_IOI, mt_VIS;
+ private MethodType mt_vOiSzA, mt_OO99;
+ private MethodType[] GALLERY;
+ private Method compareTo;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ rtype = void.class;
+ ptypes = new Class<?>[] { int.class, String.class };
+
+ mt_viS = MethodType.methodType(void.class, int.class, String.class);
+ mt_OO = MethodType.methodType(Object.class, Object.class);
+ mt_OO2 = MethodType.methodType(Object.class, Object.class, Object.class);
+ mt_vv = MethodType.methodType(void.class);
+ mt_Vv = MethodType.methodType(Void.class);
+ mt_Ov = MethodType.methodType(Object.class);
+ mt_iSI = MethodType.methodType(int.class, String.class, Integer.class);
+ mt_ISi = MethodType.methodType(Integer.class, String.class, int.class);
+ mt_ISI = MethodType.methodType(Integer.class, String.class, Integer.class);
+ mt_iSi = MethodType.methodType(int.class, String.class, int.class);
+
+ compareTo = String.class.getDeclaredMethod("compareTo", String.class);
+
+ mt_viO = MethodType.methodType(void.class, int.class, Object.class);
+ mt_iO2 = MethodType.methodType(int.class, Object.class, Object.class);
+ mt_OOi = MethodType.methodType(Object.class, Object.class, int.class);
+ mt_iOi = MethodType.methodType(int.class, Object.class, int.class);
+
+ mt_VIO = MethodType.methodType(Void.class, Integer.class, Object.class);
+ mt_IO2 = MethodType.methodType(Integer.class, Object.class, Object.class);
+ mt_OOI = MethodType.methodType(Object.class, Object.class, Integer.class);
+ mt_IOI = MethodType.methodType(Integer.class, Object.class, Integer.class);
+ mt_VIS = MethodType.methodType(Void.class, Integer.class, String.class);
+
+ mt_vOiSzA = MethodType.methodType(void.class, Object.class, int.class, String.class, boolean.class, Object[].class);
+ mt_OO99 = MethodType.genericMethodType(99);
+
+ GALLERY = new MethodType[] {
+ mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov,
+ mt_iSI, mt_ISi, mt_ISI, mt_iSi,
+ mt_viO, mt_iO2, mt_OOi, mt_iOi,
+ mt_VIO, mt_IO2, mt_OOI, mt_IOI,
+ mt_VIS, mt_vOiSzA, mt_OO99
+ };
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ }
+
+ /** Make sure the method types are all distinct. */
+ @Test
+ public void testDistinct() {
+ List<MethodType> gallery2 = new ArrayList<>();
+ for (MethodType mt : GALLERY) {
+ assertFalse(mt.toString(), gallery2.contains(mt));
+ gallery2.add(mt);
+ }
+ // check self-equality also:
+ assertEquals(Arrays.asList(GALLERY), gallery2);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class_ClassArr() {
+ System.out.println("make (from type array)");
+ MethodType result = MethodType.methodType(rtype, ptypes);
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class_List() {
+ System.out.println("make (from type list)");
+ MethodType result = MethodType.methodType(rtype, Arrays.asList(ptypes));
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_3args() {
+ System.out.println("make (from type with varargs)");
+ MethodType result = MethodType.methodType(rtype, ptypes[0], ptypes[1]);
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class() {
+ System.out.println("make (from single type)");
+ Class<?> rt = Integer.class;
+ MethodType expResult = MethodType.methodType(rt, new Class<?>[0]);
+ MethodType result = MethodType.methodType(rt);
+ assertSame(expResult, result);
+ }
+
+ @Test
+ public void testMakeGeneric() {
+ System.out.println("makeGeneric");
+ int objectArgCount = 2;
+ MethodType expResult = mt_OO2;
+ MethodType result = MethodType.genericMethodType(objectArgCount);
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_MethodType() {
+ System.out.println("make (from rtype, MethodType)");
+ MethodType expResult = mt_iO2;
+ MethodType result = MethodType.methodType(int.class, mt_IO2);
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_String_ClassLoader() {
+ System.out.println("make (from bytecode signature)");
+ ClassLoader loader = null;
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ String obj = "Ljava/lang/Object;";
+ assertEquals(obj, concat(Object.class));
+ String[] expResults = {
+ "(ILjava/lang/String;)V",
+ concat("(", obj, 2, ")", Object.class),
+ "()V", "()"+obj,
+ concat("(", String.class, Integer.class, ")I"),
+ concat("(", String.class, "I)", Integer.class),
+ concat("(", String.class, Integer.class, ")", Integer.class),
+ concat("(", String.class, "I)I")
+ };
+ for (int i = 0; i < instances.length; i++) {
+ MethodType instance = instances[i];
+ String result = instance.toMethodDescriptorString();
+ assertEquals("#"+i, expResults[i], result);
+ MethodType parsed = MethodType.fromMethodDescriptorString(result, loader);
+ assertSame("--#"+i, instance, parsed);
+ }
+ }
+ private static String concat(Object... parts) {
+ StringBuilder sb = new StringBuilder();
+ Object prevPart = "";
+ for (Object part : parts) {
+ if (part instanceof Class) {
+ part = "L"+((Class)part).getName()+";";
+ }
+ if (part instanceof Integer) {
+ for (int n = (Integer) part; n > 1; n--)
+ sb.append(prevPart);
+ part = "";
+ }
+ sb.append(part);
+ prevPart = part;
+ }
+ return sb.toString().replace('.', '/');
+ }
+
+ @Test
+ public void testHasPrimitives() {
+ System.out.println("hasPrimitives");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ boolean[] expResults = {true, false, true, false, true, true, false, true};
+ for (int i = 0; i < instances.length; i++) {
+ boolean result = instances[i].hasPrimitives();
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testHasWrappers() {
+ System.out.println("hasWrappers");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ boolean[] expResults = {false, false, false, false, true, true, true, false};
+ for (int i = 0; i < instances.length; i++) {
+ System.out.println(" hasWrappers "+instances[i]);
+ boolean result = instances[i].hasWrappers();
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testErase() {
+ System.out.println("erase");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_viO, mt_OO2, mt_vv, mt_Ov, mt_iO2, mt_OOi, mt_OO2, mt_iOi};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].erase();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testGeneric() {
+ System.out.println("generic");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_OO2, mt_OO2, mt_Ov, mt_Ov, mt_OO2, mt_OO2, mt_OO2, mt_OO2};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].generic();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testWrap() {
+ System.out.println("wrap");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_VIS, mt_OO2, mt_Vv, mt_Ov, mt_ISI, mt_ISI, mt_ISI, mt_ISI};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].wrap();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testUnwrap() {
+ System.out.println("unwrap");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSi, mt_iSi, mt_iSi, mt_iSi};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].unwrap();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ /**
+ * Test of parameterType method, of class MethodType.
+ */
+ @Test
+ public void testParameterType() {
+ System.out.println("parameterType");
+ for (int num = 0; num < ptypes.length; num++) {
+ MethodType instance = mt_viS;
+ Class<?> expResult = ptypes[num];
+ Class<?> result = instance.parameterType(num);
+ assertSame(expResult, result);
+ }
+ }
+
+ /**
+ * Test of parameterCount method, of class MethodType.
+ */
+ @Test
+ public void testParameterCount() {
+ System.out.println("parameterCount");
+ MethodType instance = mt_viS;
+ int expResult = 2;
+ int result = instance.parameterCount();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of returnType method, of class MethodType.
+ */
+ @Test
+ public void testReturnType() {
+ System.out.println("returnType");
+ MethodType instance = mt_viS;
+ Class<?> expResult = void.class;
+ Class<?> result = instance.returnType();
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of parameterList method, of class MethodType.
+ */
+ @Test
+ public void testParameterList() {
+ System.out.println("parameterList");
+ MethodType instance = mt_viS;
+ List<Class<?>> expResult = Arrays.asList(ptypes);
+ List<Class<?>> result = instance.parameterList();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of parameterArray method, of class MethodType.
+ */
+ @Test
+ public void testParameterArray() {
+ System.out.println("parameterArray");
+ MethodType instance = mt_viS;
+ Class<?>[] expResult = ptypes;
+ Class<?>[] result = instance.parameterArray();
+ assertEquals(Arrays.asList(expResult), Arrays.asList(result));
+ }
+
+ /**
+ * Test of equals method, of class MethodType.
+ */
+ @Test
+ public void testEquals_Object() {
+ System.out.println("equals");
+ Object x = null;
+ MethodType instance = mt_viS;
+ boolean expResult = false;
+ boolean result = instance.equals(x);
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of equals method, of class MethodType.
+ */
+ @Test
+ public void testEquals_MethodType() {
+ System.out.println("equals");
+ MethodType that = mt_viS;
+ MethodType instance = mt_viS;
+ boolean expResult = true;
+ boolean result = instance.equals(that);
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of hashCode method, of class MethodType.
+ */
+ @Test
+ public void testHashCode() {
+ System.out.println("hashCode");
+ MethodType instance = mt_viS;
+ ArrayList<Class<?>> types = new ArrayList<>();
+ types.add(instance.returnType());
+ types.addAll(instance.parameterList());
+ int expResult = types.hashCode();
+ int result = instance.hashCode();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of toString method, of class MethodType.
+ */
+ @Test
+ public void testToString() {
+ System.out.println("toString");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ //String expResult = "void[int, class java.lang.String]";
+ String[] expResults = {
+ "(int,String)void",
+ "(Object,Object)Object",
+ "()void",
+ "()Object",
+ "(String,Integer)int",
+ "(String,int)Integer",
+ "(String,Integer)Integer",
+ "(String,int)int"
+ };
+ for (int i = 0; i < instances.length; i++) {
+ MethodType instance = instances[i];
+ String result = instance.toString();
+ System.out.println("#"+i+":"+result);
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ private static byte[] writeSerial(Object x) throws java.io.IOException {
+ try (java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(bout)
+ ) {
+ out.writeObject(x);
+ out.flush();
+ return bout.toByteArray();
+ }
+ }
+ private static Object readSerial(byte[] wire) throws java.io.IOException, ClassNotFoundException {
+ try (java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(wire);
+ java.io.ObjectInputStream in = new java.io.ObjectInputStream(bin)) {
+ return in.readObject();
+ }
+ }
+ private static void testSerializedEquality(Object x) throws java.io.IOException, ClassNotFoundException {
+ if (x instanceof Object[])
+ x = Arrays.asList((Object[]) x); // has proper equals method
+ byte[] wire = writeSerial(x);
+ Object y = readSerial(wire);
+ assertEquals(x, y);
+ }
+
+ /** Test (de-)serialization. */
+ @Test
+ public void testSerialization() throws Throwable {
+ System.out.println("serialization");
+ for (MethodType mt : GALLERY) {
+ testSerializedEquality(mt);
+ }
+ testSerializedEquality(GALLERY);
+
+ // Make a list of mixed objects:
+ List<Object> stuff = new ArrayList<>();
+ Collections.addAll(stuff, GALLERY); // copy #1
+ Object[] triples = Arrays.copyOfRange(GALLERY, 0, GALLERY.length/2);
+ Collections.addAll(stuff, triples); // copy #3 (partial)
+ for (MethodType mt : GALLERY) {
+ Collections.addAll(stuff, mt.parameterArray());
+ }
+ Collections.shuffle(stuff, new Random(292));
+ Collections.addAll(stuff, GALLERY); // copy #2
+ testSerializedEquality(stuff);
+ }
+
+ /** Test serialization formats. */
+ @Test
+ public void testPortableSerialFormat() throws Throwable {
+ System.out.println("portable serial format");
+ boolean generateData = false;
+ //generateData = true; // set this true to generate the following input data:
+ Object[][] cases = {
+ { mt_vv, new byte[] { // ()void
+ (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x1b,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e,
+ (byte)0x67, (byte)0x2e, (byte)0x69, (byte)0x6e, (byte)0x76, (byte)0x6f, (byte)0x6b, (byte)0x65,
+ (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
+ (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x6f, (byte)0x69, (byte)0x64,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00,
+ (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e,
+ (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61,
+ (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb,
+ (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
+ } },
+ { mt_OO, new byte[] { // (Object)Object
+ (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x1b,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e,
+ (byte)0x67, (byte)0x2e, (byte)0x69, (byte)0x6e, (byte)0x76, (byte)0x6f, (byte)0x6b, (byte)0x65,
+ (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
+ (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61,
+ (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62,
+ (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
+ (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a,
+ (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67,
+ (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61, (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab,
+ (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb, (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02,
+ (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+ (byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x03, (byte)0x78,
+ } },
+ { mt_vOiSzA, new byte[] { // (Object,int,String,boolean,Object[])void
+ (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x1b,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e,
+ (byte)0x67, (byte)0x2e, (byte)0x69, (byte)0x6e, (byte)0x76, (byte)0x6f, (byte)0x6b, (byte)0x65,
+ (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
+ (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x6f, (byte)0x69, (byte)0x64,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00,
+ (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e,
+ (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61,
+ (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb,
+ (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e,
+ (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62, (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x76, (byte)0x72, (byte)0x00,
+ (byte)0x03, (byte)0x69, (byte)0x6e, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
+ (byte)0x70, (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76,
+ (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x53,
+ (byte)0x74, (byte)0x72, (byte)0x69, (byte)0x6e, (byte)0x67, (byte)0xa0, (byte)0xf0, (byte)0xa4,
+ (byte)0x38, (byte)0x7a, (byte)0x3b, (byte)0xb3, (byte)0x42, (byte)0x02, (byte)0x00, (byte)0x00,
+ (byte)0x78, (byte)0x70, (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x07, (byte)0x62, (byte)0x6f,
+ (byte)0x6f, (byte)0x6c, (byte)0x65, (byte)0x61, (byte)0x6e, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x78, (byte)0x70, (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x13, (byte)0x5b, (byte)0x4c,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e,
+ (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62, (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74,
+ (byte)0x3b, (byte)0x90, (byte)0xce, (byte)0x58, (byte)0x9f, (byte)0x10, (byte)0x73, (byte)0x29,
+ (byte)0x6c, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x78,
+ } },
+ };
+ for (Object[] c : cases) {
+ MethodType mt = (MethodType) c[0];
+ System.out.println("deserialize "+mt);
+ byte[] wire = (byte[]) c[1];
+ if (generateData) {
+ System.out.println("<generateData>");
+ wire = writeSerial(mt);
+ final String INDENT = " ";
+ System.out.print("{ // "+mt);
+ for (int i = 0; i < wire.length; i++) {
+ if (i % 8 == 0) { System.out.println(); System.out.print(INDENT+" "); }
+ String hex = Integer.toHexString(wire[i] & 0xFF);
+ if (hex.length() == 1) hex = "0"+hex;
+ System.out.print(" (byte)0x"+hex+",");
+ }
+ System.out.println();
+ System.out.println(INDENT+"}");
+ System.out.println("</generateData>");
+ System.out.flush();
+ }
+ Object decode;
+ try {
+ decode = readSerial(wire);
+ } catch (IOException | ClassNotFoundException ex) {
+ decode = ex; // oops!
+ }
+ assertEquals(mt, decode);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/lang/invoke/RicochetTest.java b/ojluni/src/test/java/lang/invoke/RicochetTest.java
new file mode 100644
index 0000000..943f17e
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/RicochetTest.java
@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for recursive method handles
+ * @run junit/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -DRicochetTest.MAX_ARITY=10 test.java.lang.invoke.RicochetTest
+ */
+/*
+ * @ignore The following test creates an unreasonable number of adapters in -Xcomp mode (7049122)
+ * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import java.util.*;
+import org.junit.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandles.*;
+import static org.junit.Assert.*;
+
+
+/**
+ *
+ * @author jrose
+ */
+public class RicochetTest {
+ private static final Class<?> CLASS = RicochetTest.class;
+ private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
+
+ public static void main(String... av) throws Throwable {
+ RicochetTest test = new RicochetTest();
+ if (av.length > 0) test.testOnly = Arrays.asList(av).toString();
+ if (REPEAT == 1 || test.testOnly != null) {
+ test.testAll();
+ if (test.testOnlyTests == null) throw new RuntimeException("no matching test: "+test.testOnly);
+ } else if (REPEAT == 0) {
+ org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
+ } else {
+ verbose(1, "REPEAT="+REPEAT);
+ for (int i = 0; i < REPEAT; i++) {
+ test.testRepetition = (i+1);
+ verbose(0, "[#"+test.testRepetition+"]");
+ test.testAll();
+ }
+ }
+ }
+ int testRepetition;
+
+ public void testAll() throws Throwable {
+ testNull();
+ testBoxInteger();
+ testFilterReturnValue();
+ testFilterObject();
+ testBoxLong();
+ testFilterInteger();
+ testIntSpreads();
+ testByteSpreads();
+ testLongSpreads();
+ testIntCollects();
+ testReturns();
+ testRecursion();
+ }
+
+ @Test
+ public void testNull() throws Throwable {
+ if (testRepetition > (1+REPEAT/100)) return; // trivial test
+ if (!startTest("testNull")) return;
+ assertEquals(opI(37), opI.invokeWithArguments(37));
+ assertEqualFunction(opI, opI);
+ }
+
+ @Test
+ public void testBoxInteger() throws Throwable {
+ if (!startTest("testBoxInteger")) return;
+ assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
+ }
+
+ @Test
+ public void testFilterReturnValue() throws Throwable {
+ if (!startTest("testFilterReturnValue")) return;
+ int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
+ Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+ assertEquals(Arrays.toString(ints), res.toString());
+ MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
+ res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+ assertEquals(Arrays.toString(ints), res.toString());
+ MethodHandle add0 = addL.bindTo(0);
+ assertEqualFunction(filterReturnValue(opL2, add0), opL2);
+ }
+
+ @Test
+ public void testFilterObject() throws Throwable {
+ if (!startTest("testFilterObject")) return;
+ MethodHandle add0 = addL.bindTo(0);
+ assertEqualFunction(sequence(opL2, add0), opL2);
+ int bump13 = -13; // value near 20 works as long as test values are near [-80..80]
+ MethodHandle add13 = addL.bindTo(bump13);
+ MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
+ MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
+ assertEqualFunction(sequence(opL2, add13_0),
+ filterArguments(opL2, 0, add13));
+ assertEqualFunction(sequence(opL2, add13_1),
+ filterArguments(opL2, 1, add13));
+ System.out.println("[testFilterObject done]");
+ }
+
+ @Test
+ public void testBoxLong() throws Throwable {
+ if (!startTest("testBoxLong")) return;
+ assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
+ }
+
+ @Test
+ public void testFilterInteger() throws Throwable {
+ if (!startTest("testFilterInteger")) return;
+ assertEqualFunction(opI, sequence(convI_L, opL_I));
+ }
+
+ @Test
+ public void testIntSpreads() throws Throwable {
+ if (!startTest("testIntSpreads")) return;
+ MethodHandle id = identity(int[].class);
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ int[] args = new int[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = j + 11;
+ //System.out.println("testIntSpreads "+Arrays.toString(args));
+ int[] args1 = (int[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(int[].class, nargs);
+ int[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (int[]) coll.invokeExact(); break;
+ case 1: args2 = (int[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(int[].class, nargs);
+ int[] args3 = (int[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testByteSpreads() throws Throwable {
+ if (!startTest("testByteSpreads")) return;
+ MethodHandle id = identity(byte[].class);
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ byte[] args = new byte[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = (byte)(j + 11);
+ //System.out.println("testByteSpreads "+Arrays.toString(args));
+ byte[] args1 = (byte[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(byte[].class, nargs);
+ byte[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (byte[]) coll.invokeExact(); break;
+ case 1: args2 = (byte[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(byte[].class, nargs);
+ byte[] args3 = (byte[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testLongSpreads() throws Throwable {
+ if (!startTest("testLongSpreads")) return;
+ MethodHandle id = identity(long[].class);
+ final int MAX = (MAX_ARITY - 2) / 2; // 253/2+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ long[] args = new long[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = (long)(j + 11);
+ //System.out.println("testLongSpreads "+Arrays.toString(args));
+ long[] args1 = (long[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(long[].class, nargs);
+ long[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (long[]) coll.invokeExact(); break;
+ case 1: args2 = (long[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(long[].class, nargs);
+ long[] args3 = (long[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testIntCollects() throws Throwable {
+ if (!startTest("testIntCollects")) return;
+ for (MethodHandle lister : INT_LISTERS) {
+ int outputs = lister.type().parameterCount();
+ for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
+ int inputs = outputs - 1 + collects;
+ if (inputs < 0) continue;
+ for (int pos = 0; pos + collects <= inputs; pos++) {
+ MethodHandle collector = INT_COLLECTORS[collects];
+ int[] args = new int[inputs];
+ int ap = 0, arg = 31;
+ for (int i = 0; i < pos; i++)
+ args[ap++] = arg++ + 0;
+ for (int i = 0; i < collects; i++)
+ args[ap++] = arg++ + 10;
+ while (ap < args.length)
+ args[ap++] = arg++ + 20;
+ // calculate piecemeal:
+ //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
+ int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
+ int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
+ int[] listargs = Arrays.copyOfRange(args, 0, outputs);
+ System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
+ listargs[pos] = coll;
+ //System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
+ Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
+ //System.out.println(" expect="+expect);
+
+ // now use the combined MH, and test the output:
+ MethodHandle mh = collectArguments(lister, pos, int[].class, INT_COLLECTORS[collects]);
+ if (mh == null) continue; // no infix collection, yet
+ assert(mh.type().parameterCount() == inputs);
+ Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
+ assertEquals(expect, observe);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testByteCollects() throws Throwable {
+ if (!startTest("testByteCollects")) return;
+ for (MethodHandle lister : BYTE_LISTERS) {
+ int outputs = lister.type().parameterCount();
+ for (int collects = 0; collects <= Math.min(outputs, BYTE_COLLECTORS.length-1); collects++) {
+ int inputs = outputs - 1 + collects;
+ if (inputs < 0) continue;
+ for (int pos = 0; pos + collects <= inputs; pos++) {
+ MethodHandle collector = BYTE_COLLECTORS[collects];
+ byte[] args = new byte[inputs];
+ int ap = 0, arg = 31;
+ for (int i = 0; i < pos; i++)
+ args[ap++] = (byte)(arg++ + 0);
+ for (int i = 0; i < collects; i++)
+ args[ap++] = (byte)(arg++ + 10);
+ while (ap < args.length)
+ args[ap++] = (byte)(arg++ + 20);
+ // calculate piecemeal:
+ //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
+ byte[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
+ byte coll = (byte) collector.asSpreader(byte[].class, collargs.length).invokeExact(collargs);
+ byte[] listargs = Arrays.copyOfRange(args, 0, outputs);
+ System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
+ listargs[pos] = coll;
+ //System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
+ Object expect = lister.asSpreader(byte[].class, listargs.length).invokeExact(listargs);
+ //System.out.println(" expect="+expect);
+
+ // now use the combined MH, and test the output:
+ MethodHandle mh = collectArguments(lister, pos, byte[].class, BYTE_COLLECTORS[collects]);
+ if (mh == null) continue; // no infix collection, yet
+ assert(mh.type().parameterCount() == inputs);
+ Object observe = mh.asSpreader(byte[].class, args.length).invokeExact(args);
+ assertEquals(expect, observe);
+ }
+ }
+ }
+ }
+
+ private static MethodHandle collectArguments(MethodHandle lister, int pos, Class<?> array, MethodHandle collector) {
+ int collects = collector.type().parameterCount();
+ int outputs = lister.type().parameterCount();
+ if (pos == outputs - 1)
+ return MethodHandles.filterArguments(lister, pos,
+ collector.asSpreader(array, collects))
+ .asCollector(array, collects);
+ //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
+ return null;
+ }
+
+ private static final Class<?>[] RETURN_TYPES = {
+ Object.class, String.class, Integer.class,
+ int.class, long.class,
+ boolean.class, byte.class, char.class, short.class,
+ float.class, double.class,
+ void.class,
+ };
+
+ @Test
+ public void testReturns() throws Throwable {
+ if (!startTest("testReturns")) return;
+ // fault injection:
+ int faultCount = 0; // total of 1296 tests
+ faultCount = Integer.getInteger("testReturns.faultCount", 0);
+ for (Class<?> ret : RETURN_TYPES) {
+ // make a complicated identity function and pass something through it
+ System.out.println(ret.getSimpleName());
+ Class<?> vret = (ret == void.class) ? Void.class : ret;
+ MethodHandle id = // (vret)->ret
+ identity(vret).asType(methodType(ret, vret));
+ final int LENGTH = 4;
+ int[] index = {0};
+ Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
+ MethodHandle indexGetter = //()->int
+ insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
+ MethodHandle valSelector = // (int)->vret
+ arrayElementGetter(vals.getClass()).bindTo(vals);
+ MethodHandle valGetter = // ()->vret
+ foldArguments(valSelector, indexGetter);
+ if (ret != void.class) {
+ for (int i = 0; i < LENGTH; i++) {
+ Object val = (i + 50);
+ if (ret == boolean.class) val = (i % 3 == 0);
+ if (ret == String.class) val = "#"+i;
+ if (ret == char.class) val = (char)('a'+i);
+ if (ret == byte.class) val = (byte)~i;
+ if (ret == short.class) val = (short)(1<<i);
+ java.lang.reflect.Array.set(vals, i, val);
+ }
+ }
+ for (int i = 0; i < LENGTH; i++) {
+ Object val = java.lang.reflect.Array.get(vals, i);
+ System.out.println(i+" => "+val);
+ index[0] = i;
+ if (--faultCount == 0) index[0] ^= 1;
+ Object x = valGetter.invokeWithArguments();
+ assertEquals(val, x);
+ // make a return-filter call: x = id(valGetter())
+ if (--faultCount == 0) index[0] ^= 1;
+ x = filterReturnValue(valGetter, id).invokeWithArguments();
+ assertEquals(val, x);
+ // make a filter call: x = id(*,valGetter(),*)
+ for (int len = 1; len <= 4; len++) {
+ for (int pos = 0; pos < len; pos++) {
+ MethodHandle proj = id; // lambda(..., vret x,...){x}
+ for (int j = 0; j < len; j++) {
+ if (j == pos) continue;
+ proj = dropArguments(proj, j, Object.class);
+ }
+ assert(proj.type().parameterCount() == len);
+ // proj: (Object*, pos: vret, Object*)->ret
+ assertEquals(vret, proj.type().parameterType(pos));
+ MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
+ if (--faultCount == 0) index[0] ^= 1;
+ x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
+ assertEquals(val, x);
+ }
+ }
+ // make a fold call:
+ for (int len = 0; len <= 4; len++) {
+ for (int fold = 0; fold <= len; fold++) {
+ MethodHandle proj = id; // lambda(ret x, ...){x}
+ if (ret == void.class) proj = constant(Object.class, null);
+ int arg0 = (ret == void.class ? 0 : 1);
+ for (int j = 0; j < len; j++) {
+ proj = dropArguments(proj, arg0, Object.class);
+ }
+ assert(proj.type().parameterCount() == arg0 + len);
+ // proj: (Object*, pos: vret, Object*)->ret
+ if (arg0 != 0) assertEquals(vret, proj.type().parameterType(0));
+ MethodHandle vgFilter = valGetter.asType(methodType(ret));
+ for (int j = 0; j < fold; j++) {
+ vgFilter = dropArguments(vgFilter, j, Object.class);
+ }
+ x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
+ if (--faultCount == 0) index[0] ^= 1;
+ assertEquals(val, x);
+ }
+ }
+ }
+ }
+ //System.out.println("faultCount="+faultCount);
+ }
+
+ @Test
+ public void testRecursion() throws Throwable {
+ if (!startTest("testRecursion")) return;
+ final int LIMIT = 10;
+ for (int i = 0; i < LIMIT; i++) {
+ RFCB rfcb = new RFCB(i);
+ Object x = "x", y = "y";
+ Object result = rfcb.recursiveFunction(x, y);
+ verbose(1, result);
+ }
+ }
+ /** Recursive Function Control Block */
+ private static class RFCB {
+ java.util.Random random;
+ final MethodHandle[] fns;
+ int depth;
+ @SuppressWarnings("LeakingThisInConstructor")
+ RFCB(int seed) throws Throwable {
+ this.random = new java.util.Random(seed);
+ this.fns = new MethodHandle[Math.max(29, (1 << MAX_DEPTH-2)/3)];
+ java.util.Arrays.fill(fns, lookup().bind(this, "recursiveFunction", genericMethodType(2)));
+ for (int i = 5; i < fns.length; i++) {
+ switch (i % 4) {
+ case 0: fns[i] = filterArguments(fns[i - 5], 0, insertArguments(fns[i - 4], 1, ".")); break;
+ case 1: fns[i] = filterArguments(fns[i - 5], 1, insertArguments(fns[i - 3], 1, ".")); break;
+ case 2: fns[i] = filterReturnValue(fns[i - 5], insertArguments(fns[i - 2], 1, ".")); break;
+ }
+ }
+ }
+ Object recursiveFunction(Object x, Object y) throws Throwable {
+ depth++;
+ try {
+ final int ACTION_COUNT = 11;
+ switch (random.nextInt(ACTION_COUNT)) {
+ case 1:
+ Throwable ex = new RuntimeException();
+ ex.fillInStackTrace();
+ if (VERBOSITY >= 2) ex.printStackTrace(System.out);
+ x = "ST; " + x;
+ break;
+ case 2:
+ System.gc();
+ x = "GC; " + x;
+ break;
+ }
+ boolean isLeaf = (depth >= MAX_DEPTH);
+ if (isLeaf) {
+ return Arrays.asList(x, y).toString();
+ }
+ return fns[random.nextInt(fns.length)].invokeExact(x, y);
+ } finally {
+ depth--;
+ }
+ }
+ }
+
+ private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
+ MethodHandle res = mh1;
+ for (MethodHandle mh2 : mhs)
+ res = filterReturnValue(res, mh2);
+ return res;
+ }
+ private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
+ assertEquals(x.type(), y.type()); //??
+ MethodType t = x.type();
+ if (t.parameterCount() == 0) {
+ assertEqualFunctionAt(null, x, y);
+ return;
+ }
+ Class<?> ptype = t.parameterType(0);
+ if (ptype == long.class || ptype == Long.class) {
+ for (long i = -10; i <= 10; i++) {
+ assertEqualFunctionAt(i, x, y);
+ }
+ } else {
+ for (int i = -10; i <= 10; i++) {
+ assertEqualFunctionAt(i, x, y);
+ }
+ }
+ }
+ private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
+ Object[] args = new Object[x.type().parameterCount()];
+ Arrays.fill(args, v);
+ Object xval = invokeWithCatch(x, args);
+ Object yval = invokeWithCatch(y, args);
+ String msg = "ok";
+ if (!Objects.equals(xval, yval)) {
+ msg = ("applying "+x+" & "+y+" to "+v);
+ }
+ assertEquals(msg, xval, yval);
+ }
+ private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
+ try {
+ return mh.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ System.out.println("threw: "+mh+Arrays.asList(args));
+ ex.printStackTrace(System.out);
+ return ex;
+ }
+ }
+
+ private static final Lookup LOOKUP = lookup();
+ private static MethodHandle findStatic(String name,
+ Class<?> rtype,
+ Class<?>... ptypes) {
+ try {
+ return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ private static MethodHandle findStatic(String name,
+ Class<?> rtype,
+ List<?> ptypes) {
+ return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
+ }
+ static int getProperty(String name, int dflt) {
+ String qual = LOOKUP.lookupClass().getName();
+ String prop = System.getProperty(qual+"."+name);
+ if (prop == null) prop = System.getProperty(name);
+ if (prop == null) return dflt;
+ return Integer.parseInt(prop);
+ }
+
+ private static int opI(int... xs) {
+ stress();
+ int base = 100;
+ int z = 0;
+ for (int x : xs) {
+ z = (z * base) + (x % base);
+ }
+ verbose("opI", xs.length, xs, z);
+ return z;
+ }
+ private static int opI2(int x, int y) { return opI(x, y); } // x*100 + y%100
+ private static int opI3(int x, int y, int z) { return opI(x, y, z); }
+ private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
+ private static int opI(int x) { return opI2(x, 37); }
+ private static Object opI_L(int x) { return (Object) opI(x); }
+ private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
+ private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
+ private static long opJ(long x) { return (long) opI((int)x); }
+ private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
+ private static Object opL(Object x) { return (Object) opI((int)x); }
+ private static int opL2_I(Object x, Object y) { return opI2((int)x, (int)y); }
+ private static int opL_I(Object x) { return opI((int)x); }
+ private static long opL_J(Object x) { return (long) opI((int)x); }
+ private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
+ static {
+ opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
+ opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
+ opI2 = findStatic("opI2", int.class, int.class, int.class);
+ opI = findStatic("opI", int.class, int.class);
+ opI_L = findStatic("opI_L", Object.class, int.class);
+ opJ = findStatic("opJ", long.class, long.class);
+ opJ2 = findStatic("opJ2", long.class, long.class, long.class);
+ opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
+ opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
+ opL = findStatic("opL", Object.class, Object.class);
+ opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
+ opL_I = findStatic("opL_I", int.class, Object.class);
+ opL_J = findStatic("opL_J", long.class, Object.class);
+ }
+ private static final MethodHandle[] INT_COLLECTORS = {
+ constant(int.class, 42), opI, opI2, opI3, opI4
+ };
+ private static final MethodHandle[] BYTE_COLLECTORS = {
+ constant(byte.class, (byte)42), i2b(opI), i2b(opI2), i2b(opI3), i2b(opI4)
+ };
+ private static final MethodHandle[] LONG_COLLECTORS = {
+ constant(long.class, 42), opJ, opJ2, opJ3
+ };
+
+ private static int addI(int x, int y) { stress(); return x+y; }
+ private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
+ private static final MethodHandle addI, addL;
+ static {
+ addI = findStatic("addI", int.class, int.class, int.class);
+ addL = findStatic("addL", Object.class, Object.class, Object.class);
+ }
+
+ private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
+ return Arrays.asList(a, b, c, d, e, f, g, h);
+ }
+ private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
+ return Arrays.asList(a, b, c, d, e, f, g, h);
+ }
+ private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
+ Collections.nCopies(8, int.class));
+ private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
+ Collections.nCopies(8, long.class));
+ private static final MethodHandle[] INT_LISTERS, LONG_LISTERS, BYTE_LISTERS;
+ static {
+ int listerCount = list8ints.type().parameterCount() + 1;
+ INT_LISTERS = new MethodHandle[listerCount];
+ LONG_LISTERS = new MethodHandle[listerCount];
+ BYTE_LISTERS = new MethodHandle[listerCount];
+ MethodHandle lister = list8ints;
+ MethodHandle llister = list8longs;
+ for (int i = listerCount - 1; ; i--) {
+ INT_LISTERS[i] = lister;
+ LONG_LISTERS[i] = llister;
+ BYTE_LISTERS[i] = i2b(lister);
+ if (i == 0) break;
+ lister = insertArguments(lister, i-1, 0);
+ llister = insertArguments(llister, i-1, 0L);
+ }
+ }
+ private static MethodHandle i2b(MethodHandle mh) {
+ return MethodHandles.explicitCastArguments(mh, subst(mh.type(), int.class, byte.class));
+ }
+ private static MethodType subst(MethodType mt, Class<?> from, Class<?> to) {
+ for (int i = 0; i < mt.parameterCount(); i++) {
+ if (mt.parameterType(i) == from)
+ mt = mt.changeParameterType(i, to);
+ }
+ if (mt.returnType() == from)
+ mt = mt.changeReturnType(to);
+ return mt;
+ }
+
+
+ private static Object convI_L(int x) { stress(); return (Object) x; }
+ private static int convL_I(Object x) { stress(); return (int) x; }
+ private static Object convJ_L(long x) { stress(); return (Object) x; }
+ private static long convL_J(Object x) { stress(); return (long) x; }
+ private static int convJ_I(long x) { stress(); return (int) x; }
+ private static long convI_J(int x) { stress(); return (long) x; }
+ private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
+ static {
+ convI_L = findStatic("convI_L", Object.class, int.class);
+ convL_I = findStatic("convL_I", int.class, Object.class);
+ convJ_L = findStatic("convJ_L", Object.class, long.class);
+ convL_J = findStatic("convL_J", long.class, Object.class);
+ convJ_I = findStatic("convJ_I", int.class, long.class);
+ convI_J = findStatic("convI_J", long.class, int.class);
+ }
+
+ // stress modes:
+ private static final int MAX_DEPTH = getProperty("MAX_DEPTH", 5);
+ private static final int REPEAT = getProperty("REPEAT", 0);
+ private static final int STRESS = getProperty("STRESS", 0);
+ private static /*v*/ int STRESS_COUNT;
+ private static final Object[] SINK = new Object[4];
+ private static void stress() {
+ if (STRESS <= 0) return;
+ int count = STRESS + (STRESS_COUNT++ & 0x1); // non-constant value
+ for (int i = 0; i < count; i++) {
+ SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
+ }
+ }
+
+ // verbosity:
+ private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
+ private static void verbose(Object a, Object b, Object c, Object d) {
+ if (VERBOSITY <= 0) return;
+ verbose(1, a, b, c, d);
+ }
+ private static void verbose(Object a, Object b, Object c) {
+ if (VERBOSITY <= 0) return;
+ verbose(1, a, b, c);
+ }
+ private static void verbose(int level, Object a, Object... bcd) {
+ if (level > VERBOSITY) return;
+ String m = a.toString();
+ if (bcd != null && bcd.length > 0) {
+ List<Object> l = new ArrayList<>(bcd.length);
+ for (Object x : bcd) {
+ if (x instanceof Object[]) x = Arrays.asList((Object[])x);
+ if (x instanceof int[]) x = Arrays.toString((int[])x);
+ if (x instanceof long[]) x = Arrays.toString((long[])x);
+ l.add(x);
+ }
+ m = m+Arrays.asList(bcd);
+ }
+ System.out.println(m);
+ }
+ String testOnly;
+ String testOnlyTests;
+ private boolean startTest(String name) {
+ if (testOnly != null && !testOnly.contains(name))
+ return false;
+ verbose(0, "["+name+"]");
+ testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
+ return true;
+ }
+
+}
diff --git a/ojluni/src/test/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java b/ojluni/src/test/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java
new file mode 100644
index 0000000..9561660
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.java.lang.invoke.lib;
+
+// Android-added: import of Function for filterException
+import java.util.function.Function;
+
+/**
+ * Helper class used to catch and process VirtualMachineError with message "Out
+ * of space in CodeCache". Some JSR292 tests run out of code cache size, so code
+ * cache overflows and VME is thrown. This VME is considered as non-critical in
+ * some JSR292 tests, so it should be processed to prevent test failure.
+ */
+public class CodeCacheOverflowProcessor {
+
+ /**
+ * Checks if an instance of Throwable is caused by VirtualMachineError with
+ * message "Out of space in CodeCache". May be used as filter in method
+ * {@code jdk.testlibrary.Utils.filterException}.
+ *
+ * @param t - Throwable to check.
+ * @return true if Throwable is caused by VME, false otherwise.
+ */
+ public static Boolean isThrowableCausedByVME(Throwable t) {
+ Throwable causeOfT = t;
+ do {
+ if (causeOfT instanceof VirtualMachineError
+ && causeOfT.getMessage().matches(".*[Oo]ut of space"
+ + " in CodeCache.*")) {
+ return true;
+ }
+ causeOfT = causeOfT != null ? causeOfT.getCause() : null;
+ } while (causeOfT != null && causeOfT != t);
+ return false;
+ }
+
+ /**
+ * Checks if the given test throws an exception caused by
+ * VirtualMachineError with message "Out of space in CodeCache", and, if VME
+ * takes place, processes it so that no exception is thrown, and prints its
+ * stack trace. If test throws exception not caused by VME, this method just
+ * re-throws this exception.
+ *
+ * @param test - test to check for and process VirtualMachineError.
+ * @return - an exception caused by VME or null
+ * if test has thrown no exception.
+ * @throws Throwable - if test has thrown an exception
+ * that is not caused by VME.
+ */
+ public static Throwable runMHTest(ThrowingRunnable test) throws Throwable {
+ Throwable t = filterException(test::run,
+ CodeCacheOverflowProcessor::isThrowableCausedByVME);
+ if (t != null) {
+ System.err.printf("%nNon-critical exception caught becuse of"
+ + " code cache size is not enough to run all test cases.%n%n");
+ }
+ return t;
+ }
+
+ // BEGIN Android-changed: these interfaces methods taken from jdk.testlibrary
+ /**
+ * Interface same as java.lang.Runnable but with
+ * method {@code run()} able to throw any Throwable.
+ */
+ public static interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ /**
+ * Filters out an exception that may be thrown by the given
+ * test according to the given filter.
+ *
+ * @param test - method that is invoked and checked for exception.
+ * @param filter - function that checks if the thrown exception matches
+ * criteria given in the filter's implementation.
+ * @return - exception that matches the filter if it has been thrown or
+ * {@code null} otherwise.
+ * @throws Throwable - if test has thrown an exception that does not
+ * match the filter.
+ */
+ public static Throwable filterException(ThrowingRunnable test,
+ Function<Throwable, Boolean> filter) throws Throwable {
+ try {
+ test.run();
+ } catch (Throwable t) {
+ if (filter.apply(t)) {
+ return t;
+ } else {
+ throw t;
+ }
+ }
+ return null;
+ }
+ // END Android-changed: these interfaces methods taken from jdk.testlibrary
+}
diff --git a/ojluni/src/test/java/lang/invoke/remote/RemoteExample.java b/ojluni/src/test/java/lang/invoke/remote/RemoteExample.java
new file mode 100644
index 0000000..5237f9d
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/remote/RemoteExample.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.invoke.remote;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import test.java.lang.invoke.MethodHandlesTest;
+
+/**
+ * Out-of-package access into protected members of test.java.lang.invoke.remote.MethodHandle.PubExample.
+ */
+public class RemoteExample extends MethodHandlesTest.PubExample {
+ public RemoteExample() { super("RemoteExample"); }
+ public static Lookup lookup() { return MethodHandles.lookup(); }
+ public final void fin_v0() { MethodHandlesTest.called("Rem/fin_v0", this); }
+ protected void pro_v0() { MethodHandlesTest.called("Rem/pro_v0", this); }
+ protected static void pro_s0() { MethodHandlesTest.called("Rem/pro_s0"); }
+}
diff --git a/ojluni/src/test/java/util/Optional/Basic.java b/ojluni/src/test/java/util/Optional/Basic.java
index 5c82418..e49c0ed 100644
--- a/ojluni/src/test/java/util/Optional/Basic.java
+++ b/ojluni/src/test/java/util/Optional/Basic.java
@@ -33,7 +33,10 @@
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import static java.util.stream.Collectors.toList;
@@ -203,4 +206,69 @@
public void testStreamPresent() {
assertEquals(Optional.of("xyzzy").stream().collect(toList()), List.of("xyzzy"));
}
+
+ // BEGIN Android-added: More tests for coverage http://b/203822442.
+ // Also improves coverage for Optional{Int,Long,Double}
+ private static final Optional<Integer> P = Optional.<Integer>of(3);
+ private static final Optional<Integer> E = Optional.<Integer>empty();
+
+ @Test
+ void testIfPresentOrElse_empty() {
+ AtomicInteger flag = new AtomicInteger(0);
+ E.ifPresentOrElse(integer -> flag.set(1), () -> flag.set(2));
+ assertEquals(flag.get(), 2);
+ }
+
+ @Test
+ void testIfPresentOrElse_present() {
+ AtomicInteger flag = new AtomicInteger(0);
+ P.ifPresentOrElse(integer -> flag.set(1), () -> flag.set(2));
+ assertEquals(flag.get(), 1);
+ }
+
+ @Test
+ void testOr_empty() {
+ Optional<Integer> o = E.or(() -> Optional.of(5));
+ assertEquals((int) o.get(), 5);
+ }
+
+ @Test
+ void testOr_present() {
+ Optional<Integer> o = P.or(() -> Optional.of(5));
+ assertEquals((int) o.get(), 3);
+ }
+
+ @Test
+ public void testStream_empty() {
+ Stream<Integer> s = E.stream();
+ assertEquals(s.collect(Collectors.toList()), List.of());
+ }
+
+ @Test
+ public void testStream_present() {
+ Stream<Integer> s = P.stream();
+ assertEquals(s.collect(Collectors.toList()), List.of(3));
+ }
+
+ @Test(expectedExceptions = NoSuchElementException.class)
+ public void testOrElseThrow_empty() {
+ E.orElseThrow();
+ }
+
+ @Test
+ public void testOrElseThrow_present() {
+ assertEquals((int) P.orElseThrow(), 3);
+ }
+
+ @Test
+ public void testIsEmpty_empty() {
+ assertTrue(E.isEmpty());
+ }
+
+ @Test
+ public void testIsEmpty_present() {
+ assertFalse(P.isEmpty());
+ }
+ // END Android-added: More tests for coverage http://b/203822442.
+
}
diff --git a/openjdk_java_files.bp b/openjdk_java_files.bp
index 99971d1e..8ba1047 100644
--- a/openjdk_java_files.bp
+++ b/openjdk_java_files.bp
@@ -625,6 +625,7 @@
"ojluni/src/main/java/java/security/interfaces/RSAPrivateCrtKey.java",
"ojluni/src/main/java/java/security/interfaces/RSAPrivateKey.java",
"ojluni/src/main/java/java/security/interfaces/RSAPublicKey.java",
+ "ojluni/src/main/java/java/security/interfaces/XECKey.java",
"ojluni/src/main/java/java/security/interfaces/package-info.java",
"ojluni/src/main/java/java/security/InvalidAlgorithmParameterException.java",
"ojluni/src/main/java/java/security/InvalidKeyException.java",
@@ -696,6 +697,8 @@
"ojluni/src/main/java/java/security/spec/RSAPrivateCrtKeySpec.java",
"ojluni/src/main/java/java/security/spec/RSAPrivateKeySpec.java",
"ojluni/src/main/java/java/security/spec/RSAPublicKeySpec.java",
+ "ojluni/src/main/java/java/security/spec/XECPrivateKeySpec.java",
+ "ojluni/src/main/java/java/security/spec/XECPublicKeySpec.java",
"ojluni/src/main/java/java/security/spec/X509EncodedKeySpec.java",
"ojluni/src/main/java/java/security/spec/package-info.java",
"ojluni/src/main/java/java/security/Timestamp.java",