> r = list;
list = new ArrayList<>();
modified.clear();
return r;
}
- public synchronized boolean reset()
- {
- if (fsw == null)
- {
+ public synchronized boolean reset() {
+ if (fsw == null) {
return false;
}
- if (signaled)
- {
- if (list.size() == 0)
- {
+
+ if (signaled) {
+ if (list.size() == 0) {
signaled = false;
- }
- else
- {
+ } else {
enqueue(this);
}
}
+
return true;
}
- void close()
- {
- if (fsw != null)
- {
+ void close() {
+ if (fsw != null) {
fsw.Dispose();
fsw = null;
}
}
- public void cancel()
- {
- synchronized (NetWatchService.this)
- {
+ public void cancel() {
+ synchronized (NetWatchService.this) {
keys.remove(this);
close();
}
}
- public Watchable watchable()
- {
+ public Watchable watchable() {
return path;
}
+
}
- synchronized WatchKey register(DotNetPath path, boolean create, boolean delete, boolean modify, boolean overflow, boolean subtree)
- {
- if (closed)
- {
+ synchronized WatchKey register(DotNetPath path, boolean create, boolean delete, boolean modify, boolean overflow, boolean subtree) throws IOException {
+ if (closed) {
throw new ClosedWatchServiceException();
}
+
NetWatchKey existing = null;
- for (NetWatchKey key : keys)
- {
- if (key.watchable().equals(path))
- {
+ for (NetWatchKey key : keys) {
+ if (key.watchable().equals(path)) {
existing = key;
break;
}
}
- if (existing == null)
- {
+
+ if (existing == null) {
existing = new NetWatchKey(path);
keys.add(existing);
}
+
existing.init(create, delete, modify, overflow, subtree);
return existing;
}
+
}
- public WatchService newWatchService() throws IOException
- {
+ public WatchService newWatchService() throws IOException {
return new NetWatchService();
}
diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java b/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java
index 5ee0e320e6..2766c08381 100644
--- a/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java
+++ b/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java
@@ -39,14 +39,10 @@ final class DotNetPath extends AbstractPath {
private final DotNetFileSystem fs;
final String path;
- DotNetPath(DotNetFileSystem fs, String path)
- {
- if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows())
- {
+ DotNetPath(DotNetFileSystem fs, String path) {
+ if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) {
path = WindowsPathParser.parse(path).path();
- }
- else
- {
+ } else {
StringBuilder sb = null;
int separatorCount = 0;
boolean prevWasSeparator = false;
@@ -107,132 +103,108 @@ public Path getRoot() {
}
private int getRootLength() {
- if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows())
- {
- if (path.length() >= 2 && path.charAt(1) == ':')
- {
- if (path.length() >= 3 && path.charAt(2) == '\\')
- {
+ if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) {
+ if (path.length() >= 2 && path.charAt(1) == ':') {
+ if (path.length() >= 3 && path.charAt(2) == '\\') {
return 3;
- }
- else
- {
+ } else {
return 2;
}
- }
- else if (path.startsWith("\\\\"))
- {
+ } else if (path.startsWith("\\\\")) {
return path.indexOf('\\', path.indexOf('\\', 2) + 1) + 1;
}
}
- if (path.length() >= 1 && path.charAt(0) == cli.System.IO.Path.DirectorySeparatorChar)
- {
+ if (path.length() >= 1 && path.charAt(0) == cli.System.IO.Path.DirectorySeparatorChar) {
return 1;
- }
- else
- {
+ } else {
return 0;
}
}
public Path getFileName() {
- if (path.length() == 0)
- {
+ if (path.length() == 0) {
return this;
}
- if (path.length() == getRootLength())
- {
+ if (path.length() == getRootLength()) {
return null;
}
String name = cli.System.IO.Path.GetFileName(path);
- if (name == null || name.length() == 0)
- {
+ if (name == null || name.length() == 0) {
return null;
}
+
return new DotNetPath(fs, name);
}
- public Path getParent()
- {
- if (path.length() == getRootLength())
- {
+ public Path getParent() {
+ if (path.length() == getRootLength()) {
return null;
}
String parent = cli.System.IO.Path.GetDirectoryName(path);
- if (parent == null || parent.length() == 0)
- {
+ if (parent == null || parent.length() == 0) {
return null;
}
+
return new DotNetPath(fs, parent);
}
- public int getNameCount()
- {
+ public int getNameCount() {
int len = getRootLength();
- if (path.length() == len)
- {
+ if (path.length() == len) {
return len == 0 ? 1 : 0;
}
int count = 1;
- for (int i = len; i < path.length(); i++)
- {
- if (path.charAt(i) == cli.System.IO.Path.DirectorySeparatorChar)
- {
+ for (int i = len; i < path.length(); i++) {
+ if (path.charAt(i) == cli.System.IO.Path.DirectorySeparatorChar) {
count++;
}
}
+
return count;
}
- public Path getName(int index)
- {
+ public Path getName(int index) {
return new DotNetPath(fs, getNameImpl(index));
}
- private String getNameImpl(int index)
- {
- for (int pos = getRootLength(); pos < path.length(); index--)
- {
+ private String getNameImpl(int index) {
+ for (int pos = getRootLength(); pos < path.length(); index--) {
int next = path.indexOf(cli.System.IO.Path.DirectorySeparatorChar, pos);
- if (index == 0)
- {
+ if (index == 0) {
return next == -1 ? path.substring(pos) : path.substring(pos, next);
}
- if (next == -1)
- {
+ if (next == -1) {
break;
}
pos = next + 1;
}
- if (path.length() == 0 && index == 0)
- {
+
+ if (path.length() == 0 && index == 0) {
return "";
}
+
throw new IllegalArgumentException();
}
- public Path subpath(int beginIndex, int endIndex)
- {
+ public Path subpath(int beginIndex, int endIndex) {
StringBuilder sb = new StringBuilder();
- for (int i = beginIndex; i < endIndex; i++)
- {
- if (i != beginIndex)
- {
+ for (int i = beginIndex; i < endIndex; i++) {
+ if (i != beginIndex) {
sb.append(cli.System.IO.Path.DirectorySeparatorChar);
}
sb.append(getNameImpl(i));
}
+
return new DotNetPath(fs, sb.toString());
}
- public boolean startsWith(Path other)
- {
+ public boolean startsWith(Path other) {
String npath = DotNetPath.from(other).path;
- if (npath.length() == 0)
- {
+ if (npath.length() == 0) {
return path.length() == 0;
}
+
return path.regionMatches(cli.IKVM.Runtime.RuntimeUtil.get_IsWindows(), 0, npath, 0, npath.length())
&& (npath.length() == getRootLength()
|| (npath.length() > getRootLength()
@@ -240,27 +212,22 @@ public boolean startsWith(Path other)
|| (path.length() > npath.length() && path.charAt(npath.length()) == cli.System.IO.Path.DirectorySeparatorChar))));
}
- public boolean endsWith(Path other)
- {
+ public boolean endsWith(Path other) {
DotNetPath nother = DotNetPath.from(other);
String npath = nother.path;
- if (npath.length() > path.length())
- {
+ if (npath.length() > path.length()) {
return false;
}
- if (npath.length() == 0)
- {
+ if (npath.length() == 0) {
return path.length() == 0;
}
int nameCount = getNameCount();
int otherNameCount = nother.getNameCount();
- if (otherNameCount > nameCount)
- {
+ if (otherNameCount > nameCount) {
return false;
}
int otherRootLength = nother.getRootLength();
- if (otherRootLength > 0)
- {
+ if (otherRootLength > 0) {
if (otherNameCount != nameCount
|| getRootLength() != otherRootLength
|| !path.regionMatches(cli.IKVM.Runtime.RuntimeUtil.get_IsWindows(), 0, npath, 0, otherRootLength))
@@ -268,6 +235,7 @@ public boolean endsWith(Path other)
return false;
}
}
+
int skip = nameCount - otherNameCount;
for (int i = 0; i < otherNameCount; i++)
{
@@ -278,6 +246,7 @@ public boolean endsWith(Path other)
return false;
}
}
+
return true;
}
@@ -473,170 +442,165 @@ private DotNetPath emptyPath() {
return new DotNetPath(fs, "");
}
- public URI toUri()
- {
- if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows())
- {
+ public URI toUri() {
+ if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) {
return DotNetWindowsUriSupport.toUri(this);
- }
- else
- {
+ } else {
return DotNetUnixUriUtils.toUri(this);
}
}
- public DotNetPath toAbsolutePath()
- {
- if (isAbsolute())
- {
+ public DotNetPath toAbsolutePath() {
+ if (isAbsolute()) {
return this;
}
+
// System.getProperty("user.dir") will trigger the specified security check
return new DotNetPath(fs, cli.System.IO.Path.GetFullPath(cli.System.IO.Path.Combine(System.getProperty("user.dir"), path)));
}
- public Path toRealPath(LinkOption... options) throws IOException
- {
+ public Path toRealPath(LinkOption... options) throws IOException {
SecurityManager sm = System.getSecurityManager();
- if (sm != null)
- {
+ if (sm != null) {
sm.checkRead(path);
- if (!isAbsolute())
- {
+ if (!isAbsolute()) {
sm.checkPropertyAccess("user.dir");
}
}
+
return new DotNetPath(fs, toRealPathImpl(path));
}
private static native String toRealPathImpl(String path);
- public WatchKey register(WatchService watcher, WatchEvent.Kind>[] events, WatchEvent.Modifier... modifiers) throws IOException
- {
- if (!(watcher instanceof DotNetFileSystem.NetWatchService))
- {
- // null check
- watcher.getClass();
- throw new ProviderMismatchException();
- }
- boolean create = false;
- boolean delete = false;
- boolean modify = false;
- boolean overflow = false;
- boolean subtree = false;
- for (WatchEvent.Kind> kind : events)
- {
- if (kind == StandardWatchEventKinds.ENTRY_CREATE)
- {
- create = true;
- }
- else if (kind == StandardWatchEventKinds.ENTRY_DELETE)
- {
- delete = true;
- }
- else if (kind == StandardWatchEventKinds.ENTRY_MODIFY)
- {
- modify = true;
- }
- else if (kind == StandardWatchEventKinds.OVERFLOW)
- {
- overflow = true;
+ public WatchKey register(WatchService watcher, WatchEvent.Kind>[] events, WatchEvent.Modifier... modifiers) throws IOException {
+ if (watcher instanceof DotNetFileSystem.NetWatchService) {
+ boolean create = false;
+ boolean delete = false;
+ boolean modify = false;
+ boolean overflow = false;
+ boolean subtree = false;
+
+ for (WatchEvent.Kind> kind : events) {
+ if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
+ create = true;
+ } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
+ delete = true;
+ } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
+ modify = true;
+ } else if (kind == StandardWatchEventKinds.OVERFLOW) {
+ overflow = true;
+ } else {
+ // null check
+ kind.getClass();
+ throw new UnsupportedOperationException();
+ }
}
- else
- {
- // null check
- kind.getClass();
- throw new UnsupportedOperationException();
+
+ if (!create && !delete && !modify) {
+ throw new IllegalArgumentException();
}
- }
- if (!create && !delete && !modify)
- {
- throw new IllegalArgumentException();
- }
- for (WatchEvent.Modifier modifier : modifiers)
- {
- if (modifier == ExtendedWatchEventModifier.FILE_TREE)
- {
- subtree = true;
+
+ for (WatchEvent.Modifier modifier : modifiers) {
+ if (modifier == ExtendedWatchEventModifier.FILE_TREE) {
+ subtree = true;
+ } else if (modifier instanceof SensitivityWatchEventModifier) {
+ // ignore
+ } else {
+ // null check
+ modifier.getClass();
+ throw new UnsupportedOperationException();
+ }
}
- else if (modifier instanceof SensitivityWatchEventModifier)
- {
- // ignore
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkRead(path);
+ if (subtree) {
+ sm.checkRead(path + cli.System.IO.Path.DirectorySeparatorChar + '-');
+ }
}
- else
- {
- // null check
- modifier.getClass();
- throw new UnsupportedOperationException();
+
+ return ((DotNetFileSystem.NetWatchService)watcher).register(this, create, delete, modify, overflow, subtree);
+ } else if (watcher instanceof PollingWatchService) {
+ boolean subtree = false;
+
+ for (WatchEvent.Modifier modifier : modifiers) {
+ if (modifier == ExtendedWatchEventModifier.FILE_TREE) {
+ subtree = true;
+ } else if (modifier instanceof SensitivityWatchEventModifier) {
+ // ignore
+ } else {
+ // null check
+ modifier.getClass();
+ throw new UnsupportedOperationException();
+ }
}
- }
- SecurityManager sm = System.getSecurityManager();
- if (sm != null)
- {
- sm.checkRead(path);
- if (subtree)
- {
- sm.checkRead(path + cli.System.IO.Path.DirectorySeparatorChar + '-');
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkRead(path);
+ if (subtree) {
+ sm.checkRead(path + cli.System.IO.Path.DirectorySeparatorChar + '-');
+ }
}
+
+ return ((PollingWatchService)watcher).register(this, events, modifiers);
+ } else {
+ watcher.getClass(); // null check
+ throw new ProviderMismatchException();
}
- return ((DotNetFileSystem.NetWatchService)watcher).register(this, create, delete, modify, overflow, subtree);
}
- public int compareTo(Path other)
- {
+ public int compareTo(Path other) {
String path2 = ((DotNetPath)other).path;
int len1 = path.length();
int len2 = path2.length();
int min = Math.min(len1, len2);
- for (int i = 0; i < min; i++)
- {
+ for (int i = 0; i < min; i++) {
char c1 = path.charAt(i);
char c2 = path2.charAt(i);
- if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2))
- {
+ if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) {
return c1 - c2;
}
}
+
return len1 - len2;
}
- public boolean equals(Object other)
- {
- if (!(other instanceof DotNetPath))
- {
+ public boolean equals(Object other) {
+ if (!(other instanceof DotNetPath)) {
return false;
}
+
return compareTo((DotNetPath)other) == 0;
}
- public int hashCode()
- {
+ public int hashCode() {
int hash = 0;
- for (int i = 0; i < path.length(); i++)
- {
+ for (int i = 0; i < path.length(); i++) {
hash = 97 * hash + Character.toUpperCase(path.charAt(i));
}
+
return hash;
}
- public String toString()
- {
+ public String toString() {
return path;
}
- boolean isUnc()
- {
+ boolean isUnc() {
return cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() && getRootLength() > 3;
}
- static DotNetPath from(Path path)
- {
- if (!(path instanceof DotNetPath))
- {
+ static DotNetPath from(Path path) {
+ if (!(path instanceof DotNetPath)) {
// null check
path.getClass();
throw new ProviderMismatchException();
}
+
return (DotNetPath)path;
}
-}
+
+}
\ No newline at end of file
diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java b/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java
index 7b4c82543d..e05a86d9e4 100644
--- a/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java
+++ b/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,6 +69,32 @@ static Path fromUri(DotNetFileSystem fs, URI uri) {
// transform escaped octets and unescaped characters to bytes
if (p.endsWith("/") && len > 1)
len--;
+ byte[] result = new byte[len];
+ int rlen = 0;
+ int pos = 0;
+ while (pos < len) {
+ char c = p.charAt(pos++);
+ byte b;
+ if (c == '%') {
+ assert (pos+2) <= len;
+ char c1 = p.charAt(pos++);
+ char c2 = p.charAt(pos++);
+ b = (byte)((decode(c1) << 4) | decode(c2));
+ if (b == 0)
+ throw new IllegalArgumentException("Nul character not allowed");
+ } else {
+ if (c == 0 || c >= 0x80)
+ throw new IllegalArgumentException("Bad escape");
+ b = (byte)c;
+ }
+ if (b == '/' && rlen > 0 && result[rlen-1] == '/') {
+ // skip redundant slashes
+ continue;
+ }
+ result[rlen++] = b;
+ }
+ if (rlen != result.length)
+ result = Arrays.copyOf(result, rlen);
return new DotNetPath(fs, p);
}
@@ -94,7 +120,7 @@ static URI toUri(DotNetPath up) {
// trailing slash if directory
if (sb.charAt(sb.length()-1) != '/') {
try {
- if (cli.System.IO.Directory.Exists(sb.toString()) || isVfsDirectory(sb.toString()))
+ if (cli.System.IO.Directory.Exists(up.toString()) || isVfsDirectory(up.toString()))
sb.append('/');
} catch (Throwable x) {
// ignore
@@ -217,15 +243,15 @@ private static int decode(char c) {
private static final long H_PCHAR
= H_UNRESERVED | highMask(":@&=+$,");
- // All valid path characters
- private static final long L_PATH = L_PCHAR | lowMask(";/");
- private static final long H_PATH = H_PCHAR | highMask(";/");
+ // All valid path characters
+ private static final long L_PATH = L_PCHAR | lowMask(";/");
+ private static final long H_PATH = H_PCHAR | highMask(";/");
- private final static char[] hexDigits = {
+ private final static char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
static native boolean isVfsDirectory(String path);
-}
\ No newline at end of file
+}
diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java b/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java
index fa37311593..7c98fd3f5f 100644
--- a/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java
+++ b/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -104,9 +104,8 @@ static URI toUri(DotNetPath path) {
boolean addSlash = false;
if (!s.endsWith("\\")) {
try {
- addSlash = cli.System.IO.Directory.Exists(s) || isVfsDirectory(s);
+ addSlash = cli.System.IO.Directory.Exists(s) || isVfsDirectory(s);
} catch (Throwable x) {
-
}
}
@@ -168,4 +167,4 @@ static DotNetPath fromUri(DotNetFileSystem fs, URI uri) {
static native boolean isVfsDirectory(String path);
-}
\ No newline at end of file
+}
diff --git a/src/IKVM.Java/local/sun/reflect/ReflectionFactory.java b/src/IKVM.Java/local/sun/reflect/ReflectionFactory.java
index 35cae84cc3..76456056c7 100644
--- a/src/IKVM.Java/local/sun/reflect/ReflectionFactory.java
+++ b/src/IKVM.Java/local/sun/reflect/ReflectionFactory.java
@@ -31,16 +31,28 @@
package sun.reflect;
+import java.io.Externalizable;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OptionalDataException;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
+import java.util.Objects;
+
import sun.reflect.misc.ReflectUtil;
+
/** The master factory for all reflective objects, both those in
java.lang.reflect (Fields, Methods, Constructors) as well as their
delegates (FieldAccessors, MethodAccessors, ConstructorAccessors).
@@ -61,6 +73,9 @@ public class ReflectionFactory {
// Provides access to package-private mechanisms in java.lang.reflect
private static volatile LangReflectAccess langReflectAccess;
+ /* Method for static class initializer , or null */
+ private static volatile Method hasStaticInitializerMethod;
+
private ReflectionFactory() {
}
@@ -282,6 +297,42 @@ public byte[] getExecutableTypeAnnotationBytes(Executable ex) {
if (constructorToCall.getDeclaringClass() == classToInstantiate) {
return constructorToCall;
}
+ return generateConstructor(classToInstantiate, constructorToCall);
+ }
+
+ /**
+ * Returns an accessible constructor capable of creating instances
+ * of the given class, initialized by the given constructor.
+ *
+ * @param classToInstantiate the class to instantiate
+ * @param constructorToCall the constructor to call
+ * @return an accessible constructor
+ */
+ public final Constructor> newConstructorForSerialization(Class> cl) {
+ Class> initCl = cl;
+ while (Serializable.class.isAssignableFrom(initCl)) {
+ if ((initCl = initCl.getSuperclass()) == null) {
+ return null;
+ }
+ }
+ Constructor> constructorToCall;
+ try {
+ constructorToCall = initCl.getDeclaredConstructor();
+ int mods = constructorToCall.getModifiers();
+ if ((mods & Modifier.PRIVATE) != 0 ||
+ ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
+ !packageEquals(cl, initCl))) {
+ return null;
+ }
+ } catch (NoSuchMethodException ex) {
+ return null;
+ }
+ return generateConstructor(cl, constructorToCall);
+ }
+
+ private final Constructor> generateConstructor(Class> classToInstantiate,
+ Constructor> constructorToCall) {
+
ConstructorAccessor acc = newConstructorAccessorForSerialization(classToInstantiate, constructorToCall);
Constructor> c = newConstructor(constructorToCall.getDeclaringClass(),
@@ -297,9 +348,222 @@ public byte[] getExecutableTypeAnnotationBytes(Executable ex) {
langReflectAccess().
getConstructorParameterAnnotations(constructorToCall));
setConstructorAccessor(c, acc);
+ c.setAccessible(true);
return c;
}
+ /**
+ * Returns an accessible no-arg constructor for an externalizable class to be
+ * initialized using a public no-argument constructor.
+ *
+ * @param cl the class to instantiate
+ * @return A no-arg constructor for the class; returns {@code null} if
+ * the class does not implement {@link java.io.Externalizable}
+ */
+ public final Constructor> newConstructorForExternalization(Class> cl) {
+ if (!Externalizable.class.isAssignableFrom(cl)) {
+ return null;
+ }
+ try {
+ Constructor> cons = cl.getConstructor();
+ cons.setAccessible(true);
+ return cons;
+ } catch (NoSuchMethodException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code readObject} method on
+ * a Serializable class.
+ * The first argument of {@link MethodHandle#invoke} is the serializable
+ * object and the second argument is the {@code ObjectInputStream} passed to
+ * {@code readObject}.
+ *
+ * @param cl a Serializable class
+ * @return a direct MethodHandle for the {@code readObject} method of the class or
+ * {@code null} if the class does not have a {@code readObject} method
+ */
+ public final MethodHandle readObjectForSerialization(Class> cl) {
+ return findReadWriteObjectForSerialization(cl, "readObject", ObjectInputStream.class);
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code readObjectNoData} method on
+ * a Serializable class.
+ * The first argument of {@link MethodHandle#invoke} is the serializable
+ * object and the second argument is the {@code ObjectInputStream} passed to
+ * {@code readObjectNoData}.
+ *
+ * @param cl a Serializable class
+ * @return a direct MethodHandle for the {@code readObjectNoData} method
+ * of the class or {@code null} if the class does not have a
+ * {@code readObjectNoData} method
+ */
+ public final MethodHandle readObjectNoDataForSerialization(Class> cl) {
+ return findReadWriteObjectForSerialization(cl, "readObjectNoData", ObjectInputStream.class);
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code writeObject} method on
+ * a Serializable class.
+ * The first argument of {@link MethodHandle#invoke} is the serializable
+ * object and the second argument is the {@code ObjectOutputStream} passed to
+ * {@code writeObject}.
+ *
+ * @param cl a Serializable class
+ * @return a direct MethodHandle for the {@code writeObject} method of the class or
+ * {@code null} if the class does not have a {@code writeObject} method
+ */
+ public final MethodHandle writeObjectForSerialization(Class> cl) {
+ return findReadWriteObjectForSerialization(cl, "writeObject", ObjectOutputStream.class);
+ }
+
+ private final MethodHandle findReadWriteObjectForSerialization(Class> cl,
+ String methodName,
+ Class> streamClass) {
+ if (!Serializable.class.isAssignableFrom(cl)) {
+ return null;
+ }
+
+ try {
+ Method meth = cl.getDeclaredMethod(methodName, streamClass);
+ int mods = meth.getModifiers();
+ if (meth.getReturnType() != Void.TYPE ||
+ Modifier.isStatic(mods) ||
+ !Modifier.isPrivate(mods)) {
+ return null;
+ }
+ meth.setAccessible(true);
+ return MethodHandles.lookup().unreflect(meth);
+ } catch (NoSuchMethodException ex) {
+ return null;
+ } catch (IllegalAccessException ex1) {
+ throw new InternalError("Error", ex1);
+ }
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code readResolve} method on
+ * a serializable class.
+ * The single argument of {@link MethodHandle#invoke} is the serializable
+ * object.
+ *
+ * @param cl the Serializable class
+ * @return a direct MethodHandle for the {@code readResolve} method of the class or
+ * {@code null} if the class does not have a {@code readResolve} method
+ */
+ public final MethodHandle readResolveForSerialization(Class> cl) {
+ return getReplaceResolveForSerialization(cl, "readResolve");
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code writeReplace} method on
+ * a serializable class.
+ * The single argument of {@link MethodHandle#invoke} is the serializable
+ * object.
+ *
+ * @param cl the Serializable class
+ * @return a direct MethodHandle for the {@code writeReplace} method of the class or
+ * {@code null} if the class does not have a {@code writeReplace} method
+ */
+ public final MethodHandle writeReplaceForSerialization(Class> cl) {
+ return getReplaceResolveForSerialization(cl, "writeReplace");
+ }
+
+ /**
+ * Returns a direct MethodHandle for the {@code writeReplace} method on
+ * a serializable class.
+ * The single argument of {@link MethodHandle#invoke} is the serializable
+ * object.
+ *
+ * @param cl the Serializable class
+ * @return a direct MethodHandle for the {@code writeReplace} method of the class or
+ * {@code null} if the class does not have a {@code writeReplace} method
+ */
+ private MethodHandle getReplaceResolveForSerialization(Class> cl,
+ String methodName) {
+ if (!Serializable.class.isAssignableFrom(cl)) {
+ return null;
+ }
+
+ Class> defCl = cl;
+ while (defCl != null) {
+ try {
+ Method m = defCl.getDeclaredMethod(methodName);
+ if (m.getReturnType() != Object.class) {
+ return null;
+ }
+ int mods = m.getModifiers();
+ if (Modifier.isStatic(mods) | Modifier.isAbstract(mods)) {
+ return null;
+ } else if (Modifier.isPublic(mods) | Modifier.isProtected(mods)) {
+ // fall through
+ } else if (Modifier.isPrivate(mods) && (cl != defCl)) {
+ return null;
+ } else if (!packageEquals(cl, defCl)) {
+ return null;
+ }
+ try {
+ // Normal return
+ m.setAccessible(true);
+ return MethodHandles.lookup().unreflect(m);
+ } catch (IllegalAccessException ex0) {
+ // setAccessible should prevent IAE
+ throw new InternalError("Error", ex0);
+ }
+ } catch (NoSuchMethodException ex) {
+ defCl = defCl.getSuperclass();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the class has a static initializer.
+ * The presence of a static initializer is used to compute the serialVersionUID.
+ * @param cl a serializable classLook
+ * @return {@code true} if the class has a static initializer,
+ * otherwise {@code false}
+ */
+ public final boolean hasStaticInitializerForSerialization(Class> cl) {
+ Method m = hasStaticInitializerMethod;
+ if (m == null) {
+ try {
+ m = ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer",
+ new Class>[]{Class.class});
+ m.setAccessible(true);
+ hasStaticInitializerMethod = m;
+ } catch (NoSuchMethodException ex) {
+ throw new InternalError("No such method hasStaticInitializer on "
+ + ObjectStreamClass.class, ex);
+ }
+ }
+ try {
+ return (Boolean) m.invoke(null, cl);
+ } catch (InvocationTargetException | IllegalAccessException ex) {
+ throw new InternalError("Exception invoking hasStaticInitializer", ex);
+ }
+ }
+
+ /**
+ * Returns a new OptionalDataException with {@code eof} set to {@code true}
+ * or {@code false}.
+ * @param bool the value of {@code eof} in the created OptionalDataException
+ * @return a new OptionalDataException
+ */
+ public final OptionalDataException newOptionalDataExceptionForSerialization(boolean bool) {
+ try {
+ Constructor boolCtor =
+ OptionalDataException.class.getDeclaredConstructor(Boolean.TYPE);
+ boolCtor.setAccessible(true);
+ return boolCtor.newInstance(bool);
+ } catch (NoSuchMethodException | InstantiationException|
+ IllegalAccessException|InvocationTargetException ex) {
+ throw new InternalError("unable to create OptionalDataException", ex);
+ }
+ }
+
//--------------------------------------------------------------------------
//
// Internals only below this point
@@ -315,4 +579,17 @@ private static LangReflectAccess langReflectAccess() {
}
return langReflectAccess;
}
+
+ /**
+ * Returns true if classes are defined in the classloader and same package, false
+ * otherwise.
+ * @param cl1 a class
+ * @param cl2 another class
+ * @returns true if the two classes are in the same classloader and package
+ */
+ private static boolean packageEquals(Class> cl1, Class> cl2) {
+ return cl1.getClassLoader() == cl2.getClassLoader() &&
+ Objects.equals(cl1.getPackage(), cl2.getPackage());
+ }
+
}
diff --git a/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj b/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj
index 6b89928275..3adcdd1793 100644
--- a/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj
+++ b/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj
@@ -16,11 +16,11 @@
-
-
+
+
-
-
+
+
diff --git a/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj b/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj
index af84027354..b11eec0712 100644
--- a/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj
+++ b/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj
@@ -8,7 +8,7 @@