Merge "Revert "Enable String compression.""
diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java
index c598eee..1367f65 100644
--- a/dex/src/main/java/com/android/dex/DexFormat.java
+++ b/dex/src/main/java/com/android/dex/DexFormat.java
@@ -51,7 +51,12 @@
* completed and is not considered a valid dex file format.
* </p>
*/
- public static final String VERSION_CURRENT = "037";
+ public static final String VERSION_CURRENT = "038";
+
+ /**
+ * Versions currently supported.
+ */
+ static final String [] VERSIONS_SUPPORTED = { "035", "037", "038" };
/** dex file version number for API level 13 and earlier */
public static final String VERSION_FOR_API_13 = "035";
@@ -91,9 +96,13 @@
String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
- if (version.equals(VERSION_CURRENT)) {
- return API_CURRENT;
- } else if (version.equals(VERSION_FOR_API_13)) {
+ for (String supported : VERSIONS_SUPPORTED) {
+ if (version.equals(supported)) {
+ return API_CURRENT;
+ }
+ }
+
+ if (version.equals(VERSION_FOR_API_13)) {
return API_NO_EXTENDED_OPCODES;
}
diff --git a/dex/src/main/java/com/android/dex/TableOfContents.java b/dex/src/main/java/com/android/dex/TableOfContents.java
index 583f195..b33a749 100644
--- a/dex/src/main/java/com/android/dex/TableOfContents.java
+++ b/dex/src/main/java/com/android/dex/TableOfContents.java
@@ -36,6 +36,8 @@
public final Section fieldIds = new Section(0x0004);
public final Section methodIds = new Section(0x0005);
public final Section classDefs = new Section(0x0006);
+ public final Section callSiteIds = new Section(0x0007);
+ public final Section methodHandles = new Section(0x0008);
public final Section mapList = new Section(0x1000);
public final Section typeLists = new Section(0x1001);
public final Section annotationSetRefLists = new Section(0x1002);
@@ -48,9 +50,9 @@
public final Section encodedArrays = new Section(0x2005);
public final Section annotationsDirectories = new Section(0x2006);
public final Section[] sections = {
- header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
- typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
- debugInfos, annotations, encodedArrays, annotationsDirectories
+ header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
+ methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
+ stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
};
public int apiLevel;
@@ -76,7 +78,12 @@
byte[] magic = headerIn.readByteArray(8);
if (!DexFormat.isSupportedDexMagic(magic)) {
- throw new DexException("Unexpected magic: " + Arrays.toString(magic));
+ String msg =
+ String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
+ + "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
+ magic[0], magic[1], magic[2], magic[3],
+ magic[4], magic[5], magic[6], magic[7]);
+ throw new DexException(msg);
}
apiLevel = DexFormat.magicToApi(magic);
diff --git a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
index 13e9317..5a84c8e 100644
--- a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
+++ b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
@@ -106,6 +106,9 @@
// Hardcode default value for AVA. b/28174137
{ "com.sun.security.preserveOldDCEncoding", null },
+
+ // Hardcode default value for LogManager. b/28174137
+ { "java.util.logging.manager", null },
};
}
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index 8465a24..fb55475 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -48,12 +48,24 @@
private long dexFile;
/**
+ * References to CallSite (C array pointer) as they become resolved following
+ * interpreter semantics.
+ */
+ private long resolvedCallSites;
+
+ /**
* References to fields (C array pointer) as they become resolved following
* interpreter semantics. May refer to fields defined in other dex files.
*/
private long resolvedFields;
/**
+ * References to MethodType (C array pointer) as they become resolved following
+ * interpreter semantics.
+ */
+ private long resolvedMethodTypes;
+
+ /**
* References to methods (C array pointer) as they become resolved following
* interpreter semantics. May refer to methods defined in other dex files.
*/
@@ -72,10 +84,9 @@
private long strings;
/**
- * References to MethodType (C array pointer) as they become resolved following
- * interpreter semantics.
+ * The number of elements in the native call sites array.
*/
- private long resolvedMethodTypes;
+ private int numResolvedCallSites;
/**
* The number of elements in the native resolvedFields array.
@@ -83,6 +94,11 @@
private int numResolvedFields;
/**
+ * The number of elements in the native method types array.
+ */
+ private int numResolvedMethodTypes;
+
+ /**
* The number of elements in the native resolvedMethods array.
*/
private int numResolvedMethods;
@@ -97,11 +113,6 @@
*/
private int numStrings;
- /**
- * The number of elements in the native method types array.
- */
- private int numResolvedMethodTypes;
-
// Only created by the VM.
private DexCache() {}
diff --git a/luni/src/test/java/libcore/java/lang/OldSystemTest.java b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
index dc5741f..ad7151d 100644
--- a/luni/src/test/java/libcore/java/lang/OldSystemTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
@@ -257,13 +257,9 @@
public void test_gc() {
Runtime rt = Runtime.getRuntime();
- Vector<StringBuffer> vec = new Vector<StringBuffer>();
- long beforeTest = rt.totalMemory() - rt.freeMemory();
- while (rt.totalMemory() - rt.freeMemory() < beforeTest * 2) {
- vec.add(new StringBuffer(1000));
- }
+ byte[] data = new byte[2 * 1024 * 1024];
long beforeGC = rt.totalMemory() - rt.freeMemory();
- vec = null;
+ data = null;
System.gc();
System.runFinalization();
long afterGC = rt.totalMemory() - rt.freeMemory();
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
index 4c65161..8e75f71 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
@@ -16,6 +16,7 @@
package libcore.java.lang.invoke;
+import java.lang.Thread;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -26,6 +27,8 @@
public class MethodHandleCombinersTest extends TestCase {
+ static final int TEST_THREAD_ITERATIONS = 1000;
+
public static void testThrowException() throws Throwable {
MethodHandle handle = MethodHandles.throwException(String.class,
IllegalArgumentException.class);
@@ -1633,4 +1636,260 @@
} catch (IllegalArgumentException expected) {
}
}
+
+ // An exception thrown on worker threads and re-thrown on the main thread.
+ static Throwable workerException = null;
+
+ private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable {
+ // Create enough worker threads to be oversubscribed in bid to force some parallelism.
+ final int threadCount = Runtime.getRuntime().availableProcessors() + 1;
+ final Thread threads [] = new Thread [threadCount];
+
+ // Launch worker threads and iterate invoking method handle.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) {
+ mh.invoke();
+ }
+ } catch (Throwable t) {
+ workerException = t;
+ fail("Unexpected exception " + workerException);
+ }
+ }});
+ threads[i].start();
+ }
+
+ // Wait for completion
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i].join();
+ }
+
+ // Fail on main thread to avoid test appearing to complete successfully.
+ Throwable t = workerException;
+ workerException = null;
+ if (t != null) {
+ throw t;
+ }
+ }
+
+ public static void testDropInsertArgumentsMultithreaded() throws Throwable {
+ MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "dropArguments_delegate",
+ MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
+ MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
+ mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42);
+ invokeMultiThreaded(mh);
+ }
+
+ private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z)
+ throws Throwable {
+ assertEquals(e.getClass(), NumberFormatException.class);
+ assertEquals(e.getMessage(), "fake");
+ assertEquals(x, 66);
+ assertEquals(y, 51);
+ assertEquals(z, 20000000000l);
+ }
+
+ public static void testThrowCatchExceptionMultiThreaded() throws Throwable {
+ MethodHandle thrower = MethodHandles.throwException(void.class,
+ NumberFormatException.class);
+ thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class);
+ MethodHandle handler = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "exceptionHandler_delegate",
+ MethodType.methodType(void.class, NumberFormatException.class,
+ int.class, int.class, long.class));
+ MethodHandle catcher =
+ MethodHandles.catchException(thrower, NumberFormatException.class, handler);
+ MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l,
+ new NumberFormatException("fake"));
+ invokeMultiThreaded(caller);
+ }
+
+ private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable {
+ String actual = (String) mh.invoke("target", 42, 56);
+ assertEquals("target", actual);
+ actual = (String) mh.invoke("blah", 41, 56);
+ assertEquals("fallback", actual);
+ }
+
+ public static void testGuardWithTestMultiThreaded() throws Throwable {
+ MethodHandle test =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_test",
+ MethodType.methodType(boolean.class,
+ new Class<?>[]{String.class,
+ long.class}));
+ final MethodType type = MethodType.methodType(String.class,
+ new Class<?>[]{String.class, long.class, int.class});
+ final MethodHandle target =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_target", type);
+ final MethodHandle fallback =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_fallback", type);
+ MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
+ MethodHandle tester = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class,
+ "testTargetAndFallback_delegate",
+ MethodType.methodType(void.class, MethodHandle.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter));
+ }
+
+ private static void arrayElementSetterGetter_delegate(MethodHandle getter,
+ MethodHandle setter,
+ int [] values)
+ throws Throwable{
+ for (int i = 0; i < values.length; ++i) {
+ int value = i * 13;
+ setter.invoke(values, i, value);
+ assertEquals(values[i], value);
+ assertEquals(getter.invoke(values, i), values[i]);
+ }
+ }
+
+ public static void testReferenceArrayGetterMultiThreaded() throws Throwable {
+ MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
+ MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class,
+ "arrayElementSetterGetter_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class));
+ mh = MethodHandles.insertArguments(mh, 0, getter, setter,
+ new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 });
+ invokeMultiThreaded(mh);
+ }
+
+ private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable {
+ assertEquals(mh.invoke(), value);
+ }
+
+ public static void testConstantMultithreaded() throws Throwable {
+ final double value = 7.77e77;
+ MethodHandle constant = MethodHandles.constant(double.class, value);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkConstant_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, double.class));
+ mh = MethodHandles.insertArguments(mh, 0, constant, value);
+ invokeMultiThreaded(mh);
+ }
+
+ private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable {
+ assertEquals(mh.invoke(value), value);
+ }
+
+ public static void testIdentityMultiThreaded() throws Throwable {
+ final char value = 'z';
+ MethodHandle identity = MethodHandles.identity(char.class);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkIdentity_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, char.class));
+ mh = MethodHandles.insertArguments(mh, 0, identity, value);
+ invokeMultiThreaded(mh);
+ }
+
+ private static int multiplyByTwo(int x) { return x * 2; }
+ private static int divideByTwo(int x) { return x / 2; }
+ private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{
+ assertEquals(mh.invoke(value), value);
+ }
+
+ public static void testFilterReturnValueMultiThreaded() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "multiplyByTwo",
+ MethodType.methodType(int.class, int.class));
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "divideByTwo",
+ MethodType.methodType(int.class, int.class));
+ MethodHandle filtered = MethodHandles.filterReturnValue(target, filter);
+ assertEquals(filtered.invoke(33), 33);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals",
+ MethodType.methodType(void.class, MethodHandle.class, int.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77));
+ }
+
+ public static void compareStringAndFloat(String s, float f) {
+ assertEquals(s, Float.toString(f));
+ }
+
+ public static void testPermuteArgumentsMultiThreaded() throws Throwable {
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "compareStringAndFloat",
+ MethodType.methodType(void.class, String.class, float.class));
+ mh = MethodHandles.permuteArguments(
+ mh, MethodType.methodType(void.class, float.class, String.class), 1, 0);
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22"));
+ }
+
+ public static void testSpreadInvokerMultiThreaded() throws Throwable {
+ MethodType methodType = MethodType.methodType(
+ int.class, new Class<?>[]{String.class, String.class, String.class});
+ MethodHandle delegate = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "spreadReferences", methodType);
+ MethodHandle mh = delegate.asSpreader(String[].class, 3);
+ mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }});
+ invokeMultiThreaded(mh);
+ }
+
+ public static void testCollectorMultiThreaded() throws Throwable {
+ MethodHandle trailingRef = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "collectCharSequence",
+ MethodType.methodType(int.class, String.class, CharSequence[].class));
+ MethodHandle mh = trailingRef.asCollector(String[].class, 2);
+ mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c");
+ invokeMultiThreaded(mh);
+ }
+
+ public static void testFilterArgumentsMultiThreaded() throws Throwable {
+ MethodHandle filter1 = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter1",
+ MethodType.methodType(String.class, char.class));
+ MethodHandle filter2 = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter2",
+ MethodType.methodType(char.class, String.class));
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filterTarget",
+ MethodType.methodType(int.class, String.class, char.class, String.class, char.class));
+ MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
+ invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX"));
+ }
+
+ private static void checkStringResult_delegate(MethodHandle mh,
+ String expected) throws Throwable {
+ assertEquals(mh.invoke(), expected);
+ }
+
+ public static void testCollectArgumentsMultiThreaded() throws Throwable {
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter",
+ MethodType.methodType(String.class, char.class, char.class));
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "target",
+ MethodType.methodType(String.class, String.class, String.class, String.class));
+ MethodHandle collect = MethodHandles.collectArguments(target, 2, filter);
+ collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd');
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkStringResult_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, String.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d"));
+ }
+
+ public static void testFoldArgumentsMultiThreaded() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "foldTarget",
+ MethodType.methodType(String.class, String.class,
+ char.class, char.class, String.class));
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "foldFilter",
+ MethodType.methodType(String.class, char.class, char.class));
+ MethodHandle adapter = MethodHandles.foldArguments(target, filter);
+ adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e");
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkStringResult_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, String.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e"));
+ }
}
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
index 8381cbd..774f102 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
@@ -965,7 +965,7 @@
assertEquals(testPattern, s);
}
- private static void testReferenceReturnValueConversions() throws Throwable {
+ public void testReferenceReturnValueConversions() throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(
Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
@@ -1023,7 +1023,7 @@
assertEquals(0, c.compareTo(Float.valueOf(2.125f)));
}
- private static void testPrimitiveReturnValueConversions() throws Throwable {
+ public void testPrimitiveReturnValueConversions() throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(
Math.class, "min", MethodType.methodType(int.class, int.class, int.class));
@@ -1155,11 +1155,6 @@
}
}
- public static void testReturnValueConversions() throws Throwable {
- testReferenceReturnValueConversions();
- testPrimitiveReturnValueConversions();
- }
-
public static class BaseVariableArityTester {
public String update(Float f0, Float... floats) {
return "base " + f0 + ", " + Arrays.toString(floats);
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index ac64315..ffba98a 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -35,6 +35,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -45,6 +46,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -85,6 +87,20 @@
Set<Provider.Service> services = provider.getServices();
assertNotNull(services);
assertFalse(services.isEmpty());
+ if (LOG_DEBUG) {
+ Set<Provider.Service> originalServices = services;
+ services = new TreeSet<Provider.Service>(
+ new Comparator<Provider.Service>() {
+ public int compare(Provider.Service a, Provider.Service b) {
+ int typeCompare = a.getType().compareTo(b.getType());
+ if (typeCompare != 0) {
+ return typeCompare;
+ }
+ return a.getAlgorithm().compareTo(b.getAlgorithm());
+ }
+ });
+ services.addAll(originalServices);
+ }
for (Provider.Service service : services) {
String type = service.getType();
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index 6185f12..df86a38 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -81,49 +81,63 @@
}
/**
- * The minimum long value below which {@link Instant#toEpochMilli()} will
- * throw is not clearly documented. This test discovers if that minimum
- * value ever changes, and also checks that it is also the minimum Instant
- * (at a millisecond boundary) that can be converted to a Date.
+ * Test that conversion between Date and Instant works when the
+ * Instant is based on a millisecond value (and thus can be
+ * represented as a Date).
*/
- public void test_convertFromInstant_lowerBound() {
- // smallest millisecond Instant that can be converted to Date
- long minConvertible = -9223372036854775000L;
+ public void test_convertFromAndToInstant_milliseconds() {
+ check_convertFromAndToInstant_milliseconds(Long.MIN_VALUE);
+ check_convertFromAndToInstant_milliseconds(Long.MAX_VALUE);
- // show that this value is < 1 sec away from Long.MIN_VALUE
- assertEquals(Long.MIN_VALUE + 808, minConvertible);
-
- Instant inBound = Instant.ofEpochMilli(minConvertible);
- assertEquals(new Date(minConvertible), Date.from(inBound));
- assertEquals(minConvertible, inBound.toEpochMilli());
-
- Instant outOfBound = Instant.ofEpochMilli(minConvertible - 1);
- try {
- Date.from(outOfBound);
- fail();
- } catch (IllegalArgumentException expected) {
- assertEquals(ArithmeticException.class, expected.getCause().getClass());
- }
-
- try {
- outOfBound.toEpochMilli();
- fail();
- } catch (ArithmeticException expected) {
-
- }
+ check_convertFromAndToInstant_milliseconds(-1);
+ check_convertFromAndToInstant_milliseconds(0);
+ check_convertFromAndToInstant_milliseconds(123456789);
}
- public void test_convertFromInstant_upperBound() {
- Date.from(Instant.ofEpochMilli(Long.MAX_VALUE));
+ private static void check_convertFromAndToInstant_milliseconds(long millis) {
+ assertEquals(new Date(millis), Date.from(Instant.ofEpochMilli(millis)));
+ assertEquals(new Date(millis).toInstant(), Instant.ofEpochMilli(millis));
+ }
- Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 0));
- Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 999999999));
- Instant outOfBound = Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1, 0);
+ /**
+ * Checks the minimum/maximum Instant values (based on seconds and
+ * nanos) that can be converted to a Date, i.e. that can be converted
+ * to milliseconds without overflowing a long. Note that the rounding
+ * is such that the lower bound is exactly Long.MIN_VALUE msec whereas
+ * the upper bound is 999,999 nanos beyond Long.MAX_VALUE msec. This
+ * makes some sense in that the magnitude of the upper/lower bound
+ * nanos differ only by 1, just like the magnitude of Long.MIN_VALUE /
+ * MAX_VALUE differ only by 1.
+ */
+ public void test_convertFromInstant_secondsAndNanos() {
+ // Documentation for how the below bounds relate to long boundaries for milliseconds
+ assertEquals(-808, Long.MIN_VALUE % 1000);
+ assertEquals(807, Long.MAX_VALUE % 1000);
+
+ // Lower bound
+ long minSecond = Long.MIN_VALUE / 1000;
+ Date.from(Instant.ofEpochSecond(minSecond));
+ // This instant exactly corresponds to Long.MIN_VALUE msec because
+ // Long.MIN_VALUE % 1000 == -808 == (-1000 + 192)
+ Date.from(Instant.ofEpochSecond(minSecond - 1, 192000000));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 0));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 191999999));
+
+ // Upper bound
+ long maxSecond = Long.MAX_VALUE / 1000;
+ Date.from(Instant.ofEpochSecond(maxSecond, 0));
+ // This Instant is 999,999 nanos beyond Long.MAX_VALUE msec because
+ // (Long.MAX_VALUE % 1000) == 807
+ Date.from(Instant.ofEpochSecond(maxSecond, 807999999));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond + 1, 0));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond, 808000000));
+ }
+
+ private static void assertArithmeticOverflowDateFrom(Instant instant) {
try {
- Date.from(outOfBound);
- fail();
+ Date.from(instant);
+ fail(instant + " should not have been convertible to Date");
} catch (IllegalArgumentException expected) {
- assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
}
diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java
index 4a61147..b7a3202 100644
--- a/luni/src/test/java/libcore/java/util/HashtableTest.java
+++ b/luni/src/test/java/libcore/java/util/HashtableTest.java
@@ -20,6 +20,11 @@
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Field;
public class HashtableTest extends junit.framework.TestCase {
@@ -130,4 +135,30 @@
} catch (ClassNotFoundException expected) {
}
}
+
+ public void test_deserializedArrayLength() throws Exception {
+ final float loadFactor = 0.75f;
+ final int entriesCount = 100;
+ // Create table
+ Hashtable<Integer, Integer> hashtable1 = new Hashtable<>(1, loadFactor);
+ for (int i = 0; i < entriesCount; i++) {
+ hashtable1.put(i, 1);
+ }
+
+ // Serialize and deserialize
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(hashtable1);
+ }
+ Hashtable<Integer, Integer> hashtable2 =
+ (Hashtable<Integer, Integer>) new ObjectInputStream(
+ new ByteArrayInputStream(bos.toByteArray())).readObject();
+
+ // Check that table size is >= min expected size. Due to a bug in
+ // Hashtable deserialization this wasn't the case.
+ Field tableField = Hashtable.class.getDeclaredField("table");
+ tableField.setAccessible(true);
+ Object[] table2 = (Object[]) tableField.get(hashtable2);
+ assertTrue(table2.length >= (entriesCount / loadFactor));
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index d0d30f1..2b3347c 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -1428,7 +1428,7 @@
} catch (IllegalStateException expected) {
}
- // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey)
+ // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs BCRSAPrivateKey)
Key encryptKey = getEncryptKey(algorithm);
final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
@@ -4626,14 +4626,14 @@
if (s.getType().equals("Cipher")) {
if (s.getAlgorithm().startsWith("AES_128/")) {
Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
- assertTrue(checkAES_keyConstraint(c, 128));
- assertFalse(checkAES_keyConstraint(c, 192));
- assertFalse(checkAES_keyConstraint(c, 256));
+ assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
} else if (s.getAlgorithm().startsWith("AES_256/")) {
Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
- assertFalse(checkAES_keyConstraint(c, 128));
- assertFalse(checkAES_keyConstraint(c, 192));
- assertTrue(checkAES_keyConstraint(c, 256));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+ assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
}
}
}
diff --git a/ojluni/src/main/java/java/lang/BootstrapMethodError.java b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
new file mode 100644
index 0000000..a1509a0
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+/**
+ * Thrown to indicate that an {@code invokedynamic} instruction has
+ * failed to find its bootstrap method,
+ * or the bootstrap method has failed to provide a
+ * {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
+ * of the correct {@linkplain java.lang.invoke.MethodHandle#type method type}.
+ *
+ * @author John Rose, JSR 292 EG
+ * @since 1.7
+ */
+public class BootstrapMethodError extends LinkageError {
+ private static final long serialVersionUID = 292L;
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with no detail message.
+ */
+ public BootstrapMethodError() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public BootstrapMethodError(String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * detail message and cause.
+ *
+ * @param s the detail message.
+ * @param cause the cause, may be {@code null}.
+ */
+ public BootstrapMethodError(String s, Throwable cause) {
+ super(s, cause);
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * cause.
+ *
+ * @param cause the cause, may be {@code null}.
+ */
+ public BootstrapMethodError(Throwable cause) {
+ // cf. Throwable(Throwable cause) constructor.
+ super(cause == null ? null : cause.toString());
+ initCause(cause);
+ }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/CallSite.java b/ojluni/src/main/java/java/lang/invoke/CallSite.java
new file mode 100644
index 0000000..f9338e6
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/CallSite.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: Not using Empty
+//import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+
+/**
+ * A {@code CallSite} is a holder for a variable {@link MethodHandle},
+ * which is called its {@code target}.
+ * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
+ * all calls to the site's current target.
+ * A {@code CallSite} may be associated with several {@code invokedynamic}
+ * instructions, or it may be "free floating", associated with none.
+ * In any case, it may be invoked through an associated method handle
+ * called its {@linkplain #dynamicInvoker dynamic invoker}.
+ * <p>
+ * {@code CallSite} is an abstract class which does not allow
+ * direct subclassing by users. It has three immediate,
+ * concrete subclasses that may be either instantiated or subclassed.
+ * <ul>
+ * <li>If a mutable target is not required, an {@code invokedynamic} instruction
+ * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
+ * <li>If a mutable target is required which has volatile variable semantics,
+ * because updates to the target must be immediately and reliably witnessed by other threads,
+ * a {@linkplain VolatileCallSite volatile call site} may be used.
+ * <li>Otherwise, if a mutable target is required,
+ * a {@linkplain MutableCallSite mutable call site} may be used.
+ * </ul>
+ * <p>
+ * A non-constant call site may be <em>relinked</em> by changing its target.
+ * The new target must have the same {@linkplain MethodHandle#type() type}
+ * as the previous target.
+ * Thus, though a call site can be relinked to a series of
+ * successive targets, it cannot change its type.
+ * <p>
+ * Here is a sample use of call sites and bootstrap methods which links every
+ * dynamic call site to print its arguments:
+<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
+static void test() throws Throwable {
+ // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
+ InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
+}
+private static void printArgs(Object... args) {
+ System.out.println(java.util.Arrays.deepToString(args));
+}
+private static final MethodHandle printArgs;
+static {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Class thisClass = lookup.lookupClass(); // (who am I?)
+ printArgs = lookup.findStatic(thisClass,
+ "printArgs", MethodType.methodType(void.class, Object[].class));
+}
+private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
+ // ignore caller and name, but match the type:
+ return new ConstantCallSite(printArgs.asType(type));
+}
+</pre></blockquote>
+ * @author John Rose, JSR 292 EG
+ */
+abstract
+public class CallSite {
+ // Android-changed: not used.
+ // static { MethodHandleImpl.initStatics(); }
+
+ // The actual payload of this call site:
+ /*package-private*/
+ MethodHandle target; // Note: This field is known to the JVM. Do not change.
+
+ /**
+ * Make a blank call site object with the given method type.
+ * An initial target method is supplied which will throw
+ * an {@link IllegalStateException} if called.
+ * <p>
+ * Before this {@code CallSite} object is returned from a bootstrap method,
+ * it is usually provided with a more useful target method,
+ * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @throws NullPointerException if the proposed type is null
+ */
+
+ /*package-private*/
+ CallSite(MethodType type) {
+ // Android-changed: No cache for these so create uninitializedCallSite target here using
+ // method handle transformations to create a method handle that has the expected method
+ // type but throws an IllegalStateException.
+ // target = type.invokers().uninitializedCallSite();
+ this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
+ this.target = MethodHandles.insertArguments(
+ this.target, 0, new IllegalStateException("uninitialized call site"));
+ if (type.parameterCount() > 0) {
+ this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
+ }
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param target the method handle which will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ /*package-private*/
+ CallSite(MethodHandle target) {
+ target.type(); // null check
+ this.target = target;
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param targetType the desired type of the call site
+ * @param createTargetHook a hook which will bind the call site to the target method handle
+ * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+ * or if the target returned by the hook is not of the given {@code targetType}
+ * @throws NullPointerException if the hook returns a null value
+ * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+ * @throws Throwable anything else thrown by the the hook function
+ */
+ /*package-private*/
+ CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+ this(targetType);
+ ConstantCallSite selfCCS = (ConstantCallSite) this;
+ MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
+ checkTargetChange(this.target, boundTarget);
+ this.target = boundTarget;
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Returns the type of this call site's target.
+ * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
+ * The {@code setTarget} method enforces this invariant by refusing any new target that does
+ * not have the previous target's type.
+ * @return the type of the current target, which is also the type of any future target
+ */
+ public MethodType type() {
+ // warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
+ return target.type();
+ }
+
+ /**
+ * Returns the target method of the call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ *
+ * @return the current linkage state of the call site, its target method handle
+ * @see ConstantCallSite
+ * @see VolatileCallSite
+ * @see #setTarget
+ * @see ConstantCallSite#getTarget
+ * @see MutableCallSite#getTarget
+ * @see VolatileCallSite#getTarget
+ */
+ public abstract MethodHandle getTarget();
+
+ /**
+ * Updates the target method of this call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ * <p>
+ * The type of the new target must be {@linkplain MethodType#equals equal to}
+ * the type of the old target.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see CallSite#getTarget
+ * @see ConstantCallSite#setTarget
+ * @see MutableCallSite#setTarget
+ * @see VolatileCallSite#setTarget
+ */
+ public abstract void setTarget(MethodHandle newTarget);
+
+ void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
+ MethodType oldType = oldTarget.type();
+ MethodType newType = newTarget.type(); // null check!
+ if (!newType.equals(oldType))
+ throw wrongTargetType(newTarget, oldType);
+ }
+
+ private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+ return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
+ }
+
+ /**
+ * Produces a method handle equivalent to an invokedynamic instruction
+ * which has been linked to this call site.
+ * <p>
+ * This method is equivalent to the following code:
+ * <blockquote><pre>
+ * MethodHandle getTarget, invoker, result;
+ * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+ * invoker = MethodHandles.exactInvoker(this.type());
+ * result = MethodHandles.foldArguments(invoker, getTarget)
+ * </pre></blockquote>
+ *
+ * @return a method handle which always invokes this call site's current target
+ */
+ public abstract MethodHandle dynamicInvoker();
+
+ /*non-public*/ MethodHandle makeDynamicInvoker() {
+ // Android-changed: Use bindTo() rather than bindReceiver() (not implemented).
+ MethodHandle getTarget = GET_TARGET.bindTo(this);
+ MethodHandle invoker = MethodHandles.exactInvoker(this.type());
+ return MethodHandles.foldArguments(invoker, getTarget);
+ }
+
+ // Android-changed: no longer final. GET_TARGET assigned in initializeGetTarget().
+ private static MethodHandle GET_TARGET = null;
+
+ private void initializeGetTarget() {
+ // Android-changed: moved from static initializer for
+ // GET_TARGET to avoid issues with running early. Called from
+ // constructors. CallSite creation is not performance critical.
+ synchronized (CallSite.class) {
+ if (GET_TARGET == null) {
+ try {
+ GET_TARGET = IMPL_LOOKUP.
+ findVirtual(CallSite.class, "getTarget",
+ MethodType.methodType(MethodHandle.class));
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ }
+ }
+ }
+
+ /* Android-changed: not used. */
+ // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
+ // /*package-private*/
+ // static Empty uninitializedCallSite() {
+ // throw new IllegalStateException("uninitialized call site");
+ // }
+
+ // unsafe stuff:
+ private static final long TARGET_OFFSET;
+ static {
+ try {
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ } catch (Exception ex) { throw new Error(ex); }
+ }
+
+ /*package-private*/
+ void setTargetNormal(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
+ target = newTarget;
+ }
+ /*package-private*/
+ MethodHandle getTargetVolatile() {
+ return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
+ }
+ /*package-private*/
+ void setTargetVolatile(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
+ UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
+ }
+
+ /* Android-changed: not used. */
+ // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
+ // static CallSite makeSite(MethodHandle bootstrapMethod,
+ // // Callee information:
+ // String name, MethodType type,
+ // // Extra arguments for BSM, if any:
+ // Object info,
+ // // Caller information:
+ // Class<?> callerClass) {
+ // Object caller = IMPL_LOOKUP.in(callerClass);
+ // CallSite site;
+ // try {
+ // Object binding;
+ // info = maybeReBox(info);
+ // if (info == null) {
+ // binding = bootstrapMethod.invoke(caller, name, type);
+ // } else if (!info.getClass().isArray()) {
+ // binding = bootstrapMethod.invoke(caller, name, type, info);
+ // } else {
+ // Object[] argv = (Object[]) info;
+ // maybeReBoxElements(argv);
+ // if (3 + argv.length > 255)
+ // throw new BootstrapMethodError("too many bootstrap method arguments");
+ // MethodType bsmType = bootstrapMethod.type();
+ // if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
+ // binding = bootstrapMethod.invoke(caller, name, type, argv);
+ // else
+ // binding = MethodHandles.spreadInvoker(bsmType, 3)
+ // .invoke(bootstrapMethod, caller, name, type, argv);
+ // }
+ // //System.out.println("BSM for "+name+type+" => "+binding);
+ // if (binding instanceof CallSite) {
+ // site = (CallSite) binding;
+ // } else {
+ // throw new ClassCastException("bootstrap method failed to produce a CallSite");
+ // }
+ // if (!site.getTarget().type().equals(type))
+ // throw new WrongMethodTypeException("wrong type: "+site.getTarget());
+ // } catch (Throwable ex) {
+ // BootstrapMethodError bex;
+ // if (ex instanceof BootstrapMethodError)
+ // bex = (BootstrapMethodError) ex;
+ // else
+ // bex = new BootstrapMethodError("call site initialization exception", ex);
+ // throw bex;
+ // }
+ // return site;
+ // }
+ // private static Object maybeReBox(Object x) {
+ // if (x instanceof Integer) {
+ // int xi = (int) x;
+ // if (xi == (byte) xi)
+ // x = xi; // must rebox; see JLS 5.1.7
+ // }
+ // return x;
+ // }
+ // private static void maybeReBoxElements(Object[] xa) {
+ // for (int i = 0; i < xa.length; i++) {
+ // xa[i] = maybeReBox(xa[i]);
+ // }
+ // }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
new file mode 100644
index 0000000..2d9fede
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
+ * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
+ * bound to the call site's target.
+ * @author John Rose, JSR 292 EG
+ */
+public class ConstantCallSite extends CallSite {
+ private final boolean isFrozen;
+
+ /**
+ * Creates a call site with a permanent target.
+ * @param target the target to be permanently associated with this call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public ConstantCallSite(MethodHandle target) {
+ super(target);
+ isFrozen = true;
+ }
+
+ /**
+ * Creates a call site with a permanent target, possibly bound to the call site itself.
+ * <p>
+ * During construction of the call site, the {@code createTargetHook} is invoked to
+ * produce the actual target, as if by a call of the form
+ * {@code (MethodHandle) createTargetHook.invoke(this)}.
+ * <p>
+ * Note that user code cannot perform such an action directly in a subclass constructor,
+ * since the target must be fixed before the {@code ConstantCallSite} constructor returns.
+ * <p>
+ * The hook is said to bind the call site to a target method handle,
+ * and a typical action would be {@code someTarget.bindTo(this)}.
+ * However, the hook is free to take any action whatever,
+ * including ignoring the call site and returning a constant target.
+ * <p>
+ * The result returned by the hook must be a method handle of exactly
+ * the same type as the call site.
+ * <p>
+ * While the hook is being called, the new {@code ConstantCallSite}
+ * object is in a partially constructed state.
+ * In this state,
+ * a call to {@code getTarget}, or any other attempt to use the target,
+ * will result in an {@code IllegalStateException}.
+ * It is legal at all times to obtain the call site's type using the {@code type} method.
+ *
+ * @param targetType the type of the method handle to be permanently associated with this call site
+ * @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
+ * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+ * or if the target returned by the hook is not of the given {@code targetType}
+ * @throws NullPointerException if the hook returns a null value
+ * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+ * @throws Throwable anything else thrown by the the hook function
+ */
+ protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+ super(targetType, createTargetHook);
+ isFrozen = true;
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a {@code final} field of the {@code ConstantCallSite}.
+ * That is, the the target is always the original value passed
+ * to the constructor call which created this instance.
+ *
+ * @return the immutable linkage state of this call site, a constant method handle
+ * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+ */
+ @Override public final MethodHandle getTarget() {
+ if (!isFrozen) throw new IllegalStateException();
+ return target;
+ }
+
+ /**
+ * Always throws an {@link UnsupportedOperationException}.
+ * This kind of call site cannot change its target.
+ * @param ignore a new target proposed for the call site, which is ignored
+ * @throws UnsupportedOperationException because this kind of call site cannot change its target
+ */
+ @Override public final void setTarget(MethodHandle ignore) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns this call site's permanent target.
+ * Since that target will never change, this is a correct implementation
+ * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
+ * @return the immutable linkage state of this call site, a constant method handle
+ * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return getTarget();
+ }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index c41e0c2..9e26196 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -665,6 +665,9 @@
*/
static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, PUBLIC);
+ /** Package-private version of lookup which is trusted. */
+ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, ALL_MODES);
+
private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) {
String name = lookupClass.getName();
if (name.startsWith("java.lang.invoke."))
@@ -1656,7 +1659,7 @@
}
}
- public void throwMakeAccessException(String message, Object from) throws
+ private void throwMakeAccessException(String message, Object from) throws
IllegalAccessException{
message = message + ": "+ toString();
if (from != null) message += ", from " + from;
diff --git a/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
new file mode 100644
index 0000000..37ca324
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: not used.
+// import java.util.concurrent.atomic.AtomicInteger;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code MutableCallSite} is a {@link CallSite} whose target variable
+ * behaves like an ordinary field.
+ * An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates
+ * all calls to the site's current target.
+ * The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site
+ * also delegates each call to the site's current target.
+ * <p>
+ * Here is an example of a mutable call site which introduces a
+ * state variable into a method handle chain.
+ * <!-- JavaDocExamplesTest.testMutableCallSite -->
+ * <blockquote><pre>
+MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
+MethodHandle MH_name = name.dynamicInvoker();
+MethodType MT_str1 = MethodType.methodType(String.class);
+MethodHandle MH_upcase = MethodHandles.lookup()
+ .findVirtual(String.class, "toUpperCase", MT_str1);
+MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
+name.setTarget(MethodHandles.constant(String.class, "Rocky"));
+assertEquals("ROCKY", (String) worker1.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Fred"));
+assertEquals("FRED", (String) worker1.invokeExact());
+// (mutation can be continued indefinitely)
+ * </pre></blockquote>
+ * <p>
+ * The same call site may be used in several places at once.
+ * <blockquote><pre>
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
+MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
+assertEquals("Fred, dear?", (String) worker2.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Wilma"));
+assertEquals("WILMA", (String) worker1.invokeExact());
+assertEquals("Wilma, dear?", (String) worker2.invokeExact());
+ * </pre></blockquote>
+ * <p>
+ * <em>Non-synchronization of target values:</em>
+ * A write to a mutable call site's target does not force other threads
+ * to become aware of the updated value. Threads which do not perform
+ * suitable synchronization actions relative to the updated call site
+ * may cache the old target value and delay their use of the new target
+ * value indefinitely.
+ * (This is a normal consequence of the Java Memory Model as applied
+ * to object fields.)
+ * <p>
+ * For target values which will be frequently updated, consider using
+ * a {@linkplain VolatileCallSite volatile call site} instead.
+ * @author John Rose, JSR 292 EG
+ */
+public class MutableCallSite extends CallSite {
+ /**
+ * Creates a blank call site object with the given method type.
+ * The initial target is set to a method handle of the given type
+ * which will throw an {@link IllegalStateException} if called.
+ * <p>
+ * The type of the call site is permanently set to the given type.
+ * <p>
+ * Before this {@code CallSite} object is returned from a bootstrap method,
+ * or invoked in some other manner,
+ * it is usually provided with a more useful target method,
+ * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @param type the method type that this call site will have
+ * @throws NullPointerException if the proposed type is null
+ */
+ public MutableCallSite(MethodType type) {
+ super(type);
+ }
+
+ /**
+ * Creates a call site object with an initial target method handle.
+ * The type of the call site is permanently set to the initial target's type.
+ * @param target the method handle that will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public MutableCallSite(MethodHandle target) {
+ super(target);
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a normal field of the {@code MutableCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, the current thread may choose to reuse the result
+ * of a previous read of the target from memory, and may fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Updates the target method of this call site, as a normal variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same
+ * as of a write to an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, unrelated threads may fail to see the updated target
+ * until they perform a read from memory.
+ * Stronger guarantees can be created by putting appropriate operations
+ * into the bootstrap method and/or the target methods used
+ * at any given call site.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
+ */
+ @Override public void setTarget(MethodHandle newTarget) {
+ checkTargetChange(this.target, newTarget);
+ setTargetNormal(newTarget);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
+
+ // Android-changed: not exposing incomplete implementation.
+ // /**
+ // * Performs a synchronization operation on each call site in the given array,
+ // * forcing all other threads to throw away any cached values previously
+ // * loaded from the target of any of the call sites.
+ // * <p>
+ // * This operation does not reverse any calls that have already started
+ // * on an old target value.
+ // * (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.)
+ // * <p>
+ // * The overall effect is to force all future readers of each call site's target
+ // * to accept the most recently stored value.
+ // * ("Most recently" is reckoned relative to the {@code syncAll} itself.)
+ // * Conversely, the {@code syncAll} call may block until all readers have
+ // * (somehow) decached all previous versions of each call site's target.
+ // * <p>
+ // * To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
+ // * should generally be performed under some sort of mutual exclusion.
+ // * Note that reader threads may observe an updated target as early
+ // * as the {@code setTarget} call that install the value
+ // * (and before the {@code syncAll} that confirms the value).
+ // * On the other hand, reader threads may observe previous versions of
+ // * the target until the {@code syncAll} call returns
+ // * (and after the {@code setTarget} that attempts to convey the updated version).
+ // * <p>
+ // * This operation is likely to be expensive and should be used sparingly.
+ // * If possible, it should be buffered for batch processing on sets of call sites.
+ // * <p>
+ // * If {@code sites} contains a null element,
+ // * a {@code NullPointerException} will be raised.
+ // * In this case, some non-null elements in the array may be
+ // * processed before the method returns abnormally.
+ // * Which elements these are (if any) is implementation-dependent.
+ // *
+ // * <h3>Java Memory Model details</h3>
+ // * In terms of the Java Memory Model, this operation performs a synchronization
+ // * action which is comparable in effect to the writing of a volatile variable
+ // * by the current thread, and an eventual volatile read by every other thread
+ // * that may access one of the affected call sites.
+ // * <p>
+ // * The following effects are apparent, for each individual call site {@code S}:
+ // * <ul>
+ // * <li>A new volatile variable {@code V} is created, and written by the current thread.
+ // * As defined by the JMM, this write is a global synchronization event.
+ // * <li>As is normal with thread-local ordering of write events,
+ // * every action already performed by the current thread is
+ // * taken to happen before the volatile write to {@code V}.
+ // * (In some implementations, this means that the current thread
+ // * performs a global release operation.)
+ // * <li>Specifically, the write to the current target of {@code S} is
+ // * taken to happen before the volatile write to {@code V}.
+ // * <li>The volatile write to {@code V} is placed
+ // * (in an implementation specific manner)
+ // * in the global synchronization order.
+ // * <li>Consider an arbitrary thread {@code T} (other than the current thread).
+ // * If {@code T} executes a synchronization action {@code A}
+ // * after the volatile write to {@code V} (in the global synchronization order),
+ // * it is therefore required to see either the current target
+ // * of {@code S}, or a later write to that target,
+ // * if it executes a read on the target of {@code S}.
+ // * (This constraint is called "synchronization-order consistency".)
+ // * <li>The JMM specifically allows optimizing compilers to elide
+ // * reads or writes of variables that are known to be useless.
+ // * Such elided reads and writes have no effect on the happens-before
+ // * relation. Regardless of this fact, the volatile {@code V}
+ // * will not be elided, even though its written value is
+ // * indeterminate and its read value is not used.
+ // * </ul>
+ // * Because of the last point, the implementation behaves as if a
+ // * volatile read of {@code V} were performed by {@code T}
+ // * immediately after its action {@code A}. In the local ordering
+ // * of actions in {@code T}, this read happens before any future
+ // * read of the target of {@code S}. It is as if the
+ // * implementation arbitrarily picked a read of {@code S}'s target
+ // * by {@code T}, and forced a read of {@code V} to precede it,
+ // * thereby ensuring communication of the new target value.
+ // * <p>
+ // * As long as the constraints of the Java Memory Model are obeyed,
+ // * implementations may delay the completion of a {@code syncAll}
+ // * operation while other threads ({@code T} above) continue to
+ // * use previous values of {@code S}'s target.
+ // * However, implementations are (as always) encouraged to avoid
+ // * livelock, and to eventually require all threads to take account
+ // * of the updated target.
+ // *
+ // * <p style="font-size:smaller;">
+ // * <em>Discussion:</em>
+ // * For performance reasons, {@code syncAll} is not a virtual method
+ // * on a single call site, but rather applies to a set of call sites.
+ // * Some implementations may incur a large fixed overhead cost
+ // * for processing one or more synchronization operations,
+ // * but a small incremental cost for each additional call site.
+ // * In any case, this operation is likely to be costly, since
+ // * other threads may have to be somehow interrupted
+ // * in order to make them notice the updated target value.
+ // * However, it may be observed that a single call to synchronize
+ // * several sites has the same formal effect as many calls,
+ // * each on just one of the sites.
+ // *
+ // * <p style="font-size:smaller;">
+ // * <em>Implementation Note:</em>
+ // * Simple implementations of {@code MutableCallSite} may use
+ // * a volatile variable for the target of a mutable call site.
+ // * In such an implementation, the {@code syncAll} method can be a no-op,
+ // * and yet it will conform to the JMM behavior documented above.
+ // *
+ // * @param sites an array of call sites to be synchronized
+ // * @throws NullPointerException if the {@code sites} array reference is null
+ // * or the array contains a null
+ // */
+ // public static void syncAll(MutableCallSite[] sites) {
+ // if (sites.length == 0) return;
+ // STORE_BARRIER.lazySet(0);
+ // for (int i = 0; i < sites.length; i++) {
+ // sites[i].getClass(); // trigger NPE on first null
+ // }
+ // // FIXME: NYI
+ // }
+ // private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 10ce855..82cc797 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -237,27 +237,25 @@
*/
public static class ReferenceArrayElementGetter extends Transformer {
private final Class<?> arrayClass;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
public ReferenceArrayElementGetter(Class<?> arrayClass) {
super(MethodType.methodType(arrayClass.getComponentType(),
new Class<?>[]{arrayClass, int.class}));
this.arrayClass = arrayClass;
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
- writer.attach(emulatedStackFrame);
// Read the array object and the index from the stack frame.
final Object[] array = (Object[]) reader.nextReference(arrayClass);
final int index = reader.nextInt();
// Write the array element back to the stack frame.
+ final StackFrameWriter writer = new StackFrameWriter();
+ writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
writer.putNextReference(array[index], arrayClass.getComponentType());
}
@@ -268,17 +266,16 @@
*/
public static class ReferenceArrayElementSetter extends Transformer {
private final Class<?> arrayClass;
- private final StackFrameReader reader;
public ReferenceArrayElementSetter(Class<?> arrayClass) {
super(MethodType.methodType(void.class,
new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() }));
this.arrayClass = arrayClass;
- reader = new StackFrameReader();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
// Read the array object, index and the value to write from the stack frame.
@@ -295,23 +292,20 @@
*/
public static class ReferenceIdentity extends Transformer {
private final Class<?> type;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
public ReferenceIdentity(Class<?> type) {
super(MethodType.methodType(type, type));
this.type = type;
-
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
+
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
-
writer.putNextReference(reader.nextReference(type), type);
}
}
@@ -336,8 +330,6 @@
private char typeChar;
- private final EmulatedStackFrame.StackFrameWriter writer;
-
public Constant(Class<?> type, Object value) {
super(MethodType.methodType(type));
this.type = type;
@@ -372,12 +364,11 @@
} else {
throw new AssertionError("unknown type: " + typeChar);
}
-
- writer = new EmulatedStackFrame.StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
@@ -491,9 +482,6 @@
private final MethodHandle target;
private final MethodHandle filter;
- private final EmulatedStackFrame.StackFrameReader returnValueReader;
- private final EmulatedStackFrame.StackFrameWriter filterWriter;
-
private final EmulatedStackFrame.Range allArgs;
public FilterReturnValue(MethodHandle target, MethodHandle filter) {
@@ -502,9 +490,6 @@
this.target = target;
this.filter = filter;
- returnValueReader = new EmulatedStackFrame.StackFrameReader();
- filterWriter = new EmulatedStackFrame.StackFrameWriter();
-
allArgs = EmulatedStackFrame.Range.all(type());
}
@@ -515,15 +500,16 @@
// the same parameter shapes.
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0);
-
target.invoke(targetFrame);
// Perform the invoke.
+ final StackFrameReader returnValueReader = new StackFrameReader();
returnValueReader.attach(targetFrame);
returnValueReader.makeReturnValueAccessor();
// Create an emulated frame for the filter and copy all its arguments across.
EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
+ final StackFrameWriter filterWriter = new StackFrameWriter();
filterWriter.attach(filterFrame);
final Class<?> returnType = target.type().rtype();
@@ -563,30 +549,24 @@
private final MethodHandle target;
private final int[] reorder;
- private final EmulatedStackFrame.StackFrameWriter writer;
- private final EmulatedStackFrame.StackFrameReader reader;
-
public PermuteArguments(MethodType type, MethodHandle target, int[] reorder) {
super(type);
this.target = target;
this.reorder = reorder;
-
- writer = new EmulatedStackFrame.StackFrameWriter();
- reader = new EmulatedStackFrame.StackFrameReader();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
- final Class<?>[] ptypes = type().ptypes();
// In the interests of simplicity, we box / unbox arguments while performing
// the permutation. We first iterate through the incoming stack frame and box
// each argument. We then unbox and write out the argument to the target frame
// according to the specified reordering.
Object[] arguments = new Object[reorder.length];
-
+ final Class<?>[] ptypes = type().ptypes();
for (int i = 0; i < ptypes.length; ++i) {
final Class<?> ptype = ptypes[i];
if (!ptype.isPrimitive()) {
@@ -613,6 +593,7 @@
}
EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type());
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(calleeFrame);
for (int i = 0; i < ptypes.length; ++i) {
@@ -1083,7 +1064,6 @@
* arguments that aren't a part of the trailing array.
*/
private final Range copyRange;
- private final StackFrameWriter writer;
Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) {
super(spreaderType);
@@ -1102,7 +1082,6 @@
this.numArrayArgs = numArrayArgs;
// Copy all args except for the last argument.
this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
- this.writer = new StackFrameWriter();
}
@Override
@@ -1115,6 +1094,7 @@
// Attach the writer, prepare to spread the trailing array arguments into
// the callee frame.
+ StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame,
arrayOffset,
copyRange.numReferences,
@@ -1350,9 +1330,6 @@
*/
private final Range copyRange;
- private final StackFrameWriter writer;
- private final StackFrameReader reader;
-
Collector(MethodHandle delegate, Class<?> arrayType, int length) {
super(delegate.type().asCollectorType(arrayType, length));
@@ -1365,8 +1342,6 @@
// Copy all args except for the last argument.
copyRange = EmulatedStackFrame.Range.of(delegate.type(), 0, arrayOffset);
- writer = new StackFrameWriter();
- reader = new StackFrameReader();
}
@Override
@@ -1379,7 +1354,9 @@
// Attach the writer, prepare to spread the trailing array arguments into
// the callee frame.
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
switch (arrayTypeChar) {
@@ -1481,12 +1458,6 @@
/** The list of filters to apply */
private final MethodHandle[] filters;
- private final StackFrameWriter writer;
- private final StackFrameReader reader;
-
- private final StackFrameWriter filterWriter;
- private final StackFrameReader filterReader;
-
FilterArguments(MethodHandle target, int pos, MethodHandle[] filters) {
super(deriveType(target, pos, filters));
@@ -1494,11 +1465,6 @@
this.pos = pos;
this.filters = filters;
- this.writer = new StackFrameWriter();
- this.reader = new StackFrameReader();
-
- this.filterReader = new StackFrameReader();
- this.filterWriter = new StackFrameWriter();
}
private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) {
@@ -1512,9 +1478,11 @@
@Override
public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(stackFrame);
EmulatedStackFrame transformedFrame = EmulatedStackFrame.create(target.type());
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(transformedFrame);
final Class<?>[] ptypes = target.type().ptypes();
@@ -1537,12 +1505,14 @@
EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
// Copy the next argument from the stack frame to the filter frame.
+ final StackFrameWriter filterWriter = new StackFrameWriter();
filterWriter.attach(filterFrame);
copyNext(reader, filterWriter, filter.type().ptypes()[0]);
filter.invoke(filterFrame);
// Copy the argument back from the filter frame to the stack frame.
+ final StackFrameReader filterReader = new StackFrameReader();
filterReader.attach(filterFrame);
filterReader.makeReturnValueAccessor();
copyNext(filterReader, writer, ptype);
@@ -1581,9 +1551,6 @@
*/
private final Range range2;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
-
private final int referencesOffset;
private final int stackFrameOffset;
@@ -1606,9 +1573,6 @@
this.range2 = null;
}
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
-
// Calculate the number of primitive bytes (or references) we copy to the
// target frame based on the return value of the combiner.
final Class<?> collectorRType = collector.type().rtype();
@@ -1637,7 +1601,9 @@
// If one of these offsets is not zero, we have a return value to copy.
if (referencesOffset != 0 || stackFrameOffset != 0) {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(filterFrame).makeReturnValueAccessor();
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame, pos, range1.numReferences, range1.numBytes);
copyNext(reader, writer, target.type().ptypes()[0]);
}
@@ -1663,9 +1629,6 @@
private final Range combinerArgs;
private final Range targetArgs;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
-
private final int referencesOffset;
private final int stackFrameOffset;
@@ -1678,9 +1641,6 @@
combinerArgs = Range.all(combiner.type());
targetArgs = Range.all(type());
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
-
final Class<?> combinerRType = combiner.type().rtype();
if (combinerRType == void.class) {
stackFrameOffset = 0;
@@ -1706,7 +1666,9 @@
// If one of these offsets is not zero, we have a return value to copy.
if (referencesOffset != 0 || stackFrameOffset != 0) {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(combinerFrame).makeReturnValueAccessor();
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame);
copyNext(reader, writer, target.type().ptypes()[0]);
}
@@ -1736,7 +1698,6 @@
private final Range range1;
private final Range range2;
- private final StackFrameWriter writer;
InsertArguments(MethodHandle target, int pos, Object[] values) {
super(target.type().dropParameterTypes(pos, pos + values.length));
@@ -1747,8 +1708,6 @@
final MethodType type = type();
range1 = EmulatedStackFrame.Range.of(type, 0, pos);
range2 = Range.of(type, pos, type.parameterCount());
-
- writer = new StackFrameWriter();
}
@Override
@@ -1760,6 +1719,7 @@
// Attach a stack frame writer so that we can copy the next |values.length|
// arguments.
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(calleeFrame, pos, range1.numReferences, range1.numBytes);
// Copy all the arguments supplied in |values|.
diff --git a/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
new file mode 100644
index 0000000..6444335
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
+ * An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
+ * to its call site target immediately, even if the update occurs in another thread.
+ * There may be a performance penalty for such tight coupling between threads.
+ * <p>
+ * In other respects, a {@code VolatileCallSite} is interchangeable
+ * with {@code MutableCallSite}.
+ * @see MutableCallSite
+ * @author John Rose, JSR 292 EG
+ */
+public class VolatileCallSite extends CallSite {
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The initial target is set to a method handle
+ * of the given type which will throw an {@code IllegalStateException} if called.
+ * @param type the method type that this call site will have
+ * @throws NullPointerException if the proposed type is null
+ */
+ public VolatileCallSite(MethodType type) {
+ super(type);
+ }
+
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The target is set to the given value.
+ * @param target the method handle that will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public VolatileCallSite(MethodHandle target) {
+ super(target);
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a {@code volatile} field of the {@code VolatileCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from a {@code volatile} field.
+ * <p>
+ * In particular, the current thread is required to issue a fresh
+ * read of the target from memory, and must not fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
+ return getTargetVolatile();
+ }
+
+ /**
+ * Updates the target method of this call site, as a volatile variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same as of a write to a volatile field.
+ * In particular, any threads is guaranteed to see the updated target
+ * the next time it calls {@code getTarget}.
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
+ */
+ @Override public void setTarget(MethodHandle newTarget) {
+ checkTargetChange(getTargetVolatile(), newTarget);
+ setTargetVolatile(newTarget);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
+}
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index deb186c..a932893 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -895,15 +895,6 @@
algorithm = algorithm.toUpperCase(ENGLISH);
this.algorithm = intern ? algorithm.intern() : algorithm;
}
- // BEGIN ANDROID-CHANGED
- // The above constructor is not amenable to compile-time execution as Locale.ENGLISH
- // cannot be retrieved. Special-case a constructor for the static field "previousKey".
- private ServiceKey() {
- this.type = "";
- this.originalAlgorithm = "";
- this.algorithm = "";
- }
- // END ANDROID-CHANGED
public int hashCode() {
return type.hashCode() + algorithm.hashCode();
}
@@ -1088,10 +1079,7 @@
// list and queries each provider with the same values until it finds
// a matching service
private static volatile ServiceKey previousKey =
- // BEGIN ANDROID-CHANGED
- // was: new ServiceKey("", "", false);
- new ServiceKey();
- // END ANDROID-CHANGED
+ new ServiceKey("", "", false);
/**
* Get an unmodifiable Set of all services supported by
diff --git a/ojluni/src/main/java/java/time/Instant.java b/ojluni/src/main/java/java/time/Instant.java
index 91a177c..9b9efa5 100644
--- a/ojluni/src/main/java/java/time/Instant.java
+++ b/ojluni/src/main/java/java/time/Instant.java
@@ -1229,8 +1229,14 @@
* @throws ArithmeticException if numeric overflow occurs
*/
public long toEpochMilli() {
- long millis = Math.multiplyExact(seconds, 1000);
- return millis + nanos / 1000_000;
+ if (seconds < 0 && nanos > 0) {
+ long millis = Math.multiplyExact(seconds+1, 1000);
+ long adjustment = nanos / 1000_000 - 1000;
+ return Math.addExact(millis, adjustment);
+ } else {
+ long millis = Math.multiplyExact(seconds, 1000);
+ return Math.addExact(millis, nanos / 1000_000);
+ }
}
//-----------------------------------------------------------------------
diff --git a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
index 676421a..5848e94 100644
--- a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
+++ b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
@@ -715,7 +715,7 @@
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
- * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
+ * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
@@ -733,7 +733,7 @@
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
- * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
+ * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
diff --git a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
index 15f7d84..29e503e 100644
--- a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
+++ b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
@@ -132,7 +132,7 @@
private static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
static Calendar createCalendar() {
- return Calendar.getJapanesImperialInstance(TimeZone.getDefault(), LOCALE);
+ return Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), LOCALE);
}
/**
diff --git a/ojluni/src/main/java/java/time/format/ZoneName.java b/ojluni/src/main/java/java/time/format/ZoneName.java
index 1d58109..c3eb20a 100644
--- a/ojluni/src/main/java/java/time/format/ZoneName.java
+++ b/ojluni/src/main/java/java/time/format/ZoneName.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 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
@@ -28,12 +28,8 @@
import android.icu.text.TimeZoneNames;
import android.icu.util.TimeZone;
import android.icu.util.ULocale;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
/**
* A helper class to map a zone name to metazone and back to the
diff --git a/ojluni/src/main/java/java/util/Calendar.java b/ojluni/src/main/java/java/util/Calendar.java
index 6f8b9a9..8593403 100644
--- a/ojluni/src/main/java/java/util/Calendar.java
+++ b/ojluni/src/main/java/java/util/Calendar.java
@@ -1489,7 +1489,15 @@
setWeekDefinition(MONDAY, 4);
cal = gcal;
break;
- // Android-changed: removed support for "buddhist" and "japanese".
+// Android-changed BEGIN: removed support for "buddhist" and "japanese".
+// case "buddhist":
+// cal = new BuddhistCalendar(zone, locale);
+// cal.clear();
+// break;
+// case "japanese":
+// cal = new JapaneseImperialCalendar(zone, locale, true);
+// break;
+// Android-changed END: removed support for "buddhist" and "japanese".
default:
throw new IllegalArgumentException("unknown calendar type: " + type);
}
@@ -1586,6 +1594,7 @@
*/
protected Calendar(TimeZone zone, Locale aLocale)
{
+ // Android-added BEGIN: Allow aLocale == null
// http://b/16938922.
//
// TODO: This is for backwards compatibility only. Seems like a better idea to throw
@@ -1593,6 +1602,7 @@
if (aLocale == null) {
aLocale = Locale.getDefault();
}
+ // Android-added END: Allow aLocale == null
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
stamp = new int[FIELD_COUNT];
@@ -1656,18 +1666,22 @@
return createCalendar(zone, aLocale);
}
+ // Android-added BEGIN: add getJapaneseImperialInstance()
/**
* Create a Japanese Imperial Calendar.
* @hide
*/
- public static Calendar getJapanesImperialInstance(TimeZone zone, Locale aLocale) {
+ public static Calendar getJapaneseImperialInstance(TimeZone zone, Locale aLocale) {
return new JapaneseImperialCalendar(zone, aLocale);
}
+ // Android-added END: add getJapaneseImperialInstance()
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
+ // Android-changed BEGIN: only support GregorianCalendar here
return new GregorianCalendar(zone, aLocale);
+ // Android-changed END: only support GregorianCalendar here
}
/**
@@ -1758,7 +1772,12 @@
public void setTimeInMillis(long millis) {
// If we don't need to recalculate the calendar field values,
// do nothing.
+// Android-changed BEGIN: Removed ZoneInfo support
if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet) {
+// if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
+// && (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
+// Android-changed END: Removed ZoneInfo support
+
return;
}
time = millis;
@@ -2041,11 +2060,13 @@
* @since 1.6
*/
public String getDisplayName(int field, int style, Locale locale) {
- // Android-changed: Android has traditionally treated ALL_STYLES as SHORT, even though
+ // Android-changed BEGIN: Treat ALL_STYLES as SHORT
+ // Android has traditionally treated ALL_STYLES as SHORT, even though
// it's not documented to be a valid value for style.
if (style == ALL_STYLES) {
style = SHORT;
}
+ // Android-changed END: Treat ALL_STYLES as SHORT
if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
@@ -2139,6 +2160,7 @@
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
+ // Android-added: Add complete() here to fix leniency, see http://b/35382060
complete();
String calendarType = getCalendarType();
@@ -2186,10 +2208,12 @@
baseStyle < minStyle || baseStyle > maxStyle) {
throw new IllegalArgumentException();
}
- // Android-changed: 3 is not a valid base style (1, 2 and 4 are, though), throw if used.
+ // Android-added BEGIN: Check for invalid baseStyle == 3
+ // 3 is not a valid base style (unlike 1, 2 and 4). Throw if used.
if (baseStyle == 3) {
throw new IllegalArgumentException();
}
+ // Android-added END: Check for invalid baseStyle == 3
if (locale == null) {
throw new NullPointerException();
}
diff --git a/ojluni/src/main/java/java/util/Formatter.java b/ojluni/src/main/java/java/util/Formatter.java
index ca1b990..43d5043 100644
--- a/ojluni/src/main/java/java/util/Formatter.java
+++ b/ojluni/src/main/java/java/util/Formatter.java
@@ -2710,6 +2710,7 @@
if (s != null) {
try {
// Android-changed: FormatSpecifierParser passes in correct String.
+ // index = Integer.parseInt(s.substring(0, s.length() - 1));
index = Integer.parseInt(s);
} catch (NumberFormatException x) {
assert(false);
@@ -2757,6 +2758,8 @@
precision = -1;
if (s != null) {
try {
+ // Android-changed: FormatSpecifierParser passes in correct String.
+ // precision = Integer.parseInt(s.substring(1));
precision = Integer.parseInt(s);
if (precision < 0)
throw new IllegalFormatPrecisionException(precision);
@@ -2789,7 +2792,7 @@
return c;
}
- // Android-changed: FormatSpecifierParser passes in the values instead of a Matcher.
+ // Android-changed BEGIN: FormatSpecifierParser passes in the values instead of a Matcher.
FormatSpecifier(String indexStr, String flagsStr, String widthStr,
String precisionStr, String tTStr, String convStr) {
int idx = 1;
@@ -2806,7 +2809,7 @@
}
conversion(convStr);
-
+ // Android-changed END: FormatSpecifierParser passes in the values instead of a Matcher.
if (dt)
checkDateTime();
else if (Conversion.isGeneral(c))
@@ -2998,6 +3001,7 @@
s = s.substring(0, precision);
if (f.contains(Flags.UPPERCASE)) {
// Android-changed: Use provided locale instead of default, if it is non-null.
+ // s = s.toUpperCase();
s = s.toUpperCase(l != null ? l : Locale.getDefault());
}
a.append(justify(s));
@@ -3369,12 +3373,13 @@
newW = adjustWidth(width - exp.length - 1, f, neg);
localizedMagnitude(sb, mant, f, newW, l);
- // Android-changed: Use localized exponent separator for %e.
+ // Android-changed BEGIN: Use localized exponent separator for %e.
Locale separatorLocale = (l != null) ? l : Locale.getDefault();
LocaleData localeData = LocaleData.get(separatorLocale);
sb.append(f.contains(Flags.UPPERCASE) ?
localeData.exponentSeparator.toUpperCase(separatorLocale) :
localeData.exponentSeparator.toLowerCase(separatorLocale));
+ // Android-changed END: Use localized exponent separator for %e.
Flags flags = f.dup().remove(Flags.GROUP);
char sign = exp[0];
@@ -4455,14 +4460,14 @@
grpSep = dfs.getGroupingSeparator();
DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
grpSize = df.getGroupingSize();
- // Android-changed: http://b/33245708 : Some locales have a group separator but
- // also patterns without groups. If we do not clear the group separator in these
- // cases a divide by zero is thrown when determining where to place the
- // separators.
+ // Android-changed BEGIN: http://b/33245708
+ // Some locales have a group separator but also patterns without groups.
+ // If we do not clear the group separator in these cases a divide by zero
+ // is thrown when determining where to place the separators.
if (!df.isGroupingUsed() || df.getGroupingSize() == 0) {
grpSep = '\0';
}
- // Android-changed: end http://b/33245708.
+ // Android-changed END: http://b/33245708.
}
}
diff --git a/ojluni/src/main/java/java/util/GregorianCalendar.java b/ojluni/src/main/java/java/util/GregorianCalendar.java
index 3d8a7c79..15c7652 100644
--- a/ojluni/src/main/java/java/util/GregorianCalendar.java
+++ b/ojluni/src/main/java/java/util/GregorianCalendar.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -739,10 +739,12 @@
gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
}
+ // Android-added BEGIN
GregorianCalendar(long milliseconds) {
this();
setTimeInMillis(milliseconds);
}
+ // Android-added END
/////////////////
// Public methods
@@ -1078,8 +1080,7 @@
}
fd += delta; // fd is the expected fixed date after the calculation
-
- // Android changed: move time zone related calculation to separate method.
+ // Android-changed BEGIN: time zone related calculation via helper methods
// Calculate the time in the UTC time zone.
long utcTime = (fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
@@ -1092,6 +1093,7 @@
// Update the time and recompute the fields.
setTimeInMillis(millis);
+ // Android-changed END: time zone related calculation via helper methods
}
}
@@ -2344,9 +2346,12 @@
}
if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
if (tz instanceof ZoneInfo) {
- // Android changed: libcore ZoneInfo uses different method to get offsets.
+ // Android changed BEGIN: use libcore.util.ZoneInfo
+ // The method name to get offsets differs from sun.util.calendar.ZoneInfo
+ // zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
ZoneInfo zoneInfo = (ZoneInfo) tz;
zoneOffset = zoneInfo.getOffsetsByUtcTime(time, zoneOffsets);
+ // Android-changed END: use libcore.util.ZoneInfo
} else {
zoneOffset = tz.getOffset(time);
zoneOffsets[0] = tz.getRawOffset();
@@ -2796,11 +2801,11 @@
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
TimeZone zone = getZone();
- // Android changed: move time zone related calculation to separate method.
-
+ // Android-changed BEGIN: time zone related calculation via helper methods
int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
millis = adjustForZoneAndDaylightSavingsTime(tzMask, millis, zone);
+ // Android-changed END: time zone related calculation via helper methods
// Set this calendar's time in milliseconds
time = millis;
@@ -2823,6 +2828,7 @@
setFieldsNormalized(mask);
}
+ // Android-added BEGIN: helper methods for time zone related calculation
/**
* Calculates the time in milliseconds that this calendar represents using the UTC time,
* timezone information (specifically Daylight Savings Time (DST) rules, if any) and knowledge
@@ -2992,6 +2998,7 @@
}
return dstOffset;
}
+ // Android-added END: helper methods for time zone related calculation
/**
* Computes the fixed date under either the Gregorian or the
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index 9997b94..4f5394e 100644
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -973,10 +973,10 @@
HashtableEntry<Object, Object> entryStack = null;
synchronized (this) {
- // Write out the length, threshold, loadfactor
+ // Write out the threshold and loadFactor
s.defaultWriteObject();
- // Write out length, count of elements
+ // Write out the length and count of elements
s.writeInt(table.length);
s.writeInt(count);
@@ -1006,22 +1006,34 @@
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
- // Read in the length, threshold, and loadfactor
+ // Read in the threshold and loadFactor
s.defaultReadObject();
+ // Validate loadFactor (ignore threshold - it will be re-computed)
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new StreamCorruptedException("Illegal Load: " + loadFactor);
+
// Read the original length of the array and number of elements
int origlength = s.readInt();
int elements = s.readInt();
- // Compute new size with a bit of room 5% to grow but
- // no larger than the original size. Make the length
+ // Validate # of elements
+ if (elements < 0)
+ throw new StreamCorruptedException("Illegal # of Elements: " + elements);
+
+ // Clamp original length to be more than elements / loadFactor
+ // (this is the invariant enforced with auto-growth)
+ origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
+
+ // Compute new length with a bit of room 5% + 3 to grow but
+ // no larger than the clamped original length. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
- int length = (int)(elements * loadFactor) + (elements / 20) + 3;
+ int length = (int)((elements + elements / 20) / loadFactor) + 3;
+
if (length > elements && (length & 1) == 0)
length--;
- if (origlength > 0 && length > origlength)
- length = origlength;
+ length = Math.min(length, origlength);
table = new HashtableEntry<?,?>[length];
threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
count = 0;
@@ -1032,7 +1044,7 @@
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
- // synch could be eliminated for performance
+ // sync is eliminated for performance
reconstitutionPut(table, key, value);
}
}
@@ -1044,9 +1056,9 @@
*
* <p>This differs from the regular put method in several ways. No
* checking for rehashing is necessary since the number of elements
- * initially in the table is known. The modCount is not incremented
- * because we are creating a new instance. Also, no return value
- * is needed.
+ * initially in the table is known. The modCount is not incremented and
+ * there's no synchronization because we are creating a new instance.
+ * Also, no return value is needed.
*/
private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java
index 9c75a32..45949c6 100644
--- a/ojluni/src/main/java/java/util/Locale.java
+++ b/ojluni/src/main/java/java/util/Locale.java
@@ -886,7 +886,8 @@
*/
public static Locale getDefault() {
// do not synchronize this method - see 4071298
- return defaultLocale;
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ return NoImagePreloadHolder.defaultLocale;
}
/**
@@ -972,6 +973,8 @@
}
private static Locale initDefault(Locale.Category category) {
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ final Locale defaultLocale = NoImagePreloadHolder.defaultLocale;
return getInstance(
System.getProperty(category.languageKey, defaultLocale.getLanguage()),
System.getProperty(category.scriptKey, defaultLocale.getScript()),
@@ -1012,7 +1015,8 @@
public static synchronized void setDefault(Locale newLocale) {
setDefault(Category.DISPLAY, newLocale);
setDefault(Category.FORMAT, newLocale);
- defaultLocale = newLocale;
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ NoImagePreloadHolder.defaultLocale = newLocale;
// Android-added: Keep ICU state in sync with java.util.
ICU.setDefaultLocale(newLocale.toLanguageTag());
}
@@ -2184,7 +2188,10 @@
*/
private transient volatile int hashCodeValue = 0;
- private volatile static Locale defaultLocale = initDefault();
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ private static class NoImagePreloadHolder {
+ public volatile static Locale defaultLocale = initDefault();
+ }
private volatile static Locale defaultDisplayLocale = null;
private volatile static Locale defaultFormatLocale = null;
diff --git a/ojluni/src/main/java/java/util/Spliterator.java b/ojluni/src/main/java/java/util/Spliterator.java
index 18cb082..35322b3 100644
--- a/ojluni/src/main/java/java/util/Spliterator.java
+++ b/ojluni/src/main/java/java/util/Spliterator.java
@@ -125,7 +125,7 @@
* are encountered.
*
* @apiNote
- * <p>Spliterators, like {@code Iterators}s, are for traversing the elements of
+ * <p>Spliterators, like {@code Iterator}s, are for traversing the elements of
* a source. The {@code Spliterator} API was designed to support efficient
* parallel traversal in addition to sequential traversal, by supporting
* decomposition as well as single-element iteration. In addition, the
diff --git a/ojluni/src/main/java/java/util/logging/Logger.java b/ojluni/src/main/java/java/util/logging/Logger.java
index c8b76f1..8cf98fe 100644
--- a/ojluni/src/main/java/java/util/logging/Logger.java
+++ b/ojluni/src/main/java/java/util/logging/Logger.java
@@ -805,6 +805,7 @@
* @param level One of the message level identifiers, e.g., SEVERE
* @param msgSupplier A function, which when called, produces the
* desired log message
+ * @since 1.8
*/
public void log(Level level, Supplier<String> msgSupplier) {
if (!isLoggable(level)) {
diff --git a/ojluni/src/test/java/nio/file/TestUtil.java b/ojluni/src/test/java/nio/file/TestUtil.java
new file mode 100644
index 0000000..2d102fd
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/TestUtil.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+// Android-changed: Adapted from jdk/test/java/nio/file/TestUtil.java
+// Android-changed: Added package & made methods public
+package test.java.nio.file;
+
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Random;
+import java.io.IOException;
+
+public class TestUtil {
+ private TestUtil() {
+ }
+
+ static public Path createTemporaryDirectory(String where) throws IOException {
+ Path dir = FileSystems.getDefault().getPath(where);
+ return Files.createTempDirectory(dir, "name");
+ }
+
+ static public Path createTemporaryDirectory() throws IOException {
+ return Files.createTempDirectory("name");
+ }
+
+ static public void removeAll(Path dir) throws IOException {
+ Files.walkFileTree(dir, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ try {
+ Files.delete(file);
+ } catch (IOException x) {
+ System.err.format("Unable to delete %s: %s\n", file, x);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ try {
+ Files.delete(dir);
+ } catch (IOException x) {
+ System.err.format("Unable to delete %s: %s\n", dir, x);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ System.err.format("Unable to visit %s: %s\n", file, exc);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ static public void deleteUnchecked(Path file) {
+ try {
+ Files.delete(file);
+ } catch (IOException exc) {
+ System.err.format("Unable to delete %s: %s\n", file, exc);
+ }
+ }
+
+ /**
+ * Creates a directory tree in the given directory so that the total
+ * size of the path is more than 2k in size. This is used for long
+ * path tests on Windows.
+ */
+ static public Path createDirectoryWithLongPath(Path dir)
+ throws IOException
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i=0; i<240; i++) {
+ sb.append('A');
+ }
+ String name = sb.toString();
+ do {
+ dir = dir.resolve(name).resolve(".");
+ Files.createDirectory(dir);
+ } while (dir.toString().length() < 2048);
+ return dir;
+ }
+
+ /**
+ * Returns true if symbolic links are supported
+ */
+ static public boolean supportsLinks(Path dir) {
+ Path link = dir.resolve("testlink");
+ Path target = dir.resolve("testtarget");
+ try {
+ Files.createSymbolicLink(link, target);
+ Files.delete(link);
+ return true;
+ } catch (UnsupportedOperationException x) {
+ return false;
+ } catch (IOException x) {
+ return false;
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
new file mode 100644
index 0000000..7f0c17b
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 7076310
+ * @summary Test AclEntry.Builder setFlags and setPermissions with empty sets
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclEntry/EmptySet.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+
+/*
+ * Test for bug 7076310 "(file) AclEntry.Builder setFlags throws
+ * IllegalArgumentException if set argument is empty"
+ * The bug is only applies when the given Set is NOT an instance of EnumSet.
+ *
+ * The setPermissions method also has the same problem.
+ */
+// Android-changed: Renamed from "EmptySet"
+public class AclEntryEmptySetTest {
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() {
+ Set<AclEntryFlag> flags = new HashSet<>();
+ AclEntry.newBuilder().setFlags(flags);
+
+ Set<AclEntryPermission> perms = new HashSet<>();
+ AclEntry.newBuilder().setPermissions(perms);
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
new file mode 100644
index 0000000..262aa81
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333 6891404
+ * @summary Unit test for java.nio.file.attribute.AclFileAttribueView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+import static java.nio.file.attribute.AclEntryType.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryFlag.*;
+
+// Android-changed: Renamed from "Basic"
+public class AclFileAttributeViewTest {
+
+ static void printAcl(List<AclEntry> acl) {
+ for (AclEntry entry: acl) {
+ System.out.format(" %s%n", entry);
+ }
+ }
+
+ // sanity check read and writing ACL
+ static void testReadWrite(Path dir) throws IOException {
+ Path file = dir.resolve("foo");
+ if (Files.notExists(file))
+ Files.createFile(file);
+
+ AclFileAttributeView view =
+ Files.getFileAttributeView(file, AclFileAttributeView.class);
+
+ // print existing ACL
+ List<AclEntry> acl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+
+ // insert entry to grant owner read access
+ UserPrincipal owner = view.getOwner();
+ AclEntry entry = AclEntry.newBuilder()
+ .setType(ALLOW)
+ .setPrincipal(owner)
+ .setPermissions(READ_DATA, READ_ATTRIBUTES)
+ .build();
+ System.out.println(" -- insert (entry 0) --");
+ System.out.format(" %s%n", entry);
+ acl.add(0, entry);
+ view.setAcl(acl);
+
+ // re-ACL and check entry
+ List<AclEntry> newacl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+ if (!newacl.get(0).equals(entry)) {
+ throw new RuntimeException("Entry 0 is not expected");
+ }
+
+ // if PosixFileAttributeView then repeat test with OWNER@
+ if (Files.getFileStore(file).supportsFileAttributeView("posix")) {
+ owner = file.getFileSystem().getUserPrincipalLookupService()
+ .lookupPrincipalByName("OWNER@");
+ entry = AclEntry.newBuilder(entry).setPrincipal(owner).build();
+
+ System.out.println(" -- replace (entry 0) --");
+ System.out.format(" %s%n", entry);
+
+ acl.set(0, entry);
+ view.setAcl(acl);
+ newacl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+ if (!newacl.get(0).equals(entry)) {
+ throw new RuntimeException("Entry 0 is not expected");
+ }
+ }
+ }
+
+ static FileAttribute<List<AclEntry>> asAclAttribute(final List<AclEntry> acl) {
+ return new FileAttribute<List<AclEntry>>() {
+ public String name() { return "acl:acl"; }
+ public List<AclEntry> value() { return acl; }
+ };
+ }
+
+ static void assertEquals(List<AclEntry> actual, List<AclEntry> expected) {
+ if (!actual.equals(expected)) {
+ System.err.format("Actual: %s\n", actual);
+ System.err.format("Expected: %s\n", expected);
+ throw new RuntimeException("ACL not expected");
+ }
+ }
+
+ // sanity check create a file or directory with initial ACL
+ static void testCreateFile(Path dir) throws IOException {
+ UserPrincipal user = Files.getOwner(dir);
+ AclFileAttributeView view;
+
+ // create file with initial ACL
+ System.out.println("-- create file with initial ACL --");
+ Path file = dir.resolve("gus");
+ List<AclEntry> fileAcl = Arrays.asList(
+ AclEntry.newBuilder()
+ .setType(AclEntryType.ALLOW)
+ .setPrincipal(user)
+ .setPermissions(SYNCHRONIZE, READ_DATA, WRITE_DATA,
+ READ_ATTRIBUTES, READ_ACL, WRITE_ATTRIBUTES, DELETE)
+ .build());
+ Files.createFile(file, asAclAttribute(fileAcl));
+ view = Files.getFileAttributeView(file, AclFileAttributeView.class);
+ assertEquals(view.getAcl(), fileAcl);
+
+ // create directory with initial ACL
+ System.out.println("-- create directory with initial ACL --");
+ Path subdir = dir.resolve("stuff");
+ List<AclEntry> dirAcl = Arrays.asList(
+ AclEntry.newBuilder()
+ .setType(AclEntryType.ALLOW)
+ .setPrincipal(user)
+ .setPermissions(SYNCHRONIZE, ADD_FILE, DELETE)
+ .build(),
+ AclEntry.newBuilder(fileAcl.get(0))
+ .setFlags(FILE_INHERIT)
+ .build());
+ Files.createDirectory(subdir, asAclAttribute(dirAcl));
+ view = Files.getFileAttributeView(subdir, AclFileAttributeView.class);
+ assertEquals(view.getAcl(), dirAcl);
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // use work directory rather than system temporary directory to
+ // improve chances that ACLs are supported
+ Path dir = Paths.get("./work" + new Random().nextInt());
+ Files.createDirectory(dir);
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("acl")) {
+ System.out.println("ACLs not supported - test skipped!");
+ return;
+ }
+ testReadWrite(dir);
+
+ // only currently feasible on Windows
+ if (System.getProperty("os.name").startsWith("Windows"))
+ testCreateFile(dir);
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
new file mode 100644
index 0000000..b393981
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 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
+ * @bug 8011536
+ * @summary Basic test for creationTime attribute on platforms/file systems
+ * that support it.
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.attribute.*;
+import java.time.Instant;
+import java.io.IOException;
+
+// Android-changed: Renamed from "CreationTime"
+public class BasicFileAttributeViewCreationTimeTest {
+
+ private static final java.io.PrintStream err = System.err;
+
+ /**
+ * Reads the creationTime attribute
+ */
+ private static FileTime creationTime(Path file) throws IOException {
+ return Files.readAttributes(file, BasicFileAttributes.class).creationTime();
+ }
+
+ /**
+ * Sets the creationTime attribute
+ */
+ private static void setCreationTime(Path file, FileTime time) throws IOException {
+ BasicFileAttributeView view =
+ Files.getFileAttributeView(file, BasicFileAttributeView.class);
+ view.setTimes(null, null, time);
+ }
+
+ static void test(Path top) throws IOException {
+ Path file = Files.createFile(top.resolve("foo"));
+
+ /**
+ * Check that creationTime reported
+ */
+ FileTime creationTime = creationTime(file);
+ Instant now = Instant.now();
+ if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) {
+ err.println("File creation time reported as: " + creationTime);
+ throw new RuntimeException("Expected to be close to: " + now);
+ }
+
+ /**
+ * Is the creationTime attribute supported here?
+ */
+ boolean supportsCreationTimeRead = false;
+ boolean supportsCreationTimeWrite = false;
+ String os = System.getProperty("os.name");
+ if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
+ supportsCreationTimeRead = true;
+ } else if (os.startsWith("Windows")) {
+ String type = Files.getFileStore(file).type();
+ if (type.equals("NTFS") || type.equals("FAT")) {
+ supportsCreationTimeRead = true;
+ supportsCreationTimeWrite = true;
+ }
+ }
+
+ /**
+ * If the creation-time attribute is supported then change the file's
+ * last modified and check that it doesn't change the creation-time.
+ */
+ if (supportsCreationTimeRead) {
+ // change modified time by +1 hour
+ Instant plusHour = Instant.now().plusSeconds(60L * 60L);
+ Files.setLastModifiedTime(file, FileTime.from(plusHour));
+ FileTime current = creationTime(file);
+ if (!current.equals(creationTime))
+ throw new RuntimeException("Creation time should not have changed");
+ }
+
+ /**
+ * If the creation-time attribute is supported and can be changed then
+ * check that the change is effective.
+ */
+ if (supportsCreationTimeWrite) {
+ // change creation time by -1 hour
+ Instant minusHour = Instant.now().minusSeconds(60L * 60L);
+ creationTime = FileTime.from(minusHour);
+ setCreationTime(file, creationTime);
+ FileTime current = creationTime(file);
+ if (Math.abs(creationTime.toMillis()-current.toMillis()) > 1000L)
+ throw new RuntimeException("Creation time not changed");
+ }
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ test(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
new file mode 100644
index 0000000..82f4e0e
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.BasicFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.*;
+
+// Android-changed: Renamed from "Basic"
+public class BasicFileAttributeViewTest {
+
+ static void check(boolean okay, String msg) {
+ if (!okay)
+ throw new RuntimeException(msg);
+ }
+
+ static void checkAttributesOfDirectory(Path dir)
+ throws IOException
+ {
+ BasicFileAttributes attrs = Files.readAttributes(dir, BasicFileAttributes.class);
+ check(attrs.isDirectory(), "is a directory");
+ check(!attrs.isRegularFile(), "is not a regular file");
+ check(!attrs.isSymbolicLink(), "is not a link");
+ check(!attrs.isOther(), "is not other");
+
+ // last-modified-time should match java.io.File in seconds
+ File f = new File(dir.toString());
+ check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+ "last-modified time should be the same");
+ }
+
+ static void checkAttributesOfFile(Path dir, Path file)
+ throws IOException
+ {
+ BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
+ check(attrs.isRegularFile(), "is a regular file");
+ check(!attrs.isDirectory(), "is not a directory");
+ check(!attrs.isSymbolicLink(), "is not a link");
+ check(!attrs.isOther(), "is not other");
+
+ // size and last-modified-time should match java.io.File in seconds
+ File f = new File(file.toString());
+ check(f.length() == attrs.size(), "size should be the same");
+ check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+ "last-modified time should be the same");
+
+ // copy last-modified time from directory to file,
+ // re-read attribtues, and check they match
+ BasicFileAttributeView view =
+ Files.getFileAttributeView(file, BasicFileAttributeView.class);
+ BasicFileAttributes dirAttrs = Files.readAttributes(dir, BasicFileAttributes.class);
+ view.setTimes(dirAttrs.lastModifiedTime(), null, null);
+
+ attrs = view.readAttributes();
+ check(attrs.lastModifiedTime().equals(dirAttrs.lastModifiedTime()),
+ "last-modified time should be equal");
+
+ // security tests
+ check (!(attrs instanceof PosixFileAttributes),
+ "should not be able to cast to PosixFileAttributes");
+ }
+
+ static void checkAttributesOfLink(Path link)
+ throws IOException
+ {
+ BasicFileAttributes attrs =
+ Files.readAttributes(link, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ check(attrs.isSymbolicLink(), "is a link");
+ check(!attrs.isDirectory(), "is a directory");
+ check(!attrs.isRegularFile(), "is not a regular file");
+ check(!attrs.isOther(), "is not other");
+ }
+
+ static void attributeReadWriteTests(Path dir)
+ throws IOException
+ {
+ // create file
+ Path file = dir.resolve("foo");
+ try (OutputStream out = Files.newOutputStream(file)) {
+ out.write("this is not an empty file".getBytes("UTF-8"));
+ }
+
+ // check attributes of directory and file
+ checkAttributesOfDirectory(dir);
+ checkAttributesOfFile(dir, file);
+
+ // symbolic links may be supported
+ Path link = dir.resolve("link");
+ try {
+ Files.createSymbolicLink(link, file);
+ } catch (UnsupportedOperationException x) {
+ return;
+ } catch (IOException x) {
+ return;
+ }
+ checkAttributesOfLink(link);
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ attributeReadWriteTests(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
new file mode 100644
index 0000000..8386a31
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.DosFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/DosFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class DosFileAttributeViewTest {
+
+ static void check(boolean okay) {
+ if (!okay)
+ throw new RuntimeException("Test failed");
+ }
+
+ // exercise each setter/getter method, leaving all attributes unset
+ static void testAttributes(DosFileAttributeView view) throws IOException {
+ view.setReadOnly(true);
+ check(view.readAttributes().isReadOnly());
+ view.setReadOnly(false);
+ check(!view.readAttributes().isReadOnly());
+ view.setHidden(true);
+ check(view.readAttributes().isHidden());
+ view.setHidden(false);
+ check(!view.readAttributes().isHidden());
+ view.setArchive(true);
+ check(view.readAttributes().isArchive());
+ view.setArchive(false);
+ check(!view.readAttributes().isArchive());
+ view.setSystem(true);
+ check(view.readAttributes().isSystem());
+ view.setSystem(false);
+ check(!view.readAttributes().isSystem());
+ }
+
+ // set the value of all attributes
+ static void setAll(DosFileAttributeView view, boolean value)
+ throws IOException
+ {
+ view.setReadOnly(value);
+ view.setHidden(value);
+ view.setArchive(value);
+ view.setSystem(value);
+ }
+
+ // read and write FAT attributes
+ static void readWriteTests(Path dir) throws IOException {
+
+ // create "foo" and test that we can read/write each FAT attribute
+ Path file = Files.createFile(dir.resolve("foo"));
+ try {
+ testAttributes(Files.getFileAttributeView(file, DosFileAttributeView.class));
+
+ // Following tests use a symbolic link so skip if not supported
+ if (!TestUtil.supportsLinks(dir))
+ return;
+
+ Path link = dir.resolve("link");
+ Files.createSymbolicLink(link, file);
+
+ // test following links
+ testAttributes(Files.getFileAttributeView(link, DosFileAttributeView.class));
+
+ // test not following links
+ try {
+ try {
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+ } catch (IOException x) {
+ // access to link attributes not supported
+ return;
+ }
+
+ // set all attributes on link
+ // run test on target of link (which leaves them all un-set)
+ // check that attributes of link remain all set
+ setAll(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), true);
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class));
+ DosFileAttributes attrs =
+ Files.getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS)
+ .readAttributes();
+ check(attrs.isReadOnly());
+ check(attrs.isHidden());
+ check(attrs.isArchive());
+ check(attrs.isSystem());
+ setAll(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), false);
+
+ // set all attributes on target
+ // run test on link (which leaves them all un-set)
+ // check that attributes of target remain all set
+ setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), true);
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+ attrs = Files.getFileAttributeView(link, DosFileAttributeView.class).readAttributes();
+ check(attrs.isReadOnly());
+ check(attrs.isHidden());
+ check(attrs.isArchive());
+ check(attrs.isSystem());
+ setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), false);
+ } finally {
+ TestUtil.deleteUnchecked(link);
+ }
+ } finally {
+ TestUtil.deleteUnchecked(file);
+ }
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+
+ try {
+ // skip test if DOS file attributes not supported
+ if (!Files.getFileStore(dir).supportsFileAttributeView("dos")) {
+ System.out.println("DOS file attribute not supported.");
+ return;
+ }
+ readWriteTests(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
new file mode 100644
index 0000000..43c5d61
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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
+ * @bug 6844313 8011647
+ * @summary Unit test for java.nio.file.FileTime
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/FileTime/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.*;
+import java.util.Random;
+import java.util.EnumSet;
+
+// Android-changed: Renamed from "Basic"
+public class FileTimeTest {
+
+ static final Random rand = new Random();
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() {
+ long now = System.currentTimeMillis();
+ long tomorrowInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) + 1;
+ long yesterdayInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) - 1;
+
+ Instant nowInstant = Instant.ofEpochMilli(now);
+
+ // equals
+ eq(now, MILLISECONDS, now, MILLISECONDS);
+ eq(now, MILLISECONDS, now*1000L, MICROSECONDS);
+ neq(now, MILLISECONDS, 0, MILLISECONDS);
+ neq(now, MILLISECONDS, 0, MICROSECONDS);
+
+ eq(nowInstant, now, MILLISECONDS);
+ eq(nowInstant, now*1000L, MICROSECONDS);
+ neq(nowInstant, 0, MILLISECONDS);
+ neq(nowInstant, 0, MICROSECONDS);
+
+ // compareTo
+ cmp(now, MILLISECONDS, now, MILLISECONDS, 0);
+ cmp(now, MILLISECONDS, now*1000L, MICROSECONDS, 0);
+ cmp(now, MILLISECONDS, now-1234, MILLISECONDS, 1);
+ cmp(now, MILLISECONDS, now+1234, MILLISECONDS, -1);
+
+ cmp(tomorrowInDays, DAYS, now, MILLISECONDS, 1);
+ cmp(now, MILLISECONDS, tomorrowInDays, DAYS, -1);
+ cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+ cmp(now, MILLISECONDS, yesterdayInDays, DAYS, 1);
+ cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+
+ cmp(Long.MAX_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, 1);
+ cmp(Long.MAX_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, 1);
+ cmp(Long.MIN_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, -1);
+ cmp(Long.MIN_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, -1);
+
+ cmp(Instant.MIN, Long.MIN_VALUE, DAYS, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, HOURS, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, MINUTES, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 1, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 100, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond(), SECONDS, 0);
+
+ cmp(Instant.MAX, Long.MAX_VALUE, DAYS, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, HOURS, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, MINUTES, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 1, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 100, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond(), SECONDS, 0);
+
+ cmp(nowInstant, now, MILLISECONDS, 0);
+ cmp(nowInstant, now*1000L, MICROSECONDS, 0);
+ cmp(nowInstant, now-1234, MILLISECONDS, 1);
+ cmp(nowInstant, now+1234, MILLISECONDS, -1);
+ cmp(nowInstant, tomorrowInDays, DAYS, -1);
+ cmp(nowInstant, yesterdayInDays, DAYS, 1);
+
+ // to(TimeUnit)
+ to(MILLISECONDS.convert(1, DAYS) - 1, MILLISECONDS);
+ to(MILLISECONDS.convert(1, DAYS) + 0, MILLISECONDS);
+ to(MILLISECONDS.convert(1, DAYS) + 1, MILLISECONDS);
+ to(1, MILLISECONDS);
+ to(0, MILLISECONDS);
+ to(1, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) - 1, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) + 0, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) + 1, MILLISECONDS);
+ for (TimeUnit unit: TimeUnit.values()) {
+ for (int i=0; i<100; i++) { to(rand.nextLong(), unit); }
+ to(Long.MIN_VALUE, unit);
+ to(Long.MAX_VALUE, unit);
+ }
+
+ // toInstant()
+ int N = 1000;
+ for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+ for (int i = 0; i < N; i++) {
+ long value = rand.nextLong();
+ FileTime ft = FileTime.from(value, unit);
+ Instant instant = ft.toInstant();
+ if (instant != Instant.MIN && instant != Instant.MAX) {
+ eqTime(value, unit, instant);
+ }
+ }
+ }
+ for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+ long value = Long.MIN_VALUE;
+ FileTime ft = FileTime.from(value, unit);
+ Instant instant = ft.toInstant();
+ if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+ eqTime(value, unit, instant);
+ } else if (!instant.equals(Instant.MIN)) {
+ throw new RuntimeException("should overflow to MIN");
+ }
+ value = Long.MAX_VALUE;
+ ft = FileTime.from(value, unit);
+ instant = ft.toInstant();
+ if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+ eqTime(value, unit, instant);
+ } else if (!instant.equals(Instant.MAX)) {
+ throw new RuntimeException("should overflow to MAX");
+ }
+ }
+
+ // from(Instant)
+ final long MAX_SECOND = 31556889864403199L;
+ for (int i = 0; i < N; i++) {
+ long v = rand.nextLong();
+ long secs = v % MAX_SECOND;
+ Instant instant = Instant.ofEpochSecond(secs, rand.nextInt(1000_000_000));
+ FileTime ft = FileTime.from(instant);
+ if (!ft.toInstant().equals(instant) || ft.to(SECONDS) != secs) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ long millis = v;
+ instant = Instant.ofEpochMilli(millis);
+ ft = FileTime.from(instant);
+ if (!ft.toInstant().equals(instant) ||
+ ft.toMillis() != instant.toEpochMilli()) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ long nanos = v;
+ ft = FileTime.from(nanos, NANOSECONDS);
+ secs = nanos / 1000_000_000;
+ nanos = nanos % 1000_000_000;
+ instant = Instant.ofEpochSecond(secs, nanos);
+ if (!ft.equals(FileTime.from(instant))) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ }
+
+ // toString
+ ts(1L, DAYS, "1970-01-02T00:00:00Z");
+ ts(1L, HOURS, "1970-01-01T01:00:00Z");
+ ts(1L, MINUTES, "1970-01-01T00:01:00Z");
+ ts(1L, SECONDS, "1970-01-01T00:00:01Z");
+ ts(1L, MILLISECONDS, "1970-01-01T00:00:00.001Z");
+ ts(1L, MICROSECONDS, "1970-01-01T00:00:00.000001Z");
+ ts(1L, NANOSECONDS, "1970-01-01T00:00:00.000000001Z");
+ ts(999999999L, NANOSECONDS, "1970-01-01T00:00:00.999999999Z");
+ ts(9999999999L, NANOSECONDS, "1970-01-01T00:00:09.999999999Z");
+
+ ts(-1L, DAYS, "1969-12-31T00:00:00Z");
+ ts(-1L, HOURS, "1969-12-31T23:00:00Z");
+ ts(-1L, MINUTES, "1969-12-31T23:59:00Z");
+ ts(-1L, SECONDS, "1969-12-31T23:59:59Z");
+ ts(-1L, MILLISECONDS, "1969-12-31T23:59:59.999Z");
+ ts(-1L, MICROSECONDS, "1969-12-31T23:59:59.999999Z");
+ ts(-1L, NANOSECONDS, "1969-12-31T23:59:59.999999999Z");
+ ts(-999999999L, NANOSECONDS, "1969-12-31T23:59:59.000000001Z");
+ ts(-9999999999L, NANOSECONDS, "1969-12-31T23:59:50.000000001Z");
+
+ ts(-62135596799999L, MILLISECONDS, "0001-01-01T00:00:00.001Z");
+ ts(-62135596800000L, MILLISECONDS, "0001-01-01T00:00:00Z");
+ ts(-62135596800001L, MILLISECONDS, "-0001-12-31T23:59:59.999Z");
+
+ ts(253402300799999L, MILLISECONDS, "9999-12-31T23:59:59.999Z");
+ ts(-377642044800001L, MILLISECONDS, "-9999-12-31T23:59:59.999Z");
+
+ // NTFS epoch in usec.
+ ts(-11644473600000000L, MICROSECONDS, "1601-01-01T00:00:00Z");
+
+ ts(Instant.MIN, "-1000000001-01-01T00:00:00Z");
+ ts(Instant.MAX, "1000000000-12-31T23:59:59.999999999Z");
+
+ try {
+ FileTime.from(0L, null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+ try {
+ FileTime.from(null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+
+ FileTime time = FileTime.fromMillis(now);
+ if (time.equals(null))
+ throw new RuntimeException("should not be equal to null");
+ try {
+ time.compareTo(null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+
+ // Instant + toMilli() overflow
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.MAX).toMillis());
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+ .toMillis());
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.MIN).toMillis());
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+ .toMillis());
+
+ // Instant + to(TimeUnit) overflow
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+ .to(MILLISECONDS));
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000,
+ MILLISECONDS.toNanos(1000)))
+ .to(MILLISECONDS));
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+ .to(MILLISECONDS));
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000,
+ -MILLISECONDS.toNanos(1)))
+ .to(MILLISECONDS));
+ }
+
+ static void overflow(long minmax, long v) {
+ if (v != minmax)
+ throw new RuntimeException("saturates to Long.MIN/MAX_VALUE expected");
+ }
+
+ static void cmp(long v1, TimeUnit u1, long v2, TimeUnit u2, int expected) {
+ int result = FileTime.from(v1, u1).compareTo(FileTime.from(v2, u2));
+ if (result != expected)
+ throw new RuntimeException("unexpected order");
+ }
+
+ static void cmp(Instant ins, long v2, TimeUnit u2, int expected) {
+ int result = FileTime.from(ins).compareTo(FileTime.from(v2, u2));
+ if (result != expected)
+ throw new RuntimeException("unexpected order");
+ }
+
+ static void eq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(v1, u1);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (!t1.equals(t2))
+ throw new RuntimeException("not equal");
+ if (t1.hashCode() != t2.hashCode())
+ throw new RuntimeException("hashCodes should be equal");
+ }
+
+ static void eq(Instant ins, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(ins);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (!t1.equals(t2))
+ throw new RuntimeException("not equal");
+ if (t1.hashCode() != t2.hashCode())
+ throw new RuntimeException("hashCodes should be equal");
+ }
+
+ static void eqTime(long value, TimeUnit unit, Instant instant) {
+ long secs = SECONDS.convert(value, unit);
+ long nanos = NANOSECONDS.convert(value - unit.convert(secs, SECONDS), unit);
+ if (nanos < 0) { // normalize nanoOfSecond to positive
+ secs -= 1;
+ nanos += 1000_000_000;
+ }
+ if (secs != instant.getEpochSecond() || (int)nanos != instant.getNano()) {
+ System.err.println(" ins=" + instant);
+ throw new RuntimeException("ft and instant are not the same time point");
+ }
+ }
+
+ static void neq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(v1, u1);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (t1.equals(t2))
+ throw new RuntimeException("should not be equal");
+ }
+
+ static void neq(Instant ins, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(ins);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (t1.equals(t2))
+ throw new RuntimeException("should not be equal");
+ }
+
+ static void to(long v, TimeUnit unit) {
+ FileTime t = FileTime.from(v, unit);
+ for (TimeUnit u: TimeUnit.values()) {
+ long result = t.to(u);
+ long expected = u.convert(v, unit);
+ if (result != expected) {
+ throw new RuntimeException("unexpected result");
+ }
+ }
+ }
+
+ static void ts(long v, TimeUnit unit, String expected) {
+ String result = FileTime.from(v, unit).toString();
+ if (!result.equals(expected)) {
+ System.err.format("FileTime.from(%d, %s).toString() failed\n", v, unit);
+ System.err.format("Expected: %s\n", expected);
+ System.err.format(" Got: %s\n", result);
+ throw new RuntimeException();
+ }
+ }
+
+ static void ts(Instant instant, String expected) {
+ String result = FileTime.from(instant).toString();
+ if (!result.equals(expected)) {
+ System.err.format("FileTime.from(%s).toString() failed\n", instant);
+ System.err.format("Expected: %s\n", expected);
+ System.err.format(" Got: %s\n", result);
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
new file mode 100644
index 0000000..7825837
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.PosixFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Unit test for PosixFileAttributeView, passing silently if this attribute
+ * view is not available.
+ */
+
+// Android-changed: Renamed from "Basic"
+public class PosixFileAttributeViewTest {
+
+ /**
+ * Use view to update permission to the given mode and check that the
+ * permissions have been updated.
+ */
+ static void testPermissions(Path file, String mode) throws IOException {
+ System.out.format("change mode: %s\n", mode);
+ Set<PosixFilePermission> perms = PosixFilePermissions.fromString(mode);
+
+ // change permissions and re-read them.
+ Files.setPosixFilePermissions(file, perms);
+ Set<PosixFilePermission> current = Files.getPosixFilePermissions(file);
+ if (!current.equals(perms)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(current) + ", expected: " +
+ PosixFilePermissions.toString(perms));
+ }
+
+ // repeat test using setAttribute/getAttribute
+ Files.setAttribute(file, "posix:permissions", perms);
+ current = (Set<PosixFilePermission>)Files.getAttribute(file, "posix:permissions");
+ if (!current.equals(perms)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(current) + ", expected: " +
+ PosixFilePermissions.toString(perms));
+ }
+ }
+
+ /**
+ * Check that the actual permissions of a file match or make it more
+ * secure than requested
+ */
+ static void checkSecure(Set<PosixFilePermission> requested,
+ Set<PosixFilePermission> actual)
+ {
+ for (PosixFilePermission perm: actual) {
+ if (!requested.contains(perm)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(actual) + ", requested: " +
+ PosixFilePermissions.toString(requested) +
+ " - file is less secure than requested");
+ }
+ }
+ }
+
+ /**
+ * Create file with given mode and check that the file is created with a
+ * mode that is not less secure
+ */
+ static void createWithPermissions(Path file,
+ String mode)
+ throws IOException
+ {
+ Set<PosixFilePermission> requested = PosixFilePermissions.fromString(mode);
+ FileAttribute<Set<PosixFilePermission>> attr =
+ PosixFilePermissions.asFileAttribute(requested);
+ System.out.format("create file with mode: %s\n", mode);
+ Files.createFile(file, attr);
+ try {
+ checkSecure(requested,
+ Files.getFileAttributeView(file, PosixFileAttributeView.class)
+ .readAttributes()
+ .permissions());
+ } finally {
+ Files.delete(file);
+ }
+
+ System.out.format("create directory with mode: %s\n", mode);
+ Files.createDirectory(file, attr);
+ try {
+ checkSecure(requested,
+ Files.getFileAttributeView(file, PosixFileAttributeView.class)
+ .readAttributes()
+ .permissions());
+ } finally {
+ Files.delete(file);
+ }
+ }
+
+ /**
+ * Test the setPermissions/permissions methods.
+ */
+ static void permissionTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Permission Tests --");
+
+ // create file and test updating and reading its permissions
+ Path file = dir.resolve("foo");
+ System.out.format("create %s\n", file);
+ Files.createFile(file);
+ try {
+ // get initial permissions so that we can restore them later
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(file, PosixFileAttributeView.class);
+ Set<PosixFilePermission> save = view.readAttributes()
+ .permissions();
+
+ // test various modes
+ try {
+ testPermissions(file, "---------");
+ testPermissions(file, "r--------");
+ testPermissions(file, "-w-------");
+ testPermissions(file, "--x------");
+ testPermissions(file, "rwx------");
+ testPermissions(file, "---r-----");
+ testPermissions(file, "----w----");
+ testPermissions(file, "-----x---");
+ testPermissions(file, "---rwx---");
+ testPermissions(file, "------r--");
+ testPermissions(file, "-------w-");
+ testPermissions(file, "--------x");
+ testPermissions(file, "------rwx");
+ testPermissions(file, "r--r-----");
+ testPermissions(file, "r--r--r--");
+ testPermissions(file, "rw-rw----");
+ testPermissions(file, "rwxrwx---");
+ testPermissions(file, "rw-rw-r--");
+ testPermissions(file, "r-xr-x---");
+ testPermissions(file, "r-xr-xr-x");
+ testPermissions(file, "rwxrwxrwx");
+ } finally {
+ view.setPermissions(save);
+ }
+ } finally {
+ Files.delete(file);
+ }
+
+ // create link (to file that doesn't exist) and test reading of
+ // permissions
+ if (TestUtil.supportsLinks(dir)) {
+ Path link = dir.resolve("link");
+ System.out.format("create link %s\n", link);
+ Files.createSymbolicLink(link, file);
+ try {
+ PosixFileAttributes attrs =
+ Files.getFileAttributeView(link,
+ PosixFileAttributeView.class,
+ NOFOLLOW_LINKS)
+ .readAttributes();
+ if (!attrs.isSymbolicLink()) {
+ throw new RuntimeException("not a link");
+ }
+ } finally {
+ Files.delete(link);
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test creating a file and directory with initial permissios
+ */
+ static void createTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Create Tests --");
+
+ Path file = dir.resolve("foo");
+
+ createWithPermissions(file, "---------");
+ createWithPermissions(file, "r--------");
+ createWithPermissions(file, "-w-------");
+ createWithPermissions(file, "--x------");
+ createWithPermissions(file, "rwx------");
+ createWithPermissions(file, "---r-----");
+ createWithPermissions(file, "----w----");
+ createWithPermissions(file, "-----x---");
+ createWithPermissions(file, "---rwx---");
+ createWithPermissions(file, "------r--");
+ createWithPermissions(file, "-------w-");
+ createWithPermissions(file, "--------x");
+ createWithPermissions(file, "------rwx");
+ createWithPermissions(file, "r--r-----");
+ createWithPermissions(file, "r--r--r--");
+ createWithPermissions(file, "rw-rw----");
+ createWithPermissions(file, "rwxrwx---");
+ createWithPermissions(file, "rw-rw-r--");
+ createWithPermissions(file, "r-xr-x---");
+ createWithPermissions(file, "r-xr-xr-x");
+ createWithPermissions(file, "rwxrwxrwx");
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test setOwner/setGroup methods - this test simply exercises the
+ * methods to avoid configuration.
+ */
+ static void ownerTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Owner Tests --");
+
+ Path file = dir.resolve("gus");
+ System.out.format("create %s\n", file);
+
+ Files.createFile(file);
+ try {
+
+ // read attributes of directory to get owner/group
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(file, PosixFileAttributeView.class);
+ PosixFileAttributes attrs = view.readAttributes();
+
+ // set to existing owner/group
+ view.setOwner(attrs.owner());
+ view.setGroup(attrs.group());
+
+ // repeat test using set/getAttribute
+ UserPrincipal owner = (UserPrincipal)Files.getAttribute(file, "posix:owner");
+ Files.setAttribute(file, "posix:owner", owner);
+ UserPrincipal group = (UserPrincipal)Files.getAttribute(file, "posix:group");
+ Files.setAttribute(file, "posix:group", group);
+
+ } finally {
+ Files.delete(file);
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test the lookupPrincipalByName/lookupPrincipalByGroupName methods
+ */
+ static void lookupPrincipalTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Lookup UserPrincipal Tests --");
+
+ UserPrincipalLookupService lookupService = dir.getFileSystem()
+ .getUserPrincipalLookupService();
+
+ // read attributes of directory to get owner/group
+ PosixFileAttributes attrs = Files.readAttributes(dir, PosixFileAttributes.class);
+
+ // lookup owner and check it matches file's owner
+ System.out.format("lookup: %s\n", attrs.owner().getName());
+ try {
+ UserPrincipal owner = lookupService.lookupPrincipalByName(attrs.owner().getName());
+ if (owner instanceof GroupPrincipal)
+ throw new RuntimeException("owner is a group?");
+ if (!owner.equals(attrs.owner()))
+ throw new RuntimeException("owner different from file owner");
+ } catch (UserPrincipalNotFoundException x) {
+ System.out.println("user not found - test skipped");
+ }
+
+ // lookup group and check it matches file's group-owner
+ System.out.format("lookup group: %s\n", attrs.group().getName());
+ try {
+ GroupPrincipal group = lookupService.lookupPrincipalByGroupName(attrs.group().getName());
+ if (!group.equals(attrs.group()))
+ throw new RuntimeException("group different from file group-owner");
+ } catch (UserPrincipalNotFoundException x) {
+ System.out.println("group not found - test skipped");
+ }
+
+ // test that UserPrincipalNotFoundException is thrown
+ String invalidPrincipal = "scumbag99";
+ try {
+ System.out.format("lookup: %s\n", invalidPrincipal);
+ lookupService.lookupPrincipalByName(invalidPrincipal);
+ throw new RuntimeException("'" + invalidPrincipal + "' is a valid user?");
+ } catch (UserPrincipalNotFoundException x) {
+ }
+ try {
+ System.out.format("lookup group: %s\n", invalidPrincipal);
+ lookupService.lookupPrincipalByGroupName("idonotexist");
+ throw new RuntimeException("'" + invalidPrincipal + "' is a valid group?");
+ } catch (UserPrincipalNotFoundException x) {
+ }
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test various exceptions are thrown as expected
+ */
+ @SuppressWarnings("unchecked")
+ static void exceptionsTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Exceptions --");
+
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(dir,PosixFileAttributeView.class);
+
+ // NullPointerException
+ try {
+ view.setOwner(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ view.setGroup(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+
+ UserPrincipalLookupService lookupService = dir.getFileSystem()
+ .getUserPrincipalLookupService();
+ try {
+ lookupService.lookupPrincipalByName(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ lookupService.lookupPrincipalByGroupName(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ view.setPermissions(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ Set<PosixFilePermission> perms = new HashSet<>();
+ perms.add(null);
+ view.setPermissions(perms);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+
+ // ClassCastException
+ try {
+ Set perms = new HashSet(); // raw type
+ perms.add(new Object());
+ view.setPermissions(perms);
+ throw new RuntimeException("ClassCastException not thrown");
+ } catch (ClassCastException x) {
+ }
+
+ System.out.println("OKAY");
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("posix")) {
+ System.out.println("PosixFileAttributeView not supported");
+ return;
+ }
+
+ permissionTests(dir);
+ createTests(dir);
+ ownerTests(dir);
+ lookupPrincipalTests(dir);
+ exceptionsTests(dir);
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
new file mode 100644
index 0000000..6fb3c78
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Random;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class UserDefinedFileAttributeViewTest {
+
+ private static Random rand = new Random();
+
+ private static final String ATTR_NAME = "mime_type";
+ private static final String ATTR_VALUE = "text/plain";
+ private static final String ATTR_VALUE2 = "text/html";
+
+ static interface Task {
+ void run() throws Exception;
+ }
+
+ static void tryCatch(Class<? extends Throwable> ex, Task task) {
+ boolean caught = false;
+ try {
+ task.run();
+ } catch (Throwable x) {
+ if (ex.isAssignableFrom(x.getClass())) {
+ caught = true;
+ } else {
+ throw new RuntimeException(x);
+ }
+ }
+ if (!caught)
+ throw new RuntimeException(ex.getName() + " expected");
+ }
+
+ static void expectNullPointerException(Task task) {
+ tryCatch(NullPointerException.class, task);
+ }
+
+ static boolean hasAttribute(UserDefinedFileAttributeView view, String attr)
+ throws IOException
+ {
+ for (String name: view.list()) {
+ if (name.equals(ATTR_NAME))
+ return true;
+ }
+ return false;
+ }
+
+ static void test(Path file, LinkOption... options) throws IOException {
+ final UserDefinedFileAttributeView view =
+ Files.getFileAttributeView(file, UserDefinedFileAttributeView.class, options);
+ ByteBuffer buf = rand.nextBoolean() ?
+ ByteBuffer.allocate(100) : ByteBuffer.allocateDirect(100);
+
+ // Test: write
+ buf.put(ATTR_VALUE.getBytes()).flip();
+ int size = buf.remaining();
+ int nwrote = view.write(ATTR_NAME, buf);
+ if (nwrote != size)
+ throw new RuntimeException("Unexpected number of bytes written");
+
+ // Test: size
+ if (view.size(ATTR_NAME) != size)
+ throw new RuntimeException("Unexpected size");
+
+ // Test: read
+ buf.clear();
+ int nread = view.read(ATTR_NAME, buf);
+ if (nread != size)
+ throw new RuntimeException("Unexpected number of bytes read");
+ buf.flip();
+ String value = Charset.defaultCharset().decode(buf).toString();
+ if (!value.equals(ATTR_VALUE))
+ throw new RuntimeException("Unexpected attribute value");
+
+ // Test: read with insufficient space
+ tryCatch(IOException.class, new Task() {
+ public void run() throws IOException {
+ view.read(ATTR_NAME, ByteBuffer.allocateDirect(1));
+ }});
+
+ // Test: replace value
+ buf.clear();
+ buf.put(ATTR_VALUE2.getBytes()).flip();
+ size = buf.remaining();
+ view.write(ATTR_NAME, buf);
+ if (view.size(ATTR_NAME) != size)
+ throw new RuntimeException("Unexpected size");
+
+ // Test: list
+ if (!hasAttribute(view, ATTR_NAME))
+ throw new RuntimeException("Attribute name not in list");
+
+ // Test: delete
+ view.delete(ATTR_NAME);
+ if (hasAttribute(view, ATTR_NAME))
+ throw new RuntimeException("Attribute name in list");
+
+ // Test: dynamic access
+ String name = "user:" + ATTR_NAME;
+ byte[] valueAsBytes = ATTR_VALUE.getBytes();
+ Files.setAttribute(file, name, valueAsBytes);
+ byte[] actualAsBytes = (byte[])Files.getAttribute(file, name);
+ if (!Arrays.equals(valueAsBytes, actualAsBytes))
+ throw new RuntimeException("Unexpected attribute value");
+ Map<String,?> map = Files.readAttributes(file, name);
+ if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+ throw new RuntimeException("Unexpected attribute value");
+ map = Files.readAttributes(file, "user:*");
+ if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+ throw new RuntimeException("Unexpected attribute value");
+ }
+
+ static void miscTests(final Path file) throws IOException {
+ final UserDefinedFileAttributeView view =
+ Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
+ view.write(ATTR_NAME, ByteBuffer.wrap(ATTR_VALUE.getBytes()));
+
+ // NullPointerException
+ final ByteBuffer buf = ByteBuffer.allocate(100);
+
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.read(null, buf);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.read(ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.write(null, buf);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.write(ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.size(null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.delete(null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.getAttribute(file, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.getAttribute(file, "user:" + ATTR_NAME, (LinkOption[])null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, "user:" + ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, null, new byte[0]);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, "user: " + ATTR_NAME, new byte[0], (LinkOption[])null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.readAttributes(file, (String)null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.readAttributes(file, "*", (LinkOption[])null);
+ }});
+
+ // Read-only buffer
+ tryCatch(IllegalArgumentException.class, new Task() {
+ public void run() throws IOException {
+ ByteBuffer buf = ByteBuffer.wrap(ATTR_VALUE.getBytes()).asReadOnlyBuffer();
+ view.write(ATTR_NAME, buf);
+ buf.flip();
+ view.read(ATTR_NAME, buf);
+ }});
+
+ // Zero bytes remaining
+ tryCatch(IOException.class, new Task() {
+ public void run() throws IOException {
+ ByteBuffer buf = buf = ByteBuffer.allocateDirect(100);
+ buf.position(buf.capacity());
+ view.read(ATTR_NAME, buf);
+ }});
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("user")) {
+ System.out.println("UserDefinedFileAttributeView not supported - skip test");
+ return;
+ }
+
+ // test access to user defined attributes of regular file
+ Path file = dir.resolve("foo.html");
+ Files.createFile(file);
+ try {
+ test(file);
+ } finally {
+ Files.delete(file);
+ }
+
+ // test access to user defined attributes of directory
+ Path subdir = dir.resolve("foo");
+ Files.createDirectory(subdir);
+ try {
+ test(subdir);
+ } finally {
+ Files.delete(subdir);
+ }
+
+ // test access to user defined attributes of sym link
+ if (TestUtil.supportsLinks(dir)) {
+ Path target = dir.resolve("doesnotexist");
+ Path link = dir.resolve("link");
+ Files.createSymbolicLink(link, target);
+ try {
+ test(link, NOFOLLOW_LINKS);
+ } catch (IOException x) {
+ // access to attributes of sym link may not be supported
+ } finally {
+ Files.delete(link);
+ }
+ }
+
+ // misc. tests
+ try {
+ file = dir.resolve("foo.txt");
+ Files.createFile(file);
+ miscTests(dir);
+ } finally {
+ Files.delete(file);
+ }
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+ }
diff --git a/ojluni/src/test/java/time/tck/java/time/TCKInstant.java b/ojluni/src/test/java/time/tck/java/time/TCKInstant.java
index 3d89db5..1b8fea7 100644
--- a/ojluni/src/test/java/time/tck/java/time/TCKInstant.java
+++ b/ojluni/src/test/java/time/tck/java/time/TCKInstant.java
@@ -1931,6 +1931,16 @@
Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1).toEpochMilli();
}
+ @Test(expectedExceptions=ArithmeticException.class)
+ public void test_toEpochMillis_overflow() {
+ Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 809_000_000).toEpochMilli();
+ }
+
+ @Test(expectedExceptions=ArithmeticException.class)
+ public void test_toEpochMillis_overflow2() {
+ Instant.ofEpochSecond(-9223372036854776L, 1).toEpochMilli();
+ }
+
//-----------------------------------------------------------------------
// compareTo()
//-----------------------------------------------------------------------
diff --git a/ojluni/src/test/java/time/test/java/time/TestInstant.java b/ojluni/src/test/java/time/test/java/time/TestInstant.java
index cf135a1..203bb57 100644
--- a/ojluni/src/test/java/time/test/java/time/TestInstant.java
+++ b/ojluni/src/test/java/time/test/java/time/TestInstant.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -62,6 +62,8 @@
import java.time.Instant;
import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import static org.testng.Assert.assertEquals;
/**
* Test Instant.
@@ -74,4 +76,24 @@
assertImmutable(Instant.class);
}
+ @DataProvider(name="sampleEpochMillis")
+ private Object[][] provider_sampleEpochMillis() {
+ return new Object[][] {
+ {"Long.MAX_VALUE", Long.MAX_VALUE},
+ {"Long.MAX_VALUE-1", Long.MAX_VALUE - 1},
+ {"1", 1L},
+ {"0", 0L},
+ {"-1", -1L},
+ {"Long.MIN_VALUE+1", Long.MIN_VALUE + 1},
+ {"Long.MIN_VALUE", Long.MIN_VALUE}
+ };
+ }
+
+ @Test(dataProvider="sampleEpochMillis")
+ public void test_epochMillis(String name, long millis) {
+ Instant t1 = Instant.ofEpochMilli(millis);
+ long m = t1.toEpochMilli();
+ assertEquals(millis, m, name);
+ }
+
}
diff --git a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
index 1ef3a5b..23ef184 100644
--- a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
+++ b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
@@ -99,7 +99,7 @@
assertEquals(locale.toString(), "ja_JP_#u-ca-japanese", "Unexpected locale");
// Android changed: Android doesn't return the Japanese Imperial Calendar from getInstance.
- Calendar cal = java.util.Calendar.getJapanesImperialInstance(TimeZone.getDefault(), locale);
+ Calendar cal = Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), locale);
assertEquals(cal.getCalendarType(), "japanese", "Unexpected calendar type");
JapaneseDate jDate = JapaneseChronology.INSTANCE.date(isoStartDate);
diff --git a/ojluni/src/test/java/time/test/java/util/TestFormatter.java b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
index afd5fc4..05cb2e7 100644
--- a/ojluni/src/test/java/time/test/java/util/TestFormatter.java
+++ b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
@@ -111,7 +111,7 @@
TimeZone tz = TimeZone.getTimeZone(zdt.getZone());
Calendar cal;
if (calLocale.getLanguage().equals("ja")) {
- cal = Calendar.getJapanesImperialInstance(tz, calLocale);
+ cal = Calendar.getJapaneseImperialInstance(tz, calLocale);
} else {
cal = Calendar.getInstance(tz, calLocale);
}
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 50c4b84..4285911 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -114,6 +114,7 @@
ojluni/src/main/java/java/lang/AssertionError.java \
ojluni/src/main/java/java/lang/AutoCloseable.java \
ojluni/src/main/java/java/lang/Boolean.java \
+ ojluni/src/main/java/java/lang/BootstrapMethodError.java \
ojluni/src/main/java/java/lang/Byte.java \
ojluni/src/main/java/java/lang/Character.java \
ojluni/src/main/java/java/lang/CharSequence.java \
@@ -240,6 +241,8 @@
ojluni/src/main/java/java/lang/VirtualMachineError.java \
ojluni/src/main/java/java/lang/Void.java \
ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java \
+ ojluni/src/main/java/java/lang/invoke/CallSite.java \
+ ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java \
ojluni/src/main/java/java/lang/invoke/MethodHandle.java \
ojluni/src/main/java/java/lang/invoke/MethodHandles.java \
ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java \
@@ -247,8 +250,10 @@
ojluni/src/main/java/java/lang/invoke/MethodHandleStatics.java \
ojluni/src/main/java/java/lang/invoke/MethodType.java \
ojluni/src/main/java/java/lang/invoke/MethodTypeForm.java \
+ ojluni/src/main/java/java/lang/invoke/MutableCallSite.java \
ojluni/src/main/java/java/lang/invoke/Stable.java \
ojluni/src/main/java/java/lang/invoke/Transformers.java \
+ ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java \
ojluni/src/main/java/java/lang/invoke/WrongMethodTypeException.java \
ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java \
ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java \
@@ -1744,10 +1749,10 @@
# On older platforms : Both sets of stub files are used and core-oj does not contain
# any of these classes.
openjdk_lambda_stub_files := \
- ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
openjdk_lambda_duplicate_stub_files := \
+ ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
ojluni/src/lambda/java/java/lang/invoke/LambdaConversionException.java \
ojluni/src/lambda/java/java/lang/invoke/MethodHandle.java \
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 8b5664e..8bc9937 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -16,6 +16,10 @@
package libcore.java.security;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import java.security.Security;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
@@ -36,7 +40,6 @@
import java.util.Set;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
-import junit.framework.Assert;
/**
* This class defines expected string names for protocols, key types,
@@ -63,7 +66,7 @@
* Java ™ PKCS#11 Reference Guide
* </a>.
*/
-public final class StandardNames extends Assert {
+public final class StandardNames {
public static final boolean IS_RI
= !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
@@ -107,7 +110,7 @@
private static void provide(String type, String algorithm) {
Set<String> algorithms = PROVIDER_ALGORITHMS.get(type);
if (algorithms == null) {
- algorithms = new HashSet();
+ algorithms = new HashSet<String>();
PROVIDER_ALGORITHMS.put(type, algorithms);
}
assertTrue("Duplicate " + type + " " + algorithm,
@@ -490,6 +493,16 @@
provide("Cipher", "PBEWITHSHAAND40BITRC2-CBC");
provide("Cipher", "PBEWITHSHAAND40BITRC4");
provide("Cipher", "PBEWITHSHAANDTWOFISH-CBC");
+ provide("Cipher", "PBEWithHmacSHA1AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA224AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA256AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA384AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA512AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA1AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA224AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA256AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA384AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA512AndAES_256");
provide("Mac", "PBEWITHHMACSHA");
provide("Mac", "PBEWITHHMACSHA1");
provide("SecretKeyFactory", "PBEWITHHMACSHA1");
@@ -590,7 +603,7 @@
unprovide("AlgorithmParameters", "PBEWithMD5AndDES"); // 1.2.840.113549.1.5.3
// EC support
- // provide("AlgorithmParameters", "EC");
+ provide("AlgorithmParameters", "EC");
provide("KeyAgreement", "ECDH");
provide("KeyFactory", "EC");
provide("KeyPairGenerator", "EC");
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index ef62a44..3829dc1 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -16,10 +16,14 @@
package libcore.java.security;
+import static org.junit.Assert.assertEquals;
+
import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
import com.android.org.bouncycastle.asn1.x509.CRLReason;
import com.android.org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import com.android.org.bouncycastle.asn1.x509.Extension;
import com.android.org.bouncycastle.asn1.x509.GeneralName;
import com.android.org.bouncycastle.asn1.x509.GeneralNames;
import com.android.org.bouncycastle.asn1.x509.GeneralSubtree;
@@ -27,23 +31,20 @@
import com.android.org.bouncycastle.asn1.x509.KeyUsage;
import com.android.org.bouncycastle.asn1.x509.NameConstraints;
import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
import com.android.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.org.bouncycastle.cert.X509v3CertificateBuilder;
import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import com.android.org.bouncycastle.cert.ocsp.BasicOCSPResp;
import com.android.org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
import com.android.org.bouncycastle.cert.ocsp.CertificateID;
import com.android.org.bouncycastle.cert.ocsp.CertificateStatus;
-import com.android.org.bouncycastle.cert.ocsp.OCSPException;
import com.android.org.bouncycastle.cert.ocsp.OCSPResp;
import com.android.org.bouncycastle.cert.ocsp.OCSPRespBuilder;
import com.android.org.bouncycastle.cert.ocsp.RevokedStatus;
import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.android.org.bouncycastle.operator.DigestCalculatorProvider;
-import com.android.org.bouncycastle.operator.OperatorCreationException;
import com.android.org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import com.android.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -64,7 +65,6 @@
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -79,7 +79,6 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
-import junit.framework.Assert;
import libcore.javax.net.ssl.TestKeyManager;
import libcore.javax.net.ssl.TestTrustManager;
@@ -90,7 +89,7 @@
* Creating a key store is relatively slow, so a singleton instance is
* accessible via TestKeyStore.get().
*/
-public final class TestKeyStore extends Assert {
+public final class TestKeyStore {
/** Size of DSA keys to generate for testing. */
private static final int DSA_KEY_SIZE_BITS = 1024;
@@ -156,11 +155,9 @@
}
}
- private static final boolean TEST_MANAGERS = true;
private static final byte[] LOCAL_HOST_ADDRESS = { 127, 0, 0, 1 };
private static final String LOCAL_HOST_NAME = "localhost";
-
public final KeyStore keyStore;
public final char[] storePassword;
public final char[] keyPassword;
@@ -707,43 +704,40 @@
throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
}
- X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
- x509cg.setSubjectDN(subject);
- x509cg.setIssuerDN(issuer);
- x509cg.setNotBefore(start);
- x509cg.setNotAfter(end);
- x509cg.setPublicKey(publicKey);
- x509cg.setSignatureAlgorithm(signatureAlgorithm);
if (serialNumber == null) {
byte[] serialBytes = new byte[16];
new SecureRandom().nextBytes(serialBytes);
serialNumber = new BigInteger(1, serialBytes);
}
- x509cg.setSerialNumber(serialNumber);
+
+ X509v3CertificateBuilder x509cg = new X509v3CertificateBuilder(
+ X500Name.getInstance(issuer.getEncoded()), serialNumber, start, end,
+ X500Name.getInstance(subject.getEncoded()),
+ SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
if (keyUsage != 0) {
- x509cg.addExtension(X509Extensions.KeyUsage,
+ x509cg.addExtension(Extension.keyUsage,
true,
new KeyUsage(keyUsage));
}
if (ca) {
- x509cg.addExtension(X509Extensions.BasicConstraints,
+ x509cg.addExtension(Extension.basicConstraints,
true,
new BasicConstraints(true));
}
for (int i = 0; i < extendedKeyUsages.size(); i++) {
KeyPurposeId keyPurposeId = extendedKeyUsages.get(i);
boolean critical = criticalExtendedKeyUsages.get(i);
- x509cg.addExtension(X509Extensions.ExtendedKeyUsage,
+ x509cg.addExtension(Extension.extendedKeyUsage,
critical,
new ExtendedKeyUsage(keyPurposeId));
}
for (GeneralName subjectAltName : subjectAltNames) {
- x509cg.addExtension(X509Extensions.SubjectAlternativeName,
+ x509cg.addExtension(Extension.subjectAlternativeName,
false,
new GeneralNames(subjectAltName).getEncoded());
}
if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
- x509cg.addExtension(X509Extensions.NameConstraints,
+ x509cg.addExtension(Extension.nameConstraints,
true,
new NameConstraints(permittedNameConstraints.toArray(
new GeneralSubtree[
@@ -753,7 +747,12 @@
excludedNameConstraints.size()])));
}
- X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
+ X509CertificateHolder x509holder = x509cg.build(
+ new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey));
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ X509Certificate x509c = (X509Certificate) certFactory.generateCertificate(
+ new ByteArrayInputStream(x509holder.getEncoded()));
+
if (StandardNames.IS_RI) {
/*
* The RI can't handle the BC EC signature algorithm
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
index ae4af45..4848db7 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
@@ -30,12 +30,14 @@
* The major distro format version supported by this device.
* Increment this for non-backwards compatible changes to the distro format. Reset the minor
* version to 1 when doing so.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final int CURRENT_FORMAT_MAJOR_VERSION = 1;
/**
* The minor distro format version supported by this device. Increment this for
* backwards-compatible changes to the distro format.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final int CURRENT_FORMAT_MINOR_VERSION = 1;
@@ -122,10 +124,8 @@
}
public static boolean isCompatibleWithThisDevice(DistroVersion distroVersion) {
- return (DistroVersion.CURRENT_FORMAT_MAJOR_VERSION
- == distroVersion.formatMajorVersion)
- && (DistroVersion.CURRENT_FORMAT_MINOR_VERSION
- <= distroVersion.formatMinorVersion);
+ return (CURRENT_FORMAT_MAJOR_VERSION == distroVersion.formatMajorVersion)
+ && (CURRENT_FORMAT_MINOR_VERSION <= distroVersion.formatMinorVersion);
}
@Override
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
index c04b4cb..04e3f2a 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
@@ -40,6 +40,7 @@
/**
* The name of the file inside the distro containing the distro version information.
* The content is ASCII bytes representing a set of version numbers. See {@link DistroVersion}.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final String DISTRO_VERSION_FILE_NAME = "distro_version";
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
similarity index 98%
rename from tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
rename to tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
index d08bb1d..1b56ebf 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
@@ -26,8 +26,7 @@
* A distro-validation / extraction class. Separate from the services code that uses it for easier
* testing. This class is not thread-safe: callers are expected to handle mutual exclusion.
*/
-// TODO(nfuller) Rename to TimeZoneDistroInstaller
-public final class TimeZoneBundleInstaller {
+public final class TimeZoneDistroInstaller {
/** {@link #installWithErrorCode(byte[])} result code: Success. */
public final static int INSTALL_SUCCESS = 0;
/** {@link #installWithErrorCode(byte[])} result code: Distro corrupt. */
@@ -49,7 +48,7 @@
private final File currentTzDataDir;
private final File workingDir;
- public TimeZoneBundleInstaller(String logTag, File systemTzDataFile, File installDir) {
+ public TimeZoneDistroInstaller(String logTag, File systemTzDataFile, File installDir) {
this.logTag = logTag;
this.systemTzDataFile = systemTzDataFile;
oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
diff --git a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
similarity index 92%
rename from tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
rename to tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
index 088e7cb..6ae0e56 100644
--- a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
+++ b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
@@ -34,9 +34,9 @@
import libcore.tzdata.update2.tools.TimeZoneDistroBuilder;
/**
- * Tests for {@link TimeZoneBundleInstaller}.
+ * Tests for {@link TimeZoneDistroInstaller}.
*/
-public class TimeZoneBundleInstallerTest extends TestCase {
+public class TimeZoneDistroInstallerTest extends TestCase {
// OLDER_RULES_VERSION < SYSTEM_RULES_VERSION < NEW_RULES_VERSION < NEWER_RULES_VERSION
private static final String OLDER_RULES_VERSION = "2030a";
@@ -44,7 +44,7 @@
private static final String NEW_RULES_VERSION = "2030c";
private static final String NEWER_RULES_VERSION = "2030d";
- private TimeZoneBundleInstaller installer;
+ private TimeZoneDistroInstaller installer;
private File tempDir;
private File testInstallDir;
private File testSystemTzDataDir;
@@ -61,8 +61,8 @@
byte[] systemTzDataBytes = createTzData(SYSTEM_RULES_VERSION);
createFile(testSystemTzDataFile, systemTzDataBytes);
- installer = new TimeZoneBundleInstaller(
- "TimeZoneBundleInstallerTest", testSystemTzDataFile, testInstallDir);
+ installer = new TimeZoneDistroInstaller(
+ "TimeZoneDistroInstallerTest", testSystemTzDataFile, testInstallDir);
}
private static File createDirectory(String prefix) throws Exception {
@@ -89,8 +89,8 @@
/** Tests the an update on a device will fail if the /system tzdata file cannot be found. */
public void testInstall_badSystemFile() throws Exception {
File doesNotExist = new File(testSystemTzDataDir, "doesNotExist");
- TimeZoneBundleInstaller brokenSystemInstaller = new TimeZoneBundleInstaller(
- "TimeZoneBundleInstallerTest", doesNotExist, testInstallDir);
+ TimeZoneDistroInstaller brokenSystemInstaller = new TimeZoneDistroInstaller(
+ "TimeZoneDistroInstallerTest", doesNotExist, testInstallDir);
TimeZoneDistro tzData = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
try {
@@ -106,7 +106,7 @@
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -117,7 +117,7 @@
public void testInstall_successfulFirstUpdate_sameVersionAsSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(SYSTEM_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -128,7 +128,7 @@
public void testInstall_unsuccessfulFirstUpdate_olderVersionThanSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+ TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -139,19 +139,19 @@
public void testInstall_successfulFollowOnUpdate_newerVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro2);
TimeZoneDistro distro3 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro3.getBytes()));
assertDistroInstalled(distro3);
}
@@ -163,13 +163,13 @@
public void testInstall_unsuccessfulFollowOnUpdate_olderVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+ TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro1);
}
@@ -178,7 +178,7 @@
public void testInstall_missingTzDataFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
@@ -187,7 +187,7 @@
.clearTzDataForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
@@ -196,7 +196,7 @@
public void testInstall_missingIcuFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
@@ -205,7 +205,7 @@
.clearIcuDataForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
@@ -220,7 +220,7 @@
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -234,7 +234,7 @@
.clearVersionForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -248,7 +248,7 @@
.replaceFormatVersionForTests(2, 1)
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -265,7 +265,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidFormatVersionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -281,7 +281,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRevisionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -297,7 +297,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRulesVersionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}