diff --git a/.github/workflows/IKVM.yml b/.github/workflows/IKVM.yml
index d7e07166a1..d1f1d0169e 100644
--- a/.github/workflows/IKVM.yml
+++ b/.github/workflows/IKVM.yml
@@ -282,6 +282,7 @@ jobs:
run:
- IKVM.ByteCode.Tests
- IKVM.Tests
+ - IKVM.Java.Tests
- IKVM.Tools.Importer.Tests
- IKVM.Tools.Exporter.Tests
- IKVM.Tools.Tests
@@ -304,6 +305,8 @@ jobs:
exclude:
- tfm: net461
sys: linux
+ - run: IKVM.Tests.Java
+ tfm: net6.0
- run: IKVM.Tools.Exporter.Tests
tfm: net6.0
- run: IKVM.Tools.Importer.Tests
@@ -362,34 +365,26 @@ jobs:
if: runner.os == 'Windows'
shell: pwsh
run: |
- $dir="C:\work"
+ $dir="C:\w"
mkdir $dir
mkdir $dir\temp
mkdir $dir\dotnet
- mkdir $dir\nuget
- mkdir $dir\nuget\packages
mkdir $dir\ikvm
- mkdir $dir\ikvm\dist
Add-Content $env:GITHUB_ENV "`nIKVMPATH=$dir\ikvm"
Add-Content $env:GITHUB_ENV "`nTMP=$dir\temp`nTEMP=$dir\temp`nTMPDIR=$dir\temp"
Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=$dir\dotnet"
- Add-Content $env:GITHUB_ENV "`nNUGET_PACKAGES=$dir\nuget\packages"
- name: Set Paths (Linux)
if: runner.os == 'Linux'
shell: pwsh
run: |
- $dir="${{ runner.temp }}/work"
+ $dir="${{ runner.temp }}/w"
mkdir $dir
mkdir $dir/temp
mkdir $dir/dotnet
- mkdir $dir/nuget
- mkdir $dir/nuget/packages
mkdir $dir/ikvm
- mkdir $dir/ikvm/dist
Add-Content $env:GITHUB_ENV "`nIKVMPATH=$dir/ikvm"
Add-Content $env:GITHUB_ENV "`nTMP=$dir/temp`nTEMP=$dir/temp`nTMPDIR=$dir/temp"
Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=$dir/dotnet"
- Add-Content $env:GITHUB_ENV "`nNUGET_PACKAGES=$dir/nuget/packages"
- name: Setup .NET 3.1
uses: actions/setup-dotnet@v3
with:
@@ -409,14 +404,14 @@ jobs:
uses: actions/download-artifact@v3
with:
name: tests
- path: ${{ env.IKVMPATH }}/dist
+ path: ${{ env.IKVMPATH }}
- name: Restore Tests
run: tar xzvf tests.tar.gz
- working-directory: ${{ env.IKVMPATH }}/dist
+ working-directory: ${{ env.IKVMPATH }}
- name: Delete Tests
shell: pwsh
run: ri tests.tar.gz
- working-directory: ${{ env.IKVMPATH }}/dist
+ working-directory: ${{ env.IKVMPATH }}
- name: Execute Tests
timeout-minutes: 120
shell: pwsh
@@ -448,13 +443,17 @@ jobs:
dotnet test -f $tfm --blame -v 2 --results-directory "TestResults" --logger:"console;verbosity=detailed" --logger:trx --collect "Code Coverage" $tests
}
}
- working-directory: ${{ env.IKVMPATH }}/dist
+ working-directory: ${{ env.IKVMPATH }}
+ - name: Archive Test Results
+ if: always() && startsWith(env.RET, 'TestResults--')
+ run: tar czvf ${{ env.TMPDIR }}/TestResults.tar.gz TestResults
+ working-directory: ${{ env.IKVMPATH }}
- name: Upload Test Results
if: always() && startsWith(env.RET, 'TestResults--')
uses: actions/upload-artifact@v3
with:
name: ${{ env.RET }}
- path: ${{ env.IKVMPATH }}/dist/TestResults
+ path: ${{ env.TMPDIR }}/TestResults.tar.gz
release:
name: Release
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/hotfix/')
@@ -538,14 +537,6 @@ jobs:
env:
GITHUB_REPOS: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Push NuGet (Azure DevOps)
- shell: pwsh
- run: |
- dotnet nuget add source $env:AZUREDEVOPS_REPOS --name az --username az --password $env:AZUREDEVOPS_TOKEN --store-password-in-clear-text
- dotnet nuget push dist/nuget/*.nupkg --source az --api-key az --skip-duplicate --no-symbols
- env:
- AZUREDEVOPS_REPOS: https://pkgs.dev.azure.com/ikvm-revived/ikvm/_packaging/ikvm-ci/nuget/v3/index.json
- AZUREDEVOPS_TOKEN: ${{ secrets.AZUREDEVOPS_PAT }}
- name: Push NuGet
if: github.ref == 'refs/heads/main' || github.event.head_commit.message == '+push'
shell: pwsh
diff --git a/IKVM.deps.props b/IKVM.deps.props
index cc8c723926..2280d2e86b 100644
--- a/IKVM.deps.props
+++ b/IKVM.deps.props
@@ -3,6 +3,7 @@
+
diff --git a/IKVM.sln b/IKVM.sln
index 58acd14991..67013badae 100644
--- a/IKVM.sln
+++ b/IKVM.sln
@@ -283,10 +283,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Importer", "src\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Importer.Tests", "src\IKVM.Tools.Importer.Tests\IKVM.Tools.Importer.Tests.csproj", "{6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tests.Java", "src\IKVM.Tests.Java\IKVM.Tests.Java.msbuildproj", "{87CB99AE-16D1-4D58-BEAC-72485E02B321}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.ByteCode.Tests", "src\IKVM.ByteCode.Tests\IKVM.ByteCode.Tests.csproj", "{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Java.Tests", "src\IKVM.Java.Tests\IKVM.Java.Tests.msbuildproj", "{B1B3F6B2-8A32-4C49-A9D2-154CD084CD9D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -605,14 +605,14 @@ Global
{6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Release|Any CPU.Build.0 = Release|Any CPU
- {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Release|Any CPU.Build.0 = Release|Any CPU
{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B1B3F6B2-8A32-4C49-A9D2-154CD084CD9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B1B3F6B2-8A32-4C49-A9D2-154CD084CD9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B1B3F6B2-8A32-4C49-A9D2-154CD084CD9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B1B3F6B2-8A32-4C49-A9D2-154CD084CD9D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/README.md b/README.md
index a9a2156703..dd45a4823c 100644
--- a/README.md
+++ b/README.md
@@ -92,7 +92,7 @@ IKVM includes build-time support for translating Java libraries to .NET assembli
-
+
```
@@ -102,7 +102,7 @@ The output assembly will be generated as part of your project's build process an
```xml
-
+
MyAssembly
3.2.1.0
3.0.0.0
diff --git a/ext/helloworld-2.0.jar b/ext/helloworld/helloworld-2.0.jar
similarity index 100%
rename from ext/helloworld-2.0.jar
rename to ext/helloworld/helloworld-2.0.jar
diff --git a/ext/helloworld-mod.jar b/ext/helloworld/helloworld-mod.jar
similarity index 100%
rename from ext/helloworld-mod.jar
rename to ext/helloworld/helloworld-mod.jar
diff --git a/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs b/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs
index 2e3e37b5d8..354e50c987 100644
--- a/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs
+++ b/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs
@@ -34,17 +34,6 @@ public class JTRegTestManager
static readonly MD5 md5 = MD5.Create();
- ///
- /// Computes the hash of the value.
- ///
- ///
- ///
- static byte[] ComputeHash(byte[] buffer)
- {
- lock (md5)
- return md5.ComputeHash(buffer);
- }
-
///
/// Initializes the static instance.
///
@@ -82,10 +71,22 @@ static JTRegTestManager()
///
///
///
- protected static string GetSourceHash(string source)
+ protected static string GetSourceId(string source)
{
+ // allows a lock around the MD5 instance
+ byte[] ComputeHash(byte[] buffer)
+ {
+ lock (md5)
+ {
+ var b = new byte[8];
+ var h = md5.ComputeHash(buffer);
+ Array.Copy(h, b, 8);
+ return b;
+ }
+ }
+
var b = ComputeHash(Encoding.UTF8.GetBytes(source));
- var s = new StringBuilder(32);
+ var s = new StringBuilder(16);
for (int i = 0; i < b.Length; i++)
s.Append(b[i].ToString("x2"));
return s.ToString();
@@ -182,11 +183,11 @@ public void DiscoverTests(string source, IJTRegDiscoveryContext context, Cancell
return;
// output path for jtreg state
- var id = GetSourceHash(source);
+ var id = GetSourceId(source);
var baseDir = Path.Combine(Path.GetTempPath(), BASEDIR_PREFIX + id);
// attempt to create a temporary directory as scratch space for this test
- context.SendMessage(JTRegTestMessageLevel.Informational, $"JTReg: Using run directory: {baseDir}");
+ context.SendMessage(JTRegTestMessageLevel.Informational, $"JTReg: Using discover directory: {baseDir}");
Directory.CreateDirectory(baseDir);
// output to framework
@@ -293,8 +294,11 @@ internal void RunTestForSource(string source, IJTRegExecutionContext context, Li
return;
// output path for jtreg state
- var id = GetSourceHash(source);
+ var id = GetSourceId(source);
var baseDir = Path.Combine(context.TestRunDirectory, BASEDIR_PREFIX + id);
+
+ // attempt to create a temporary directory as scratch space for this test
+ context.SendMessage(JTRegTestMessageLevel.Informational, $"JTReg: Using run directory: {baseDir}");
Directory.CreateDirectory(baseDir);
// output to framework
diff --git a/src/IKVM.Java-ref/java/lang/Class.cs b/src/IKVM.Java-ref/java/lang/Class.cs
index 3075fc094a..f2453f4522 100644
--- a/src/IKVM.Java-ref/java/lang/Class.cs
+++ b/src/IKVM.Java-ref/java/lang/Class.cs
@@ -1,9 +1,16 @@
-namespace java.lang
+using System;
+
+namespace java.lang
{
public class Class
{
+ public Class(Type type)
+ {
+
+ }
+
}
}
diff --git a/src/IKVM.Java.Extensions/java/util/IteratorExtensions.cs b/src/IKVM.Java.Extensions/java/util/IteratorExtensions.cs
index fc7ce72143..e055609cff 100644
--- a/src/IKVM.Java.Extensions/java/util/IteratorExtensions.cs
+++ b/src/IKVM.Java.Extensions/java/util/IteratorExtensions.cs
@@ -36,6 +36,18 @@ public static List RemainingToList(this Iterator iterator)
return l;
}
+ ///
+ /// Returns an enumerable that iterators over the items in an iterator.
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable RemainingToEnumerable(this Iterator iterator)
+ {
+ while (iterator.hasNext())
+ yield return (T)iterator.next();
+ }
+
}
}
diff --git a/src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj b/src/IKVM.Java.Tests/IKVM.Java.Tests.msbuildproj
similarity index 94%
rename from src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj
rename to src/IKVM.Java.Tests/IKVM.Java.Tests.msbuildproj
index a08ebb273c..ebf39a0f2a 100644
--- a/src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj
+++ b/src/IKVM.Java.Tests/IKVM.Java.Tests.msbuildproj
@@ -19,10 +19,6 @@
-
-
-
-
diff --git a/src/IKVM.Java.Tests/java/util/concurrent/ForkJoinPoolTests.java b/src/IKVM.Java.Tests/java/util/concurrent/ForkJoinPoolTests.java
new file mode 100644
index 0000000000..4fd8b929a6
--- /dev/null
+++ b/src/IKVM.Java.Tests/java/util/concurrent/ForkJoinPoolTests.java
@@ -0,0 +1,48 @@
+package ikvm.tests.java.java.util.concurrent;
+
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+@cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.Annotation()
+public class ForkJoinPoolTests {
+
+ class RecursiveActionTestTask extends RecursiveAction {
+
+ private int range;
+ private AtomicInteger count;
+
+ public RecursiveActionTestTask(int range, AtomicInteger count) {
+ this.range = range;
+ this.count = count;
+ }
+
+ protected void compute() {
+ if (this.range > 1) {
+ RecursiveActionTestTask t1 = new RecursiveActionTestTask(this.range / 2, count);
+ t1.fork();
+ RecursiveActionTestTask t2 = new RecursiveActionTestTask(this.range / 2, count);
+ t2.fork();
+
+ t1.join();
+ t2.join();
+ } else {
+ count.incrementAndGet();
+ }
+ }
+
+ }
+
+ @cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Annotation()
+ public void canExecuteRecursiveAction() throws Exception {
+ ForkJoinPool p = ForkJoinPool.commonPool();
+ AtomicInteger c = new AtomicInteger();
+ RecursiveActionTestTask t = new RecursiveActionTestTask(1024 * 1024, c);
+ p.invoke(t);
+ if (c.get() != 1024 * 1024) {
+ throw new Exception();
+ }
+ }
+
+}
diff --git a/src/IKVM.Java/local/ikvm/runtime/Launcher.java b/src/IKVM.Java/local/ikvm/runtime/Launcher.java
deleted file mode 100644
index da31e65049..0000000000
--- a/src/IKVM.Java/local/ikvm/runtime/Launcher.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package ikvm.runtime;
-
-import java.util.Properties;
-
-import cli.System.Type;
-
-public final class Launcher
-{
-
- private Launcher()
- {
-
- }
-
- /**
- * Launches the given .NET type as a Java application class. This method should be invoked before any other JVM work is
- * conducted, ideally by generated executables.
- */
- @cli.IKVM.Attributes.HideFromJavaAttribute.Annotation
- public static native int run(Type main, String[] args, String jvmArgPrefix, Properties properties);
-
-}
diff --git a/src/IKVM.Java/local/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/IKVM.Java/local/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index f2e5a5c8a2..dce35765df 100644
--- a/src/IKVM.Java/local/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/src/IKVM.Java/local/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -38,7 +38,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import sun.misc.Unsafe;
/**
* Provides a framework for implementing blocking locks and related
@@ -378,7 +378,6 @@ protected AbstractQueuedSynchronizer() { }
* on the design of this class.
*/
static final class Node {
- static final AtomicReferenceFieldUpdater nextUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
@@ -544,7 +543,7 @@ protected final int getState() {
/**
* Sets the value of synchronization state.
- * This operation has memory semantics of a {@code volatile} read.
+ * This operation has memory semantics of a {@code volatile} write.
* @param newState the new state value
*/
protected final void setState(int newState) {
@@ -562,7 +561,10 @@ protected final void setState(int newState) {
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
- protected final native boolean compareAndSetState(int expect, int update); // implemented in map.xml
+ protected final boolean compareAndSetState(int expect, int update) {
+ // See below for intrinsics setup to support this
+ return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
+ }
// Queuing utilities
@@ -661,7 +663,7 @@ private void unparkSuccessor(Node node) {
}
/**
- * Release action for shared mode -- signals successor and ensure
+ * Release action for shared mode -- signals successor and ensures
* propagation. (Note: For exclusive mode, release just amounts
* to calling unparkSuccessor of head if it needs signal.)
*/
@@ -2247,39 +2249,67 @@ protected final Collection getWaitingThreads() {
}
/**
- * IKVM specific. We use AtomicReferenceFieldUpdater instead of Unsafe primitives.
+ * Setup to support compareAndSet. We need to natively implement
+ * this here: For the sake of permitting future enhancements, we
+ * cannot explicitly subclass AtomicInteger, which would be
+ * efficient and useful otherwise. So, as the lesser of evils, we
+ * natively implement using hotspot intrinsics API. And while we
+ * are at it, we do the same for other CASable fields (which could
+ * otherwise be done with atomic field updaters).
*/
- private static final AtomicReferenceFieldUpdater headUpdater =
- AtomicReferenceFieldUpdater.newUpdater(AbstractQueuedSynchronizer.class, Node.class, "head");
- private static final AtomicReferenceFieldUpdater tailUpdater =
- AtomicReferenceFieldUpdater.newUpdater(AbstractQueuedSynchronizer.class, Node.class, "tail");
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final long stateOffset;
+ private static final long headOffset;
+ private static final long tailOffset;
+ private static final long waitStatusOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ stateOffset = unsafe.objectFieldOffset
+ (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
+ headOffset = unsafe.objectFieldOffset
+ (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
+ tailOffset = unsafe.objectFieldOffset
+ (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
+ waitStatusOffset = unsafe.objectFieldOffset
+ (Node.class.getDeclaredField("waitStatus"));
+ nextOffset = unsafe.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+
+ } catch (Exception ex) { throw new Error(ex); }
+ }
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
- return headUpdater.compareAndSet(this, null, update);
+ return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return tailUpdater.compareAndSet(this, expect, update);
+ return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
/**
* CAS waitStatus field of a node.
*/
- private static final native boolean compareAndSetWaitStatus(Node node,
+ private static final boolean compareAndSetWaitStatus(Node node,
int expect,
- int update); // implemented in map.xml
+ int update) {
+ return unsafe.compareAndSwapInt(node, waitStatusOffset,
+ expect, update);
+ }
+
/**
* CAS next field of a node.
*/
private static final boolean compareAndSetNext(Node node,
Node expect,
Node update) {
- return Node.nextUpdater.compareAndSet(node, expect, update);
+ return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
}
diff --git a/src/IKVM.Java/local/java/util/concurrent/locks/LockSupport.java b/src/IKVM.Java/local/java/util/concurrent/locks/LockSupport.java
index 45a3811296..46a0ab597c 100644
--- a/src/IKVM.Java/local/java/util/concurrent/locks/LockSupport.java
+++ b/src/IKVM.Java/local/java/util/concurrent/locks/LockSupport.java
@@ -34,6 +34,7 @@
*/
package java.util.concurrent.locks;
+import sun.misc.Unsafe;
/**
* Basic thread blocking primitives for creating locks and other
@@ -120,18 +121,10 @@ public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
- t.parkBlocker = arg;
+ // Even though volatile, hotspot doesn't need a write barrier here.
+ UNSAFE.putObject(t, parkBlockerOffset, arg);
}
- private static final int PARK_STATE_RUNNING = 0;
- private static final int PARK_STATE_PERMIT = 1;
- private static final int PARK_STATE_PARKED = 2;
-
- // these native methods are all implemented in map.xml
- private static native int cmpxchgParkState(Thread t, int newValue, int comparand);
- private static native Object getParkLock(Thread t);
- private static native void setParkLock(Thread t, Object obj);
-
/**
* Makes available the permit for the given thread, if it
* was not already available. If the thread was blocked on
@@ -145,63 +138,7 @@ private static void setBlocker(Thread t, Object arg) {
*/
public static void unpark(Thread thread) {
if (thread != null)
- {
- if (cmpxchgParkState(thread, PARK_STATE_PERMIT, PARK_STATE_RUNNING) == PARK_STATE_PARKED)
- {
- if (cmpxchgParkState(thread, PARK_STATE_RUNNING, PARK_STATE_PARKED) == PARK_STATE_PARKED)
- {
- // thread is currently blocking, so we have to release it
- Object lock = getParkLock(thread);
- synchronized (lock)
- {
- lock.notify();
- }
- }
- }
- }
- }
-
- private static void parkImpl(Thread currentThread, boolean deadline, long nanos)
- {
- if (cmpxchgParkState(currentThread, PARK_STATE_RUNNING, PARK_STATE_PERMIT) == PARK_STATE_PERMIT)
- {
- // we consumed a permit
- return;
- }
-
- Object lock = getParkLock(currentThread);
- if (lock == null)
- {
- // we lazily allocate the lock object
- lock = new Object();
- setParkLock(currentThread, lock);
- }
- synchronized (lock)
- {
- if (cmpxchgParkState(currentThread, PARK_STATE_PARKED, PARK_STATE_RUNNING) == PARK_STATE_PERMIT)
- {
- // entering the parked state failed because we got a permit after the previous permit test
- // release the permit and return
- cmpxchgParkState(currentThread, PARK_STATE_RUNNING, PARK_STATE_PERMIT);
- return;
- }
- if (deadline)
- {
- nanos -= System.currentTimeMillis() * 1000000;
- }
- if (nanos >= 0)
- {
- try
- {
- lock.wait(nanos / 1000000, (int)(nanos % 1000000));
- }
- catch (InterruptedException _)
- {
- currentThread.interrupt();
- }
- }
- cmpxchgParkState(currentThread, PARK_STATE_RUNNING, PARK_STATE_PARKED);
- }
+ UNSAFE.unpark(thread);
}
/**
@@ -235,7 +172,7 @@ private static void parkImpl(Thread currentThread, boolean deadline, long nanos)
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- parkImpl(t, false, 0L);
+ UNSAFE.park(false, 0L);
setBlocker(t, null);
}
@@ -275,7 +212,7 @@ public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- parkImpl(t, false, nanos);
+ UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
@@ -316,7 +253,7 @@ public static void parkNanos(Object blocker, long nanos) {
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- parkImpl(t, true, deadline * 1000000);
+ UNSAFE.park(true, deadline);
setBlocker(t, null);
}
@@ -335,7 +272,7 @@ public static void parkUntil(Object blocker, long deadline) {
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
- return t.parkBlocker;
+ return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
/**
@@ -364,7 +301,7 @@ public static Object getBlocker(Thread t) {
* for example, the interrupt status of the thread upon return.
*/
public static void park() {
- parkImpl(Thread.currentThread(), false, 0L);
+ UNSAFE.park(false, 0L);
}
/**
@@ -398,7 +335,7 @@ public static void park() {
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
- parkImpl(Thread.currentThread(), false, nanos);
+ UNSAFE.park(false, nanos);
}
/**
@@ -432,7 +369,7 @@ public static void parkNanos(long nanos) {
* to wait until
*/
public static void parkUntil(long deadline) {
- parkImpl(Thread.currentThread(), true, deadline * 1000000);
+ UNSAFE.park(true, deadline);
}
/**
@@ -442,15 +379,36 @@ public static void parkUntil(long deadline) {
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
- if ((r = t.threadLocalRandomSecondarySeed) != 0) {
+ if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
- t.threadLocalRandomSecondarySeed = r;
+ UNSAFE.putInt(t, SECONDARY, r);
return r;
}
+ // Hotspot implementation via intrinsics API
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long parkBlockerOffset;
+ private static final long SEED;
+ private static final long PROBE;
+ private static final long SECONDARY;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class> tk = Thread.class;
+ parkBlockerOffset = UNSAFE.objectFieldOffset
+ (tk.getDeclaredField("parkBlocker"));
+ SEED = UNSAFE.objectFieldOffset
+ (tk.getDeclaredField("threadLocalRandomSeed"));
+ PROBE = UNSAFE.objectFieldOffset
+ (tk.getDeclaredField("threadLocalRandomProbe"));
+ SECONDARY = UNSAFE.objectFieldOffset
+ (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
+ } catch (Exception ex) { throw new Error(ex); }
+ }
+
}
diff --git a/src/IKVM.Java/map.xml b/src/IKVM.Java/map.xml
index 39eab4a394..0a1f628502 100644
--- a/src/IKVM.Java/map.xml
+++ b/src/IKVM.Java/map.xml
@@ -33,6 +33,12 @@
Never
+
+
+
+
+
+
@@ -1618,62 +1624,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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 d0ecb25885..0691ae9950 100644
--- a/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj
+++ b/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj
@@ -7,10 +7,10 @@
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/src/IKVM.MSBuild.Tasks.Tests/IkvmReferenceItemPrepareTests.cs b/src/IKVM.MSBuild.Tasks.Tests/IkvmReferenceItemPrepareTests.cs
index 66a358ab55..b828d76b74 100644
--- a/src/IKVM.MSBuild.Tasks.Tests/IkvmReferenceItemPrepareTests.cs
+++ b/src/IKVM.MSBuild.Tasks.Tests/IkvmReferenceItemPrepareTests.cs
@@ -16,7 +16,7 @@ namespace IKVM.MSBuild.Tasks.Tests
public class IkvmReferenceItemPrepareTests
{
- readonly static string HELLOWORLD1_JAR = @".\ext\helloworld-2.0-1\helloworld-2.0.jar";
+ readonly static string HELLOWORLD1_JAR = @".\helloworld\helloworld-2.0-1\helloworld-2.0.jar";
///
/// Builds a new task instance with various information.
@@ -38,17 +38,17 @@ IkvmReferenceItemPrepare BuildTestTask(string toolFramework, string toolVersion)
[TestMethod]
public void Should_normalize_jar_itemspec()
{
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
IkvmReferenceItemPrepare.AssignMetadata(IkvmReferenceItem.Import(new[] { i1 }));
- i1.ItemSpec.Should().Be(Path.Combine("ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ i1.ItemSpec.Should().Be(Path.Combine("helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
}
[TestMethod]
public void Should_normalize_dir_itemspec()
{
- var i1 = new TaskItem(@".\ext");
+ var i1 = new TaskItem(@".\helloworld");
IkvmReferenceItemPrepare.AssignMetadata(IkvmReferenceItem.Import(new[] { i1 }));
- i1.ItemSpec.Should().Be(@"ext" + Path.DirectorySeparatorChar);
+ i1.ItemSpec.Should().Be(@"helloworld" + Path.DirectorySeparatorChar);
}
[TestMethod]
@@ -62,30 +62,30 @@ public void Should_not_normalize_unknown_itemspec()
[TestMethod]
public void Should_add_jar_identity_to_compile()
{
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
IkvmReferenceItemPrepare.AssignMetadata(IkvmReferenceItem.Import(new[] { i1 }));
var c = i1.GetMetadata(IkvmReferenceItemMetadata.Compile);
- c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().Contain(Path.Combine("ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().Contain(Path.Combine("helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
}
[TestMethod]
public void Should_not_add_dir_identity_to_compile()
{
- var i1 = new TaskItem(Path.Combine(".", "ext"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld"));
IkvmReferenceItemPrepare.AssignMetadata(IkvmReferenceItem.Import(new[] { i1 }));
var c = i1.GetMetadata(IkvmReferenceItemMetadata.Compile);
- c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().NotContain(@"ext");
+ c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().NotContain(@"helloworld");
}
[TestMethod]
public void Should_resolve_reference()
{
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
- var i2 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-2", "helloworld-2.0.jar"));
- i2.SetMetadata(IkvmReferenceItemMetadata.References, Path.Combine(".", "ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ var i2 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-2", "helloworld-2.0.jar"));
+ i2.SetMetadata(IkvmReferenceItemMetadata.References, Path.Combine(".", "helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
IkvmReferenceItemPrepare.AssignMetadata(IkvmReferenceItem.Import(new[] { i1, i2 }));
var c = i2.GetMetadata(IkvmReferenceItemMetadata.References);
- c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().Contain(Path.Combine("ext", "helloworld-2.0-1", "helloworld-2.0.jar"));
+ c.Split(IkvmReferenceItemMetadata.PropertySeperatorChar).Should().Contain(Path.Combine("helloworld", "helloworld-2.0-1", "helloworld-2.0.jar"));
}
[TestMethod]
@@ -115,9 +115,9 @@ public void Should_fail_when_missing_name()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyVersion, "2.0");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
errors.Should().Contain(x => x.Code == "IKVMSDK0011");
@@ -132,9 +132,9 @@ public void Should_fail_when_missing_version()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyName, "helloworld");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
errors.Should().Contain(x => x.Code == "IKVMSDK0012");
@@ -149,9 +149,9 @@ public void Should_fail_when_missing_file_version()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyName, "helloworld");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
errors.Should().Contain(x => x.Code == "IKVMSDK0013");
@@ -166,10 +166,10 @@ public void Should_fail_when_invalid_version()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyName, "helloworld");
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyVersion, "invalid");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
errors.Should().Contain(x => x.Code == "IKVMSDK0003");
@@ -202,10 +202,10 @@ public void Should_fail_when_invalid_sources()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyName, "helloworld");
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyVersion, "2.0");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.Sources, "invalid");
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
@@ -221,10 +221,10 @@ public void Should_fail_when_missing_sources()
var t = new IkvmReferenceItemPrepare();
t.BuildEngine = engine.Object;
- var i1 = new TaskItem(Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ var i1 = new TaskItem(Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyName, "helloworld");
i1.SetMetadata(IkvmReferenceItemMetadata.AssemblyVersion, "2.0");
- i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "ext", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
+ i1.SetMetadata(IkvmReferenceItemMetadata.Compile, Path.Combine(".", "helloworld", "helloworld-2.0-1", ".", "helloworld-2.0.jar"));
i1.SetMetadata(IkvmReferenceItemMetadata.Sources, "missing.java");
t.Items = new[] { i1 };
t.Validate(IkvmReferenceItem.Import(t.Items)).Should().BeFalse();
diff --git a/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj b/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj
index 2bf9111ad9..fb867cc26d 100644
--- a/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj
+++ b/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj
@@ -11,10 +11,10 @@
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets
index 7d323d8e47..fd9b16e6df 100644
--- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets
+++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets
@@ -1,4 +1,4 @@
-
+
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
diff --git a/src/IKVM.Runtime/Accessors/Accessor.cs b/src/IKVM.Runtime/Accessors/Accessor.cs
index 44fd8373d4..df3f483339 100644
--- a/src/IKVM.Runtime/Accessors/Accessor.cs
+++ b/src/IKVM.Runtime/Accessors/Accessor.cs
@@ -48,6 +48,25 @@ public Accessor(AccessorTypeResolver resolver, string typeName)
///
protected Type Resolve(ref Type type, string typeName) => AccessorUtil.LazyGet(ref type, () => Resolve(typeName));
+ ///
+ /// Creates a new array of the accessed type.
+ ///
+ ///
+ ///
+ public object[] InitArray(int length) => (object[])Array.CreateInstance(Type, length);
+
+ ///
+ /// Creates a new array of the accessed type from the elements in the given array.
+ ///
+ ///
+ ///
+ public object[] InitArray(params object[] source)
+ {
+ var a = InitArray(source.Length);
+ Array.Copy(source, a, source.Length);
+ return a;
+ }
+
}
///
diff --git a/src/IKVM.Runtime/Accessors/AccessorRef.cs b/src/IKVM.Runtime/Accessors/AccessorRef.cs
new file mode 100644
index 0000000000..4a01158adf
--- /dev/null
+++ b/src/IKVM.Runtime/Accessors/AccessorRef.cs
@@ -0,0 +1,24 @@
+namespace IKVM.Runtime.Accessors
+{
+
+#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false
+
+ ///
+ /// Represents a reference to an accessor.
+ ///
+ ///
+ internal struct AccessorRef where T : Accessor
+ {
+
+ T accessor;
+
+ ///
+ /// Gets the accessor value.
+ ///
+ public T Value => JVM.BaseAccessors.Get(ref accessor);
+
+ }
+
+#endif
+
+}
diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ClassAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ClassAccessor.cs
new file mode 100644
index 0000000000..1b88c73b43
--- /dev/null
+++ b/src/IKVM.Runtime/Accessors/Java/Lang/ClassAccessor.cs
@@ -0,0 +1,67 @@
+using System;
+
+namespace IKVM.Runtime.Accessors.Java.Lang
+{
+
+#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false
+
+ ///
+ /// Provides runtime access to the 'java.lang.Class' type.
+ ///
+ internal sealed class ClassAccessor : Accessor
public static object MainThreadGroup => mainThreadGroup.Value;
+#endif
+
///
/// Ensures the JVM is initialized.
///
public static void EnsureInitialized()
{
+#if FIRST_PASS || IMPORTER || EXPORTER
+ throw new NotImplementedException();
+#else
if (initialized == false)
{
lock (initializedLock)
@@ -104,11 +112,52 @@ public static void EnsureInitialized()
if (initialized == false)
{
initialized = true;
+
+ // always required
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.String).TypeHandle);
+
+ // initialize java_lang.System (needed before creating the thread)
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.System).TypeHandle);
+
+ // initialize thread groups
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.ThreadGroup).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.Thread).TypeHandle);
+ GC.KeepAlive(SystemThreadGroup);
+ GC.KeepAlive(MainThreadGroup);
+
+ // the VM creates & returns objects of this class
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.Class).TypeHandle);
+
+ // the VM preresolves methods to these classes
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.reflect.Method).TypeHandle);
+
+ // ensure the System class is initialized
SystemAccessor.InvokeInitializeSystemClass();
+
+ // should be done before System, but that fails for now
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.@ref.Finalizer).TypeHandle);
+
+ // initialize certain classes after System properties are available
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.OutOfMemoryError).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.NullPointerException).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.ClassCastException).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.ArrayStoreException).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.ArithmeticException).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.StackOverflowError).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.IllegalMonitorStateException).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.IllegalArgumentException).TypeHandle);
+
+ // the static initializer of java.lang.Compiler tries to read property "java.compiler" and read & write property "java.vm.info"
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.Compiler).TypeHandle);
+
+ // initialize some JSR292 core classes to avoid deadlock during class loading
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.invoke.MethodHandle).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.invoke.MemberName).TypeHandle);
+ RuntimeHelpers.RunClassConstructor(typeof(java.lang.invoke.MethodHandleNatives).TypeHandle);
}
}
}
-
+#endif
}
///
@@ -117,7 +166,11 @@ public static void EnsureInitialized()
///
static object MakeSystemThreadGroup()
{
+#if FIRST_PASS || IMPORTER || EXPORTER
+ throw new NotImplementedException();
+#else
return ThreadGroupAccessor.Init();
+#endif
}
///
@@ -126,10 +179,12 @@ static object MakeSystemThreadGroup()
///
static object MakeMainThreadGroup()
{
+#if FIRST_PASS || IMPORTER || EXPORTER
+ throw new NotImplementedException();
+#else
return ThreadGroupAccessor.Init(null, SystemThreadGroup, "main");
- }
-
#endif
+ }
///
/// Gets an environmental variable.
diff --git a/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs b/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs
index cea01b3998..c2c7773dbe 100644
--- a/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs
+++ b/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs
@@ -27,12 +27,12 @@ Jeroen Frijters
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
-using System.Text;
using IKVM.Runtime.Vfs;
namespace IKVM.Java.Externs.java.io
{
+
static class WinNTFileSystem
{
@@ -49,13 +49,17 @@ public static string getDriveDirectory(object _this, int drive)
}
catch (ArgumentException)
{
+
}
catch (SecurityException)
{
+
}
catch (PathTooLongException)
{
+
}
+
return "\\";
}
@@ -125,29 +129,18 @@ static string CanonicalizePath(string path)
return path;
}
- public static string canonicalize0(object _this, string path)
+ public static string canonicalize0(object self, string path)
{
#if FIRST_PASS
- return null;
+ throw new NotImplementedException();
#else
try
{
- // TODO there is still a known bug here. A dotted path component right after the root component
- // are not removed as they should be. E.g. "c:\..." => "C:\..." or "\\server\..." => IOException
- // Another know issue is that when running under Mono on Windows, the case names aren't converted
- // to the correct (on file system) casing.
- //
- // FXBUG we're appending the directory separator to work around an apparent .NET bug.
- // If we don't do this, "c:\j\." would be canonicalized to "C:\"
- int colon = path.IndexOf(':', 2);
- if (colon != -1)
- return CanonicalizePath(path.Substring(0, colon) + Path.DirectorySeparatorChar) + path.Substring(colon);
-
- return CanonicalizePath(path + Path.DirectorySeparatorChar);
+ return CanonicalizePath(Path.IsPathRooted(path) == false ? Path.GetFullPath(path) : path);
}
- catch (ArgumentException x)
+ catch (Exception e)
{
- throw new global::java.io.IOException(x.Message);
+ throw new global::java.io.IOException(e);
}
#endif
}
@@ -160,7 +153,7 @@ public static string canonicalizeWithPrefix0(object _this, string canonicalPrefi
private static string GetPathFromFile(global::java.io.File file)
{
#if FIRST_PASS
- return null;
+ throw new NotImplementedException();
#else
return file.getPath();
#endif
@@ -196,19 +189,25 @@ public static int getBooleanAttributes(object _this, global::java.io.File f)
}
catch (ArgumentException)
{
+
}
catch (UnauthorizedAccessException)
{
+
}
catch (SecurityException)
{
+
}
catch (NotSupportedException)
{
+
}
catch (IOException)
{
+
}
+
return 0;
}
@@ -288,20 +287,26 @@ public static bool checkAccess(object _this, global::java.io.File f, int access)
}
catch (SecurityException)
{
+
}
catch (ArgumentException)
{
+
}
catch (UnauthorizedAccessException)
{
+
}
catch (IOException)
{
+
}
catch (NotSupportedException)
{
+
}
}
+
return ok;
}
@@ -567,19 +572,25 @@ public static bool createDirectory(object _this, global::java.io.File f)
}
catch (SecurityException)
{
+
}
catch (ArgumentException)
{
+
}
catch (UnauthorizedAccessException)
{
+
}
catch (IOException)
{
+
}
catch (NotSupportedException)
{
+
}
+
return false;
}
@@ -690,9 +701,8 @@ public static long getSpace0(object _this, global::java.io.File f, int t)
long freeAvailable;
long total;
long totalFree;
- StringBuilder volname = new StringBuilder(256);
- if (GetVolumePathName(GetPathFromFile(f), volname, volname.Capacity) != 0
- && GetDiskFreeSpaceEx(volname.ToString(), out freeAvailable, out total, out totalFree) != 0)
+ var volname = new System.Text.StringBuilder(256);
+ if (GetVolumePathName(GetPathFromFile(f), volname, volname.Capacity) != 0 && GetDiskFreeSpaceEx(volname.ToString(), out freeAvailable, out total, out totalFree) != 0)
{
switch (t)
{
@@ -705,6 +715,7 @@ public static long getSpace0(object _this, global::java.io.File f, int t)
}
}
#endif
+
return 0;
}
@@ -712,11 +723,13 @@ public static long getSpace0(object _this, global::java.io.File f, int t)
private static extern int GetDiskFreeSpaceEx(string directory, out long freeAvailable, out long total, out long totalFree);
[DllImport("kernel32")]
- private static extern int GetVolumePathName(string lpszFileName, [In, Out] StringBuilder lpszVolumePathName, int cchBufferLength);
+ private static extern int GetVolumePathName(string lpszFileName, [In, Out] System.Text.StringBuilder lpszVolumePathName, int cchBufferLength);
public static void initIDs()
{
+
}
+
}
}
\ No newline at end of file
diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/System.cs b/src/IKVM.Runtime/Java/Externs/java/lang/System.cs
index 33ea771b2f..8794705007 100644
--- a/src/IKVM.Runtime/Java/Externs/java/lang/System.cs
+++ b/src/IKVM.Runtime/Java/Externs/java/lang/System.cs
@@ -33,11 +33,7 @@ static class System
///
public static void registerNatives()
{
-#if FIRST_PASS
- throw new NotImplementedException();
-#else
- JVM.EnsureInitialized();
-#endif
+
}
///
diff --git a/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs b/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs
index 417efe93f0..e0aa65c80f 100644
--- a/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs
+++ b/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs
@@ -196,7 +196,7 @@ public static object getObject(object self, object o, long offset)
#else
return o switch
{
- object[] array => array[offset],
+ object[] array => array[offset / IntPtr.Size],
_ => GetField(o, offset)
};
#endif
@@ -217,7 +217,7 @@ public static void putObject(object self, object o, long offset, object x)
switch (o)
{
case object[] array:
- array[offset] = x;
+ array[offset / IntPtr.Size] = x;
break;
default:
PutField(o, offset, x);
@@ -1057,30 +1057,38 @@ public static int arrayBaseOffset(object self, global::java.lang.Class arrayClas
}
///
- /// Implementation of native method 'arrayIndexScale'.
+ /// Determines the index scale for the specified array type.
///
- ///
- ///
+ ///
///
- public static int arrayIndexScale(object self, global::java.lang.Class arrayClass)
+ static int ArrayIndexScale(TypeWrapper tw)
{
- var tw = TypeWrapper.FromClass(arrayClass);
- var ac = tw.TypeAsTBD;
-
- if (ac == typeof(byte[]) || ac == typeof(bool[]))
+ var et = tw.ElementTypeWrapper;
+ if (et == PrimitiveTypeWrapper.BYTE || et == PrimitiveTypeWrapper.BOOLEAN)
return 1;
-
- if (ac == typeof(char[]) || ac == typeof(short[]))
+ else if (et == PrimitiveTypeWrapper.CHAR || et == PrimitiveTypeWrapper.SHORT)
return 2;
-
- if (ac == typeof(int[]) || ac == typeof(float[]) || ac == typeof(object[]))
+ else if (et == PrimitiveTypeWrapper.INT || et == PrimitiveTypeWrapper.FLOAT)
return 4;
-
- if (ac == typeof(long[]) || ac == typeof(double[]))
+ else if (et == PrimitiveTypeWrapper.LONG || et == PrimitiveTypeWrapper.DOUBLE)
return 8;
+ else if (et.IsPrimitive == false && et.IsNonPrimitiveValueType)
+ return Marshal.SizeOf(et.TypeAsTBD);
+ else if (et.IsPrimitive == false && et.IsNonPrimitiveValueType == false)
+ return IntPtr.Size;
+ else
+ return 1;
+ }
- // don't change this, the Unsafe intrinsics depend on this value
- return 1;
+ ///
+ /// Implementation of native method 'arrayIndexScale'.
+ ///
+ ///
+ ///
+ ///
+ public static int arrayIndexScale(object self, global::java.lang.Class arrayClass)
+ {
+ return ArrayIndexScale(TypeWrapper.FromClass(arrayClass));
}
///
@@ -1279,6 +1287,9 @@ static Delegate CreateGetArrayVolatileDelegate(TypeWrapper tw)
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Conv_Ovf_I);
+ il.Emit(OpCodes.Ldc_I4, ArrayIndexScale(tw.MakeArrayType(1)));
+ il.Emit(OpCodes.Div);
+ il.Emit(OpCodes.Conv_Ovf_I);
il.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType);
if (tw.IsWidePrimitive == false)
@@ -1334,6 +1345,9 @@ static Delegate CreatePutArrayVolatileDelegate(TypeWrapper tw)
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Conv_Ovf_I);
+ il.Emit(OpCodes.Ldc_I4, ArrayIndexScale(tw.MakeArrayType(1)));
+ il.Emit(OpCodes.Div);
+ il.Emit(OpCodes.Conv_Ovf_I);
il.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType);
il.Emit(OpCodes.Ldarg_2);
@@ -1392,7 +1406,7 @@ public static object getObjectVolatile(object self, object o, long offset)
switch (o)
{
case object[] array when array.GetType() == typeof(object[]):
- return Volatile.Read(ref array[offset]);
+ return Volatile.Read(ref array[offset / IntPtr.Size]);
case object[] array:
return GetArrayObjectVolatile(array, offset);
default:
@@ -1416,7 +1430,7 @@ public static void putObjectVolatile(object self, object o, long offset, object
switch (o)
{
case object[] array when array.GetType() == typeof(object[]):
- Volatile.Write(ref array[offset], x);
+ Volatile.Write(ref array[offset / IntPtr.Size], x);
break;
case object[] array:
PutArrayObjectVolatile(array, offset, x);
@@ -1833,7 +1847,7 @@ static Delegate CreateCompareExchangeArrayDelegate(TypeWrapper tw)
static object CompareAndSwapArray(T[] o, long offset, T value, T comparand)
where T : class
{
- return Interlocked.CompareExchange(ref o[offset], value, comparand);
+ return Interlocked.CompareExchange(ref o[offset / ArrayIndexScale(ClassLoaderWrapper.GetWrapperFromType(o.GetType()))], value, comparand);
}
///
@@ -1879,7 +1893,7 @@ public static bool compareAndSwapObject(object self, object o, long offset, obje
#else
return o switch
{
- object[] array when array.GetType() == typeof(object[]) => Interlocked.CompareExchange(ref array[offset], x, expected) == expected,
+ object[] array when array.GetType() == typeof(object[]) => Interlocked.CompareExchange(ref array[offset / IntPtr.Size], x, expected) == expected,
object[] array => CompareAndSwapObjectArray(array, offset, x, expected) == expected,
_ => CompareAndSwapField(o, offset, expected, x)
};
@@ -1954,30 +1968,91 @@ public static bool compareAndSwapLong(object self, object o, long offset, long e
#endif
}
+ const int PARK_STATE_RUNNING = 0;
+ const int PARK_STATE_PERMIT = 1;
+ const int PARK_STATE_PARKED = 2;
+
+ ///
+ /// Implements the native method 'unpark'.
+ ///
+ ///
+ ///
public static void unpark(object self, object thread)
{
#if FIRST_PASS
throw new NotImplementedException();
#else
- global::java.util.concurrent.locks.LockSupport.unpark((global::java.lang.Thread)thread);
+ var currentThread = (global::java.lang.Thread)thread;
+ if (currentThread == null)
+ throw new global::java.lang.IllegalStateException();
+
+ if (currentThread != null)
+ {
+ if (Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_PERMIT, PARK_STATE_RUNNING) == PARK_STATE_PARKED)
+ {
+ if (Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_RUNNING, PARK_STATE_PARKED) == PARK_STATE_PARKED)
+ {
+ // initialize lock
+ Interlocked.CompareExchange(ref currentThread.parkLock, new global::java.lang.Object(), null);
+
+ // thread is currently blocking, so we have to release it
+ lock (currentThread.parkLock)
+ Monitor.Pulse(currentThread.parkLock);
+ }
+ }
+ }
#endif
}
+ ///
+ /// Implements the native method 'park'.
+ ///
+ ///
+ ///
+ ///
public static void park(object self, bool isAbsolute, long time)
{
#if FIRST_PASS
throw new NotImplementedException();
#else
- if (isAbsolute)
- {
- global::java.util.concurrent.locks.LockSupport.parkUntil(time);
- }
- else
+ var currentThread = global::java.lang.Thread.currentThread();
+ if (currentThread == null)
+ throw new global::java.lang.IllegalStateException();
+
+ if (Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_RUNNING, PARK_STATE_PERMIT) == PARK_STATE_PERMIT)
+ return;
+
+ // initialize lock
+ Interlocked.CompareExchange(ref currentThread.parkLock, new global::java.lang.Object(), null);
+
+ // lock thread
+ lock (currentThread.parkLock)
{
- if (time == 0)
- time = global::java.lang.Long.MAX_VALUE;
+ if (Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_PARKED, PARK_STATE_RUNNING) == PARK_STATE_PERMIT)
+ {
+ Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_RUNNING, PARK_STATE_PERMIT);
+ return;
+ }
+
+ if (isAbsolute)
+ {
+ time *= 1000000;
+ time -= global::java.lang.System.currentTimeMillis() * 1000000;
+ }
+
+ if (time >= 0)
+ {
+ try
+ {
+ ((global::java.lang.Object)currentThread.parkLock).wait(time / 1000000, (int)(time % 1000000));
+ }
+ catch (global::java.lang.InterruptedException _)
+ {
+ currentThread.interrupt();
+ }
+ }
- global::java.util.concurrent.locks.LockSupport.parkNanos(time);
+ Interlocked.CompareExchange(ref currentThread.parkState, PARK_STATE_RUNNING, PARK_STATE_PARKED);
}
#endif
}
@@ -2389,7 +2464,8 @@ static unsafe long CompareExchangeInt64(Array obj, long offset, long value, long
}
finally
{
- h.Free();
+ if (h.IsAllocated)
+ h.Free();
}
}
diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs
index 1f6fc0aad1..3b50a6d60c 100644
--- a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs
+++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs
@@ -7,7 +7,6 @@
using IKVM.Runtime.Accessors.Java.Nio.File;
using IKVM.Runtime.Accessors.Sun.Nio.Fs;
-
namespace IKVM.Java.Externs.sun.nio.fs
{
@@ -78,19 +77,22 @@ bool ApplyFilter(object i)
}
}
- return EnumeratorIteratorAccessor.Init(files.Select(i => DotNetPathAccessor.Init(fileSystem, Path.Combine(directoryPath, i))).Where(ApplyFilter).GetEnumerator());
+ return EnumeratorIteratorAccessor.Init(files.Select(i => DotNetPathAccessor.Init(fileSystem, i)).Where(ApplyFilter).GetEnumerator());
}
catch (global::java.lang.Throwable)
{
throw;
}
+ catch (Exception e) when (File.Exists(directoryPath))
+ {
+ throw new global::java.nio.file.NotDirectoryException(directoryPath);
+ }
+ catch (Exception e) when (Directory.Exists(directoryPath) == false)
+ {
+ throw new global::java.nio.file.NotDirectoryException(directoryPath);
+ }
catch (Exception e)
{
- if (File.Exists(directoryPath))
- throw new global::java.nio.file.NotDirectoryException(directoryPath);
- if (Directory.Exists(directoryPath) == false)
- throw new global::java.nio.file.NotDirectoryException(directoryPath);
-
throw new global::java.io.IOException(e.Message);
}
#endif
diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs
index 19fb4482a4..bc332f1fba 100644
--- a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs
+++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs
@@ -1,12 +1,10 @@
using System;
using System.IO;
-using System.Linq.Expressions;
+using System.Linq;
using System.Reflection;
-using System.Reflection.Emit;
using System.Security;
using System.Security.AccessControl;
-using IKVM.Internal;
using IKVM.Runtime;
using IKVM.Runtime.Accessors.Java.Io;
using IKVM.Runtime.Accessors.Java.Lang;
@@ -231,7 +229,7 @@ public static object newDirectoryStream(object self, object dir, object filter)
if (VfsTable.Default.GetEntry(path) is not VfsDirectory vfsDirectory)
throw new global::java.nio.file.NotDirectoryException(path);
- return DotNetDirectoryStreamAccessor.Init(dir, vfsDirectory.List(), filter);
+ return DotNetDirectoryStreamAccessor.Init(dir, vfsDirectory.List().Select(i => Path.Combine(path, i)), filter);
}
if (File.Exists(path))
diff --git a/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs b/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs
index b355a3246b..a91f57a5f0 100644
--- a/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs
+++ b/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs
@@ -29,6 +29,7 @@ Jeroen Frijters
using IKVM.Attributes;
using IKVM.Internal;
+using IKVM.Runtime;
namespace IKVM.Java.Externs.sun.reflect
{
@@ -97,7 +98,7 @@ internal static bool IsHideFromStackWalk(MethodBase mb)
throw new NotImplementedException();
#else
if (realFramesToSkip <= 0)
- return global::ikvm.@internal.ClassLiteral.Value;
+ return (global::java.lang.Class)ClassLiteral.Value;
for (int i = 2; ;)
{
diff --git a/src/IKVM.Runtime/Launcher.cs b/src/IKVM.Runtime/Launcher.cs
index 6ada07509e..3eed52ddc1 100644
--- a/src/IKVM.Runtime/Launcher.cs
+++ b/src/IKVM.Runtime/Launcher.cs
@@ -6,6 +6,7 @@
using System.Net;
using System.Net.Sockets;
using System.Reflection;
+using System.Runtime.ExceptionServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -13,7 +14,10 @@
using IKVM.Attributes;
using IKVM.Internal;
+using IKVM.Runtime.Accessors.Ikvm.Internal;
using IKVM.Runtime.Accessors.Java.Lang;
+using IKVM.Runtime.Accessors.Java.Lang.Reflect;
+using IKVM.Runtime.Accessors.Sun.Launcher;
namespace IKVM.Runtime
{
@@ -22,14 +26,24 @@ namespace IKVM.Runtime
/// Utility for launching a Java class from a main entry point. Parses JVM command line options, then passes the
/// remainder through to the underlying Java main method.
///
- static class Launcher
+ public static class Launcher
{
#if FIRST_PASS == false && IMPORTER == false && EXPORTER == false
+ static CallerIDAccessor callerIDAccessor;
+ static ClassAccessor classAccessor;
+ static MethodAccessor methodAccessor;
static SystemAccessor systemAccessor;
static ThreadAccessor threadAccessor;
static ThreadGroupAccessor threadGroupAccessor;
+ static LauncherHelperAccessor launcherHelperAccessor;
+
+ static CallerIDAccessor CallerIDAccessor => JVM.BaseAccessors.Get(ref callerIDAccessor);
+
+ static ClassAccessor ClassAccessor => JVM.BaseAccessors.Get(ref classAccessor);
+
+ static MethodAccessor MethodAccessor => JVM.BaseAccessors.Get(ref methodAccessor);
static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor);
@@ -37,6 +51,8 @@ static class Launcher
static ThreadGroupAccessor ThreadGroupAccessor => JVM.BaseAccessors.Get(ref threadGroupAccessor);
+ static LauncherHelperAccessor LauncherHelperAccessor => JVM.BaseAccessors.Get(ref launcherHelperAccessor);
+
#endif
///
@@ -115,7 +131,7 @@ static string[] Glob(string[] paths)
/// Sets the startup properties.
///
///
- static void SetProperties(IDictionary properties)
+ static void SetUserProperties(IDictionary properties)
{
#if FIRST_PASS || IMPORTER
throw new NotImplementedException();
@@ -140,9 +156,6 @@ static void EnterMainThread()
{
try
{
- if (false)
- throw new InvalidOperationException();
-
Thread.CurrentThread.Name = "main";
}
catch (InvalidOperationException)
@@ -151,7 +164,7 @@ static void EnterMainThread()
}
}
- JVM.EnsureInitialized();
+ // first invocation of a type in the base assembly
ThreadAccessor.InvokeCurrentThread();
try
@@ -161,7 +174,7 @@ static void EnterMainThread()
}
catch (java.lang.IllegalArgumentException)
{
- // ignore it;
+ // ignore
}
#endif
}
@@ -485,7 +498,12 @@ public static int Run(string main, bool jar, string[] args, string rarg, IDictio
initialize["sun.java.launcher"] = "SUN_STANDARD";
// apply the loaded VM properties
- SetProperties(initialize);
+ SetUserProperties(initialize);
+
+ // VM initialization, configures system properties, done before any static initializers
+ JVM.EnsureInitialized();
+
+ // first entry into base assembly
EnterMainThread();
// we were instructed to show the version
@@ -500,22 +518,24 @@ public static int Run(string main, bool jar, string[] args, string rarg, IDictio
}
// process the main argument, returning the true value, and resetting the command property to match
- var clazz = sun.launcher.LauncherHelper.checkAndLoadMain(true, jar ? 2 : 1, main);
+ var clazz = LauncherHelperAccessor.InvokeCheckAndLoadMain(true, jar ? 2 : 1, main);
SystemAccessor.InvokeSetProperty("sun.java.command", initialize["sun.java.command"]);
// find the main method and ensure it is accessible
- var method = clazz.getMethod("main", typeof(string[]));
- method.setAccessible(true);
+ var method = ClassAccessor.InvokeGetMethod(clazz, "main", ClassAccessor.InitArray(ClassLoaderWrapper.GetWrapperFromType(typeof(string[])).ClassObject), CallerIDAccessor.InvokeCreate(typeof(Launcher).TypeHandle));
+ MethodAccessor.InvokeSetAccessible(method, true);
try
{
// invoke main method, which is responsible for exit
- method.invoke(null, new object[] { appArgs.ToArray() });
+ MethodAccessor.InvokeInvoke(method, null, new[] { appArgs.ToArray() });
return 0;
}
catch (java.lang.reflect.InvocationTargetException e)
{
- throw e.getCause();
+ // we want to unwrap the cause to report to the user
+ ExceptionDispatchInfo.Capture(e.getCause()).Throw();
+ throw null;
}
}
catch (Exception e)
@@ -593,7 +613,7 @@ static void HandleDebugTrace()
///
/// Prints out the standard launcher help information.
///
- public static void PrintHelp()
+ static void PrintHelp()
{
var exe = Process.GetCurrentProcess().ProcessName;
Console.Error.WriteLine("Usage: {0} [-options] class [args...]", exe);
@@ -638,7 +658,7 @@ public static void PrintHelp()
///
/// Prints out the extended launcher help information.
///
- public static void PrintXHelp()
+ static void PrintXHelp()
{
Console.Error.WriteLine(" -Xnoclassgc disable class garbage collection");
Console.Error.WriteLine(" -Xtime time the execution");
diff --git a/src/IKVM.Runtime/RuntimeHelperTypes.cs b/src/IKVM.Runtime/RuntimeHelperTypes.cs
index c9cc00d97b..e69de29bb2 100644
--- a/src/IKVM.Runtime/RuntimeHelperTypes.cs
+++ b/src/IKVM.Runtime/RuntimeHelperTypes.cs
@@ -1,105 +0,0 @@
-/*
- Copyright (C) 2009 Jeroen Frijters
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- Jeroen Frijters
- jeroen@frijters.net
-
-*/
-using System;
-
-using IKVM.Runtime;
-
-#if IMPORTER
-using IKVM.Reflection;
-using IKVM.Reflection.Emit;
-using IKVM.Tools.Importer;
-
-using Type = IKVM.Reflection.Type;
-
-#else
-using System.Reflection;
-using System.Reflection.Emit;
-#endif
-
-using System.Diagnostics;
-
-namespace IKVM.Internal
-{
-
- static class RuntimeHelperTypes
- {
-
- private static Type classLiteralType;
- private static FieldInfo classLiteralField;
-
- internal static FieldInfo GetClassLiteralField(Type type)
- {
- Debug.Assert(type != Types.Void);
- if (classLiteralType == null)
- {
-#if IMPORTER
- classLiteralType = JVM.BaseAssembly.GetType("ikvm.internal.ClassLiteral`1");
-#elif !FIRST_PASS
- classLiteralType = typeof(ikvm.@internal.ClassLiteral<>);
-#endif
- }
-#if !IMPORTER
- if (!IsTypeBuilder(type))
- {
- return classLiteralType.MakeGenericType(type).GetField("Value", BindingFlags.Public | BindingFlags.Static);
- }
-#endif
- if (classLiteralField == null)
- {
- classLiteralField = classLiteralType.GetField("Value", BindingFlags.Public | BindingFlags.Static);
- }
- return TypeBuilder.GetField(classLiteralType.MakeGenericType(type), classLiteralField);
- }
-
- private static bool IsTypeBuilder(Type type)
- {
- return type is TypeBuilder || (type.HasElementType && IsTypeBuilder(type.GetElementType()));
- }
-
-#if IMPORTER
- internal static void Create(CompilerClassLoader ccl)
- {
- EmitClassLiteral(ccl);
- }
-
- private static void EmitClassLiteral(CompilerClassLoader ccl)
- {
- TypeBuilder tb = ccl.GetTypeWrapperFactory().ModuleBuilder.DefineType("ikvm.internal.ClassLiteral`1", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.Class | TypeAttributes.BeforeFieldInit);
- GenericTypeParameterBuilder typeParam = tb.DefineGenericParameters("T")[0];
- Type classType = CoreClasses.java.lang.Class.Wrapper.TypeAsSignatureType;
- classLiteralField = tb.DefineField("Value", classType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
- CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb, ccl));
- ilgen.Emit(OpCodes.Ldtoken, typeParam);
- ilgen.Emit(OpCodes.Call, Types.Type.GetMethod("GetTypeFromHandle", new Type[] { Types.RuntimeTypeHandle }));
- MethodWrapper mw = CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("", "(Lcli.System.Type;)V", false);
- mw.Link();
- mw.EmitNewobj(ilgen);
- ilgen.Emit(OpCodes.Stsfld, classLiteralField);
- ilgen.Emit(OpCodes.Ret);
- ilgen.DoEmit();
- classLiteralType = tb.CreateType();
- }
-#endif
- }
-}
diff --git a/src/IKVM.Runtime/TypeWrapper.cs b/src/IKVM.Runtime/TypeWrapper.cs
index 08225a5ccd..8b5c8343c0 100644
--- a/src/IKVM.Runtime/TypeWrapper.cs
+++ b/src/IKVM.Runtime/TypeWrapper.cs
@@ -113,7 +113,13 @@ internal void EmitClassLiteral(CodeEmitter ilgen)
}
else
{
- ilgen.Emit(OpCodes.Ldsfld, RuntimeHelperTypes.GetClassLiteralField(type));
+#if IMPORTER
+ var classLiteralType = StaticCompiler.GetRuntimeType("IKVM.Runtime.ClassLiteral`1").MakeGenericType(type);
+#else
+ var classLiteralType = typeof(ClassLiteral<>).MakeGenericType(type);
+#endif
+
+ ilgen.Emit(OpCodes.Call, classLiteralType.GetProperty("Value").GetMethod);
}
}
@@ -168,6 +174,7 @@ internal java.lang.Class ClassObject
get
{
Debug.Assert(!IsUnloadable && !IsVerifierType);
+
if (classObject == null)
LazyInitClass();
@@ -253,7 +260,7 @@ private void LazyInitClass()
}
else
{
- clazz = (java.lang.Class)typeof(ikvm.@internal.ClassLiteral<>).MakeGenericType(type).GetField("Value").GetValue(null);
+ clazz = (java.lang.Class)typeof(ClassLiteral<>).MakeGenericType(type).GetProperty("Value").GetGetMethod().Invoke(null, Array.Empty());
}
}
#if __MonoCS__
diff --git a/src/IKVM.Runtime/intrinsics.cs b/src/IKVM.Runtime/intrinsics.cs
index 356da57f22..f012292cd8 100644
--- a/src/IKVM.Runtime/intrinsics.cs
+++ b/src/IKVM.Runtime/intrinsics.cs
@@ -218,6 +218,31 @@ static Dictionary Register()
return intrinsics;
}
+ ///
+ /// Emits IL that pushes the index scale for the specified array type onto the stack.
+ ///
+ ///
+ ///
+ ///
+ static void EmitArrayIndexScale(EmitIntrinsicContext eic, TypeWrapper tw)
+ {
+ var et = tw.ElementTypeWrapper;
+ if (et == PrimitiveTypeWrapper.BYTE || et == PrimitiveTypeWrapper.BOOLEAN)
+ eic.Emitter.EmitLdc_I4(1);
+ else if (et == PrimitiveTypeWrapper.CHAR || et == PrimitiveTypeWrapper.SHORT)
+ eic.Emitter.EmitLdc_I4(2);
+ else if (et == PrimitiveTypeWrapper.INT || et == PrimitiveTypeWrapper.FLOAT)
+ eic.Emitter.EmitLdc_I4(4);
+ else if (et == PrimitiveTypeWrapper.LONG || et == PrimitiveTypeWrapper.DOUBLE)
+ eic.Emitter.EmitLdc_I4(8);
+ else if (et.IsPrimitive == false && et.IsNonPrimitiveValueType)
+ eic.Emitter.Emit(OpCodes.Sizeof, et.TypeAsArrayType);
+ else if (et.IsPrimitive == false && et.IsNonPrimitiveValueType == false)
+ eic.Emitter.Emit(OpCodes.Sizeof, Types.IntPtr);
+ else
+ eic.Emitter.EmitLdc_I4(1);
+ }
+
internal static bool IsIntrinsic(MethodWrapper mw)
{
return intrinsics.ContainsKey(new IntrinsicKey(mw)) && mw.DeclaringType.GetClassLoader() == CoreClasses.java.lang.Object.Wrapper.GetClassLoader();
@@ -543,21 +568,42 @@ internal static bool IsSupportedArrayTypeForUnsafeOperation(TypeWrapper tw)
return tw.IsArray && !tw.IsGhostArray && !tw.ElementTypeWrapper.IsPrimitive && !tw.ElementTypeWrapper.IsNonPrimitiveValueType;
}
+ ///
+ /// Attempts to replace an invocation of Unsafe.putObject with an intrinsic implementation.
+ ///
+ ///
+ ///
static bool Unsafe_putObject(EmitIntrinsicContext eic)
{
return Unsafe_putObjectImpl(eic, false);
}
+ ///
+ /// Attempts to replace an invocation of Unsafe.putOrderedObject with an intrinsic implementation.
+ ///
+ ///
+ ///
static bool Unsafe_putOrderedObject(EmitIntrinsicContext eic)
{
return Unsafe_putObjectImpl(eic, false);
}
+ ///
+ /// Attempts to replace an invocation of Unsafe.putObjectVolatile with an intrinsic implementation.
+ ///
+ ///
+ ///
static bool Unsafe_putObjectVolatile(EmitIntrinsicContext eic)
{
return Unsafe_putObjectImpl(eic, true);
}
+ ///
+ /// Attempts to replace an invocation of an Unsafe put operation with an intrinsic implementation.
+ ///
+ ///
+ ///
+ ///
static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool isVolatile)
{
var tw = eic.GetStackTypeWrapper(0, 2);
@@ -576,8 +622,11 @@ static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool isVolatile)
eic.Emitter.Emit(OpCodes.Stloc, array);
EmitConsumeUnsafe(eic);
+ // emit new call that sets the element by index
eic.Emitter.Emit(OpCodes.Ldloc, array);
eic.Emitter.Emit(OpCodes.Ldloc, index);
+ EmitArrayIndexScale(eic, tw);
+ eic.Emitter.Emit(OpCodes.Div);
eic.Emitter.Emit(OpCodes.Ldloc, value);
eic.Emitter.Emit(OpCodes.Stelem_Ref);
@@ -659,8 +708,11 @@ static bool Unsafe_getObjectVolatile(EmitIntrinsicContext eic)
eic.Emitter.Emit(OpCodes.Stloc, target);
EmitConsumeUnsafe(eic);
+ // emit new call that gets the element by index
eic.Emitter.Emit(OpCodes.Ldloc, target);
eic.Emitter.Emit(OpCodes.Ldloc, offset);
+ EmitArrayIndexScale(eic, tw);
+ eic.Emitter.Emit(OpCodes.Div);
eic.Emitter.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType.GetElementType());
eic.Emitter.Emit(OpCodes.Volatile);
eic.Emitter.Emit(OpCodes.Ldind_Ref);
@@ -711,6 +763,8 @@ static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic)
// emit new call site
eic.Emitter.Emit(OpCodes.Ldloc, target);
eic.Emitter.Emit(OpCodes.Ldloc, offset);
+ EmitArrayIndexScale(eic, tw);
+ eic.Emitter.Emit(OpCodes.Div);
eic.Emitter.Emit(OpCodes.Ldelema, type);
eic.Emitter.Emit(OpCodes.Ldloc, update);
eic.Emitter.Emit(OpCodes.Ldloc, expect);
@@ -769,6 +823,7 @@ static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic)
eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type));
eic.Emitter.Emit(OpCodes.Ldloc, expect);
eic.Emitter.Emit(OpCodes.Ceq);
+
eic.Emitter.ReleaseTempLocal(expect);
eic.Emitter.ReleaseTempLocal(update);
eic.NonLeaf = false;
@@ -820,6 +875,7 @@ static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic)
eic.Emitter.Emit(OpCodes.Ldloc, update);
fw.EmitUnsafeCompareAndSwap(eic.Emitter);
+ eic.Emitter.ReleaseTempLocal(target);
eic.Emitter.ReleaseTempLocal(expect);
eic.Emitter.ReleaseTempLocal(update);
eic.NonLeaf = false;
@@ -857,6 +913,8 @@ static bool Unsafe_getAndSetObject(EmitIntrinsicContext eic)
// emit new call
eic.Emitter.Emit(OpCodes.Ldloc, target);
eic.Emitter.Emit(OpCodes.Ldloc, offset);
+ EmitArrayIndexScale(eic, tw);
+ eic.Emitter.Emit(OpCodes.Div);
eic.Emitter.Emit(OpCodes.Ldelema, type);
eic.Emitter.Emit(OpCodes.Ldloc, newValue);
eic.Emitter.Emit(OpCodes.Call, MakeExchange(type));
@@ -920,6 +978,7 @@ static bool Unsafe_compareAndSwapInt(EmitIntrinsicContext eic)
eic.Emitter.Emit(OpCodes.Ldloc, update);
fw.EmitUnsafeCompareAndSwap(eic.Emitter);
+ eic.Emitter.ReleaseTempLocal(target);
eic.Emitter.ReleaseTempLocal(expect);
eic.Emitter.ReleaseTempLocal(update);
eic.NonLeaf = false;
diff --git a/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java b/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java
deleted file mode 100644
index 0722252e0a..0000000000
--- a/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package ikvm.tests.java.java.lang;
-
-import java.net.*;
-
-@cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.Annotation()
-public class ClassLoaderTests {
-
- static class Foo extends ClassLoader {
- @Override
- protected void finalize() {
- System.out.println("");
- }
- }
-
- static class Bar extends Foo {
- @Override
- protected void finalize() {
- super.finalize();
- System.out.println("");
- }
- }
-
- static class ShouldPreventUninitializedParentClass {
- static ClassLoader loader;
-
- public static void run() throws Exception {
- try {
- new ClassLoader(null) {
- @Override
- protected void finalize() {
- loader = this;
- }
- };
- } catch (SecurityException exc) {
-
- }
-
- System.gc();
- System.runFinalization();
-
- if (loader != null) {
- try {
- URLClassLoader child = URLClassLoader.newInstance(new URL[0], loader);
- throw new RuntimeException("child class loader created");
- } catch (SecurityException se) {
- // test passed
- }
- } else {
- // test passed
- }
- }
- }
-
- @cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Annotation()
- public void shouldPreventUninitializedParent() throws Exception {
- ShouldPreventUninitializedParentClass.run();
- }
-
-}
diff --git a/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java b/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java
deleted file mode 100644
index 460fe18d80..0000000000
--- a/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java
+++ /dev/null
@@ -1,162 +0,0 @@
-package ikvm.tests.java.java.nio.channels;
-
-import java.net.*;
-import java.io.*;
-import java.nio.channels.*;
-import java.util.Random;
-
-@cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.Annotation()
-public class AsynchronousServerSocketChannelTests {
-
- static final Random rand = new Random();
-
- @cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Annotation()
- public void canReadAndWriteStreams() throws Exception {
- // establish loopback connection
- AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0));
- int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort();
- InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), port);
- AsynchronousSocketChannel ch1 = AsynchronousSocketChannel.open();
- ch1.connect(isa).get();
- AsynchronousSocketChannel ch2 = listener.accept().get();
-
- // start thread to write to stream
- Writer writer = new Writer(Channels.newOutputStream(ch1));
- Thread writerThread = new Thread(writer);
- writerThread.start();
-
- // start thread to read from stream
- Reader reader = new Reader(Channels.newInputStream(ch2));
- Thread readerThread = new Thread(reader);
- readerThread.start();
-
- // wait for threads to complete
- writerThread.join();
- readerThread.join();
-
- // shutdown listener
- listener.close();
-
- // check that reader received what we expected
- if (reader.total() != writer.total())
- throw new RuntimeException("Unexpected number of bytes read");
- if (reader.hash() != writer.hash())
- throw new RuntimeException("Hash incorrect for bytes read");
-
- // channels should be closed
- if (ch1.isOpen() || ch2.isOpen())
- throw new RuntimeException("Channels should be closed");
- }
-
- static class Reader implements Runnable {
-
- private final InputStream in;
- private volatile int total;
- private volatile int hash;
-
- Reader(InputStream in) {
- this.in = in;
- }
-
- public void run() {
- try {
- int n;
- do {
- // random offset/len
- byte[] buf = new byte[128 + rand.nextInt(128)];
- int len, off;
- if (rand.nextBoolean()) {
- len = buf.length;
- off = 0;
- n = in.read(buf);
- } else {
- len = 1 + rand.nextInt(64);
- off = rand.nextInt(64);
- n = in.read(buf, off, len);
- }
- if (n > len)
- throw new RuntimeException("Too many bytes read");
- if (n > 0) {
- total += n;
- for (int i=0; i 0);
- in.close();
- } catch (IOException x) {
- x.printStackTrace();
- }
- }
-
- int total() {
- return total;
- }
-
- int hash() {
- return hash;
- }
-
- }
-
- static class Writer implements Runnable {
-
- private final OutputStream out;
- private final int total;
- private volatile int hash;
-
- Writer(OutputStream out) {
- this.out = out;
- this.total = 50*1000 + rand.nextInt(50*1000);
- }
-
- public void run() {
- hash = 0;
- int rem = total;
- try {
- do {
- byte[] buf = new byte[1 + rand.nextInt(rem)];
- int off, len;
-
- // write random bytes
- if (rand.nextBoolean()) {
- off = 0;
- len = buf.length;
- } else {
- off = rand.nextInt(buf.length);
- int r = buf.length - off;
- len = (r <= 1) ? 1 : (1 + rand.nextInt(r));
- }
- for (int i=0; i 0);
-
- // close stream when done
- out.close();
-
- } catch (IOException x) {
- x.printStackTrace();
- }
- }
-
- int total() {
- return total;
- }
-
- int hash() {
- return hash;
- }
-
- }
-
-}
diff --git a/src/IKVM.Tests/IKVM.Tests.csproj b/src/IKVM.Tests/IKVM.Tests.csproj
index 9765a62d2f..251d8365a9 100644
--- a/src/IKVM.Tests/IKVM.Tests.csproj
+++ b/src/IKVM.Tests/IKVM.Tests.csproj
@@ -21,7 +21,7 @@
-
+
PreserveNewest
diff --git a/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs b/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs
index a5093a7f16..4b2856bcda 100644
--- a/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs
+++ b/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs
@@ -52,7 +52,7 @@ public async Task Setup()
{
Runtime = Path.Combine("lib", tfm, "IKVM.Runtime.dll"),
ResponseFile = $"{n}_ikvmc.rsp",
- Input = { Path.Combine("ext", "helloworld-2.0.jar") },
+ Input = { Path.Combine("helloworld", "helloworld-2.0.jar") },
Assembly = $"helloworld_{n}",
Version = "1.0.0.0",
NoStdLib = true,
diff --git a/src/IKVM.Tests/Java/java/io/FileTests.cs b/src/IKVM.Tests/Java/java/io/FileTests.cs
index c5fc5f3dbc..cbd4c6fb25 100644
--- a/src/IKVM.Tests/Java/java/io/FileTests.cs
+++ b/src/IKVM.Tests/Java/java/io/FileTests.cs
@@ -12,6 +12,12 @@ namespace IKVM.Tests.Java.java.io
public class FileTests
{
+ [TestMethod]
+ public void CanConvertRelativePathToRealPath()
+ {
+ (new global::java.io.File(".")).toPath().toRealPath();
+ }
+
[TestMethod]
public void CanCreateFile()
{
diff --git a/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs b/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs
index 0b3b43329d..a5a739d907 100644
--- a/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs
+++ b/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs
@@ -1,4 +1,6 @@
-using java.nio.charset;
+using FluentAssertions;
+
+using java.nio.charset;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -9,12 +11,38 @@ namespace IKVM.Tests.Java.java.nio.charset
public class CharsetTests
{
+ [TestMethod]
+ public void CanGetDefaultCharsets()
+ {
+ Charset.defaultCharset();
+ }
+
[TestMethod]
public void CanGetAvailableCharsets()
{
Charset.availableCharsets();
}
+ [DataTestMethod]
+ [DataRow("cp437")]
+ [DataRow("cp850")]
+ [DataRow("cp852")]
+ [DataRow("cp855")]
+ [DataRow("cp857")]
+ [DataRow("cp860")]
+ [DataRow("cp861")]
+ [DataRow("cp863")]
+ [DataRow("cp865")]
+ [DataRow("cp866")]
+ [DataRow("cp869")]
+ [DataRow("cp936")]
+ public void CanEncodeAndDecode(string charset)
+ {
+ var o = Charset.forName(charset).encode("test");
+ var i = Charset.forName(charset).decode(o);
+ i.toString().Should().Be("test");
+ }
+
}
}
diff --git a/src/IKVM.Tests/Java/java/nio/file/DirectoryStreamTests.cs b/src/IKVM.Tests/Java/java/nio/file/DirectoryStreamTests.cs
new file mode 100644
index 0000000000..4cd2120ced
--- /dev/null
+++ b/src/IKVM.Tests/Java/java/nio/file/DirectoryStreamTests.cs
@@ -0,0 +1,63 @@
+using System;
+
+using FluentAssertions;
+
+using java.io;
+using java.nio.file;
+using java.util;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace IKVM.Tests.Java.java.nio.file
+{
+
+ [TestClass]
+ public class DirectoryStreamTests
+ {
+
+ [TestMethod]
+ public void ShouldListFilesAtAbsolutePath()
+ {
+ var d = Files.createTempDirectory("test");
+ d.toFile().deleteOnExit();
+
+ var f1 = new File(d.toFile(), "test1.txt");
+ f1.createNewFile();
+ var f2 = new File(d.toFile(), "test2.txt");
+ f2.createNewFile();
+ var f3 = new File(d.toFile(), "test3.txt");
+ f3.createNewFile();
+
+ using var stream = Files.newDirectoryStream(d);
+ var l = stream.iterator().RemainingToList();
+ l.Should().HaveCount(3);
+ l.Should().Contain(i => i.ToString() == f1.ToString());
+ l.Should().Contain(i => i.ToString() == f2.ToString());
+ l.Should().Contain(i => i.ToString() == f3.ToString());
+ }
+
+ [TestMethod]
+ public void ShouldListFilesAtRelativePath()
+ {
+ var n = Guid.NewGuid().ToString("n");
+ var d = Files.createDirectory(Paths.get(n));
+ d.toFile().deleteOnExit();
+
+ var f1 = new File(d.toFile(), "test1.txt");
+ f1.createNewFile();
+ var f2 = new File(d.toFile(), "test2.txt");
+ f2.createNewFile();
+ var f3 = new File(d.toFile(), "test3.txt");
+ f3.createNewFile();
+
+ using var stream = Files.newDirectoryStream(d);
+ var l = stream.iterator().RemainingToList();
+ l.Should().HaveCount(3);
+ l.Should().Contain(i => i.ToString() == f1.ToString());
+ l.Should().Contain(i => i.ToString() == f2.ToString());
+ l.Should().Contain(i => i.ToString() == f3.ToString());
+ }
+
+ }
+
+}
diff --git a/src/IKVM.Tests/Java/java/util/concurrent/ForkJoinPoolTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/ForkJoinPoolTests.cs
new file mode 100644
index 0000000000..b7c70d76f4
--- /dev/null
+++ b/src/IKVM.Tests/Java/java/util/concurrent/ForkJoinPoolTests.cs
@@ -0,0 +1,14 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace IKVM.Tests.Java.java.util.concurrent
+{
+
+ [TestClass]
+ public class ForkJoinPoolTests
+ {
+
+
+
+ }
+
+}
diff --git a/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicReferenceArrayTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicReferenceArrayTests.cs
new file mode 100644
index 0000000000..2d42575176
--- /dev/null
+++ b/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicReferenceArrayTests.cs
@@ -0,0 +1,44 @@
+using FluentAssertions;
+
+using java.util.concurrent.atomic;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace IKVM.Tests.Java.java.util.concurrent.atomic
+{
+
+ [TestClass]
+ public class AtomicReferenceArrayTests
+ {
+
+ [TestMethod]
+ public void CanLazySetArray()
+ {
+ var ao = new AtomicReferenceArray(32);
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.lazySet(idx, new object());
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.get(idx).Should().NotBeNull();
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.lazySet(idx, null);
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.get(idx).Should().BeNull();
+ }
+
+ [TestMethod]
+ public void CanSetArray()
+ {
+ var ao = new AtomicReferenceArray(32);
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.set(idx, new object());
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.get(idx).Should().NotBeNull();
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.set(idx, null);
+ for (int idx = 0; idx < ao.length(); ++idx)
+ ao.get(idx).Should().BeNull();
+ }
+
+ }
+
+}
diff --git a/src/IKVM.Tests/Java/java/util/zip/ZipTests.cs b/src/IKVM.Tests/Java/java/util/zip/ZipTests.cs
index 04d913e1a2..e3c98a4776 100644
--- a/src/IKVM.Tests/Java/java/util/zip/ZipTests.cs
+++ b/src/IKVM.Tests/Java/java/util/zip/ZipTests.cs
@@ -12,7 +12,6 @@ public class ZipTests
[TestMethod]
public void TotalInOut()
{
-
const int BUF_SIZE = 1 * 1024 * 1024;
long dataSize = 128L * 1024L * 1024L; // 128MB
diff --git a/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs b/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs
index 76a0fea3cf..07c52c9004 100644
--- a/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs
+++ b/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs
@@ -947,17 +947,18 @@ public void CanSetObjectInArray()
var o1 = new object();
var o2 = new object();
var a = new object[16];
- u.putObject(a, 0L, o1);
- u.getObject(a, 0L).Should().BeSameAs(o1);
+ var s = u.arrayIndexScale(typeof(object[]));
+ u.putObject(a, s * 0L, o1);
+ u.getObject(a, s * 0L).Should().BeSameAs(o1);
a[0].Should().BeSameAs(o1);
- u.putObject(a, 0L, null);
- u.getObject(a, 0L).Should().BeSameAs(null);
+ u.putObject(a, s * 0L, null);
+ u.getObject(a, s * 0L).Should().BeSameAs(null);
a[0].Should().BeNull();
- u.putObject(a, 1L, o2);
- u.getObject(a, 1L).Should().BeSameAs(o2);
+ u.putObject(a, s * 1L, o2);
+ u.getObject(a, s * 1L).Should().BeSameAs(o2);
a[1].Should().BeSameAs(o2);
- u.putObject(a, 1L, null);
- u.getObject(a, 1L).Should().BeSameAs(null);
+ u.putObject(a, s * 1L, null);
+ u.getObject(a, s * 1L).Should().BeSameAs(null);
a[1].Should().BeNull();
}
@@ -967,17 +968,18 @@ public void CanSetObjectInArrayVolatile()
var o1 = new object();
var o2 = new object();
var a = new object[16];
- u.putObjectVolatile(a, 0L, o1);
- u.getObjectVolatile(a, 0L).Should().BeSameAs(o1);
+ var s = u.arrayIndexScale(typeof(object[]));
+ u.putObjectVolatile(a, s * 0L, o1);
+ u.getObjectVolatile(a, s * 0L).Should().BeSameAs(o1);
a[0].Should().BeSameAs(o1);
- u.putObjectVolatile(a, 0L, null);
- u.getObjectVolatile(a, 0L).Should().BeSameAs(null);
+ u.putObjectVolatile(a, s * 0L, null);
+ u.getObjectVolatile(a, s * 0L).Should().BeSameAs(null);
a[0].Should().BeNull();
- u.putObjectVolatile(a, 1L, o2);
- u.getObjectVolatile(a, 1L).Should().BeSameAs(o2);
+ u.putObjectVolatile(a, s * 1L, o2);
+ u.getObjectVolatile(a, s * 1L).Should().BeSameAs(o2);
a[1].Should().BeSameAs(o2);
- u.putObjectVolatile(a, 1L, null);
- u.getObjectVolatile(a, 1L).Should().BeSameAs(null);
+ u.putObjectVolatile(a, s * 1L, null);
+ u.getObjectVolatile(a, s * 1L).Should().BeSameAs(null);
a[1].Should().BeNull();
}
@@ -994,17 +996,18 @@ public void CanSetObjectInTypedArray()
var o1 = new AnonymousTestObject();
var o2 = new AnonymousTestObject();
var a = new AnonymousTestObject[16];
- u.putObject(a, 0L, o1);
- u.getObject(a, 0L).Should().BeSameAs(o1);
+ var s = u.arrayIndexScale(typeof(AnonymousTestObject[]));
+ u.putObject(a, s * 0L, o1);
+ u.getObject(a, s * 0L).Should().BeSameAs(o1);
a[0].Should().BeSameAs(o1);
- u.putObject(a, 0L, null);
- u.getObject(a, 0L).Should().BeSameAs(null);
+ u.putObject(a, s * 0L, null);
+ u.getObject(a, s * 0L).Should().BeSameAs(null);
a[0].Should().BeNull();
- u.putObject(a, 1L, o2);
- u.getObject(a, 1L).Should().BeSameAs(o2);
+ u.putObject(a, s * 1L, o2);
+ u.getObject(a, s * 1L).Should().BeSameAs(o2);
a[1].Should().BeSameAs(o2);
- u.putObject(a, 1L, null);
- u.getObject(a, 1L).Should().BeSameAs(null);
+ u.putObject(a, s * 1L, null);
+ u.getObject(a, s * 1L).Should().BeSameAs(null);
a[1].Should().BeNull();
}
@@ -1014,17 +1017,18 @@ public void CanSetObjectInTypedArrayVolatile()
var o1 = new AnonymousTestObject();
var o2 = new AnonymousTestObject();
var a = new AnonymousTestObject[16];
- u.putObjectVolatile(a, 0L, o1);
- u.getObjectVolatile(a, 0L).Should().BeSameAs(o1);
+ var s = u.arrayIndexScale(typeof(AnonymousTestObject[]));
+ u.putObjectVolatile(a, s * 0L, o1);
+ u.getObjectVolatile(a, s * 0L).Should().BeSameAs(o1);
a[0].Should().BeSameAs(o1);
- u.putObjectVolatile(a, 0L, null);
- u.getObjectVolatile(a, 0L).Should().BeSameAs(null);
+ u.putObjectVolatile(a, s * 0L, null);
+ u.getObjectVolatile(a, s * 0L).Should().BeSameAs(null);
a[0].Should().BeNull();
- u.putObjectVolatile(a, 1L, o2);
- u.getObjectVolatile(a, 1L).Should().BeSameAs(o2);
+ u.putObjectVolatile(a, s * 1L, o2);
+ u.getObjectVolatile(a, s * 1L).Should().BeSameAs(o2);
a[1].Should().BeSameAs(o2);
- u.putObjectVolatile(a, 1L, null);
- u.getObjectVolatile(a, 1L).Should().BeSameAs(null);
+ u.putObjectVolatile(a, s * 1L, null);
+ u.getObjectVolatile(a, s * 1L).Should().BeSameAs(null);
a[1].Should().BeNull();
}
@@ -1391,14 +1395,16 @@ public void CanCompareAndSwapObjectInArray()
var a = new object[4];
for (int i = 0; i < 4; i++)
{
+ var of = i * u.arrayIndexScale(typeof(object[]));
+
var o1 = new object();
var o2 = new object();
- u.compareAndSwapObject(a, i, null, o1).Should().BeTrue();
+ u.compareAndSwapObject(a, of, null, o1).Should().BeTrue();
a[i].Should().BeSameAs(o1);
- u.compareAndSwapObject(a, i, o1, o2).Should().BeTrue();
+ u.compareAndSwapObject(a, of, o1, o2).Should().BeTrue();
a[i].Should().BeSameAs(o2);
- u.compareAndSwapObject(a, i, o1, o2).Should().BeFalse();
+ u.compareAndSwapObject(a, of, o1, o2).Should().BeFalse();
a[i].Should().BeSameAs(o2);
}
}
@@ -1409,14 +1415,16 @@ public void CanCompareAndSwapStringInArray()
var a = new string[4];
for (int i = 0; i < 4; i++)
{
+ var of = i * u.arrayIndexScale(typeof(string[]));
+
var o1 = "TEST1";
var o2 = "TEST2";
- u.compareAndSwapObject(a, i, null, o1).Should().BeTrue();
+ u.compareAndSwapObject(a, of, null, o1).Should().BeTrue();
a[i].Should().BeSameAs(o1);
- u.compareAndSwapObject(a, i, o1, o2).Should().BeTrue();
+ u.compareAndSwapObject(a, of, o1, o2).Should().BeTrue();
a[i].Should().BeSameAs(o2);
- u.compareAndSwapObject(a, i, o1, o2).Should().BeFalse();
+ u.compareAndSwapObject(a, of, o1, o2).Should().BeFalse();
a[i].Should().BeSameAs(o2);
}
}
diff --git a/src/IKVM.Tests/Runtime/Accessors/Java/Lang/ClassAccessorTests.cs b/src/IKVM.Tests/Runtime/Accessors/Java/Lang/ClassAccessorTests.cs
new file mode 100644
index 0000000000..adb36f7d27
--- /dev/null
+++ b/src/IKVM.Tests/Runtime/Accessors/Java/Lang/ClassAccessorTests.cs
@@ -0,0 +1,33 @@
+using FluentAssertions;
+
+using IKVM.Runtime.Accessors;
+using IKVM.Runtime.Accessors.Ikvm.Internal;
+using IKVM.Runtime.Accessors.Java.Lang;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace IKVM.Tests.Runtime.Accessors.Java.Lang
+{
+
+ [TestClass]
+ public class ClassAccessorTests
+ {
+
+ [TestMethod]
+ public void CanInvokeGetMethod()
+ {
+ AccessorRef callerIDAccessorRef = new();
+ AccessorRef classAccessorRef = new();
+
+ var callerID = callerIDAccessorRef.Value.InvokeCreate(typeof(ClassAccessorTests).TypeHandle);
+
+ var c = classAccessorRef.Value.InvokeForName("java.lang.Object", callerID);
+ c.Should().NotBeNull();
+
+ var m = classAccessorRef.Value.InvokeGetMethod(c, "toString", classAccessorRef.Value.InitArray(0), callerID);
+ m.Should().NotBeNull();
+ }
+
+ }
+
+}
diff --git a/src/IKVM.Tests/Util/Jar/JarFileTests.cs b/src/IKVM.Tests/Util/Jar/JarFileTests.cs
index e987022446..da664281f2 100644
--- a/src/IKVM.Tests/Util/Jar/JarFileTests.cs
+++ b/src/IKVM.Tests/Util/Jar/JarFileTests.cs
@@ -16,14 +16,14 @@ public class JarFileTests
[TestMethod]
public void CanReadManifestVersion()
{
- var z = new JarFile(Path.Combine("ext","helloworld-2.0.jar"));
+ var z = new JarFile(Path.Combine("helloworld", "helloworld-2.0.jar"));
z.Manifest.MainAttributes.Should().Contain("Manifest-Version", "1.0");
}
[TestMethod]
public void CanReadModuleName()
{
- var z = new JarFile(Path.Combine("ext", "helloworld-mod.jar"));
+ var z = new JarFile(Path.Combine("helloworld", "helloworld-mod.jar"));
z.GetModuleInfo().Name.Should().Be("helloworld");
}
diff --git a/src/IKVM.Tools.Importer/CompilerClassLoader.cs b/src/IKVM.Tools.Importer/CompilerClassLoader.cs
index 6122369687..5ba606b77d 100644
--- a/src/IKVM.Tools.Importer/CompilerClassLoader.cs
+++ b/src/IKVM.Tools.Importer/CompilerClassLoader.cs
@@ -484,29 +484,35 @@ void SetMain(TypeWrapper type, PEFileKinds target, IDictionary p
throw new ArgumentNullException(nameof(properties));
// global main method decorated with appropriate apartment type
- var mainMethodProxy = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod("Main", MethodAttributes.Public | MethodAttributes.Static, Types.Int32, new Type[] { Types.String.MakeArrayType() });
+ var mainMethodProxy = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod("Main", MethodAttributes.Public | MethodAttributes.Static, Types.Int32, new[] { Types.String.MakeArrayType() });
if (apartmentAttributeType != null)
- mainMethodProxy.SetCustomAttribute(new CustomAttributeBuilder(apartmentAttributeType.GetConstructor(Type.EmptyTypes), new object[0]));
+ mainMethodProxy.SetCustomAttribute(new CustomAttributeBuilder(apartmentAttributeType.GetConstructor(Type.EmptyTypes), Array.Empty()));
var ilgen = CodeEmitter.Create(mainMethodProxy);
- var propertiesType = LoadClassByDottedName("java.util.Properties").TypeAsTBD;
- var launcherType = LoadClassByDottedName("ikvm.runtime.Launcher").TypeAsTBD;
- var launchMethod = launcherType.GetMethod("run", new Type[] { Types.Type, Types.String.MakeArrayType(), Types.String, propertiesType });
- // first argument to Launch (Class)
- ilgen.Emit(OpCodes.Ldtoken, type.TypeAsTBD);
- ilgen.Emit(OpCodes.Call, Types.Type.GetMethod("GetTypeFromHandle"));
+ // first argument to Launch (type name)
+ ilgen.Emit(OpCodes.Ldstr, type.Name);
- // second argument: args
+ // second argument: is this a jar
+ ilgen.Emit(OpCodes.Ldc_I4_0);
+
+ // third argument: args
ilgen.Emit(OpCodes.Ldarg_0);
- // third argument, runtime prefix
+ // fourth argument, runtime prefix
ilgen.Emit(OpCodes.Ldstr, DEFAULT_RUNTIME_ARGS_PREFIX);
- // fourth argument, property set to initialize JVM with
- ilgen.Emit(OpCodes.Newobj, propertiesType.GetConstructor(Type.EmptyTypes));
+ // fifth argument, property set to initialize JVM
if (properties.Count > 0)
{
+ var environmentType = JVM.Import(typeof(Environment));
+ var environmentExpandMethod = environmentType.GetMethod(nameof(Environment.ExpandEnvironmentVariables), new[] { Types.String });
+ var dictionaryType = JVM.Import(typeof(Dictionary));
+ var dictionaryAddMethod = dictionaryType.GetMethod("Add", new[] { Types.String, Types.String });
+
+ ilgen.EmitLdc_I4(properties.Count);
+ ilgen.Emit(OpCodes.Newobj, dictionaryType.GetConstructor(new[] { Types.Int32 }));
+
foreach (var kvp in properties)
{
ilgen.Emit(OpCodes.Dup);
@@ -515,13 +521,19 @@ void SetMain(TypeWrapper type, PEFileKinds target, IDictionary p
// property value can reference an environmental variable (reassess the requirment for this)
if (kvp.Value.IndexOf('%') < kvp.Value.LastIndexOf('%'))
- ilgen.Emit(OpCodes.Call, JVM.Import(typeof(Environment)).GetMethod(nameof(Environment.ExpandEnvironmentVariables), new[] { Types.String }));
+ ilgen.Emit(OpCodes.Call, environmentExpandMethod);
- ilgen.Emit(OpCodes.Callvirt, propertiesType.GetMethod("setProperty", new Type[] { Types.Object, Types.Object }));
+ // add to properties dictionary
+ ilgen.Emit(OpCodes.Callvirt, dictionaryAddMethod);
}
}
+ else
+ {
+ ilgen.Emit(OpCodes.Ldnull);
+ }
// invoke the launcher main method
+ var launchMethod = StaticCompiler.GetRuntimeType("IKVM.Runtime.Launcher").GetMethod("Run");
ilgen.Emit(OpCodes.Call, launchMethod);
ilgen.Emit(OpCodes.Ret);
@@ -2544,18 +2556,16 @@ internal static int Compile(string runtimeAssembly, List option
}
compiler.CompilePass1();
}
+
foreach (CompilerClassLoader compiler in compilers)
{
compiler.CompilePass2();
}
+
if (compilingCoreAssembly)
- {
- RuntimeHelperTypes.Create(compilers[0]);
foreach (CompilerClassLoader compiler in compilers)
- {
compiler.EmitRemappedTypes2ndPass();
- }
- }
+
foreach (CompilerClassLoader compiler in compilers)
{
int rc = compiler.CompilePass3();
@@ -2564,6 +2574,7 @@ internal static int Compile(string runtimeAssembly, List option
return rc;
}
}
+
Tracer.Info(Tracer.Compiler, "CompilerClassLoader.Save...");
foreach (CompilerClassLoader compiler in compilers)
{
@@ -3060,8 +3071,21 @@ private int CompilePass3()
throw new FatalCompilerErrorException(Message.ClassLoaderConstructorMissing);
// apply custom attribute specifying custom class loader
- var ci = JVM.LoadType(typeof(CustomAssemblyClassLoaderAttribute)).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { Types.Type }, null);
+ var ci = JVM.LoadType(typeof(CustomAssemblyClassLoaderAttribute)).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { Types.Type }, null);
assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ci, new object[] { classLoaderType.TypeAsTBD }));
+
+ // the class loader type defines a module initialize method, ensure we call it upon module load
+ var mwModuleInit = classLoaderType.GetMethodWrapper("InitializeModule", "(Lcli.System.Reflection.Module;)V", false);
+ if (mwModuleInit != null && mwModuleInit.IsStatic == false)
+ {
+ var moduleInit = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes);
+ var moduleInitIL = moduleInit.GetILGenerator();
+ moduleInitIL.Emit(OpCodes.Ldtoken, moduleInit);
+ moduleInitIL.Emit(OpCodes.Call, JVM.Import(typeof(System.Reflection.MethodBase)).GetMethod("GetMethodFromHandle", new[] { JVM.Import(typeof(RuntimeMethodHandle)) }));
+ moduleInitIL.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Reflection.MemberInfo)).GetProperty("Module").GetGetMethod());
+ moduleInitIL.Emit(OpCodes.Call, StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule"));
+ moduleInitIL.Emit(OpCodes.Ret);
+ }
}
if (options.iconfile != null)
@@ -3074,16 +3098,6 @@ private int CompilePass3()
assemblyBuilder.__DefineManifestResource(IkvmImporterInternal.ReadAllBytes(options.manifestFile));
}
- // define a module initialization method
- // this method invokes on module load and calls into the runtime
- var moduleInit = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes);
- var moduleInitIL = moduleInit.GetILGenerator();
- moduleInitIL.Emit(OpCodes.Ldtoken, moduleInit);
- moduleInitIL.Emit(OpCodes.Call, JVM.Import(typeof(System.Reflection.MethodBase)).GetMethod("GetMethodFromHandle", new Type[] { JVM.Import(typeof(RuntimeMethodHandle)) }));
- moduleInitIL.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Reflection.MemberInfo)).GetMethod("get_Module"));
- moduleInitIL.Emit(OpCodes.Call, StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule"));
- moduleInitIL.Emit(OpCodes.Ret);
-
assemblyBuilder.DefineVersionInfoResource();
return 0;
diff --git a/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj b/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj
index 6805c372ac..fc20058fc3 100644
--- a/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj
+++ b/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj
@@ -16,7 +16,7 @@
-
+
PreserveNewest
diff --git a/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs b/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs
index 69b15b19ab..9b4e03a04e 100644
--- a/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs
+++ b/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs
@@ -25,7 +25,7 @@ async Task CompileJar(string tfm)
{
var libs = Path.Combine(TESTBASE, "lib", tfm);
- var p = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), "ext", tfm, "helloworld-2.0.dll");
+ var p = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), "helloworld", tfm, "helloworld-2.0.dll");
Directory.CreateDirectory(Path.GetDirectoryName(p));
var rid = "";
@@ -42,7 +42,7 @@ async Task CompileJar(string tfm)
{
Runtime = Path.Combine(TESTBASE, "lib", tfm, "IKVM.Runtime.dll"),
ResponseFile = $"CompileJar_{tfm}_ikvmc.rsp",
- Input = { Path.Combine(TESTBASE, "ext", "helloworld-2.0.jar") },
+ Input = { Path.Combine(TESTBASE, "helloworld", "helloworld-2.0.jar") },
Assembly = "helloworld-2.0",
Version = "1.0.0.0",
NoStdLib = true,
diff --git a/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs b/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs
index dc717ebb41..50c873377a 100644
--- a/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs
+++ b/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs
@@ -26,7 +26,7 @@ async Task Can_export_dll(string tfm)
{
var libs = Path.Combine(TESTBASE, "lib", tfm);
- var a = Path.Combine(TESTBASE, "ext", tfm, "HelloWorldDotNet.dll");
+ var a = Path.Combine(TESTBASE, "helloworld", tfm, "HelloWorldDotNet.dll");
var p = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), Path.ChangeExtension(a, ".jar"));
Directory.CreateDirectory(Path.GetDirectoryName(p));
diff --git a/src/dist-tests/dist-tests.csproj b/src/dist-tests/dist-tests.csproj
index 57cb08b650..bc59cbfd03 100644
--- a/src/dist-tests/dist-tests.csproj
+++ b/src/dist-tests/dist-tests.csproj
@@ -13,6 +13,8 @@
+
+
@@ -39,7 +41,8 @@
<_ProjectName>%(TestTarget.ProjectName)
<_ProjectFile>%(TestTarget.ProjectFile)
- <_ProjectFile Condition=" '$(_ProjectFile)' == '' ">..\$(_ProjectName)\$(_ProjectName).csproj
+ <_ProjectFile Condition=" '$(_ProjectFile)' == '' And Exists('..\$(_ProjectName)\$(_ProjectName).csproj') ">..\$(_ProjectName)\$(_ProjectName).csproj
+ <_ProjectFile Condition=" '$(_ProjectFile)' == '' And Exists('..\$(_ProjectName)\$(_ProjectName).msbuildproj') ">..\$(_ProjectName)\$(_ProjectName).msbuildproj
<_TargetFramework>%(TestTarget.TargetFramework)
diff --git a/src/java/Program.cs b/src/java/Program.cs
index 2fa263b360..a7336611f4 100644
--- a/src/java/Program.cs
+++ b/src/java/Program.cs
@@ -1,8 +1,5 @@
-using ikvm.runtime;
-
-using IKVM.Attributes;
-
-using java.util;
+using IKVM.Attributes;
+using IKVM.Runtime;
namespace IKVM.Tools.Java
{
@@ -11,7 +8,7 @@ public static class Program
{
[HideFromJava]
- public static int Main(string[] args) => Launcher.run(null, args, "", new Properties());
+ public static int Main(string[] args) => Launcher.Run(null, false, args, "", null);
}
diff --git a/src/java/Properties/launchSettings.json b/src/java/Properties/launchSettings.json
deleted file mode 100644
index c57951f810..0000000000
--- a/src/java/Properties/launchSettings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "profiles": {
- "java": {
- "commandName": "Project",
- "commandLineArgs": "-cp foo"
- }
- }
-}
\ No newline at end of file