mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C backend: support coroutines
This commit is contained in:
parent
578912056b
commit
abdd9b3270
|
@ -15,21 +15,24 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.interop.Rename;
|
import org.teavm.interop.Rename;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
import org.teavm.interop.Superclass;
|
import org.teavm.interop.Superclass;
|
||||||
import org.teavm.interop.Sync;
|
import org.teavm.interop.Sync;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.jso.browser.TimerHandler;
|
import org.teavm.jso.browser.TimerHandler;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
import org.teavm.platform.PlatformObject;
|
import org.teavm.platform.PlatformObject;
|
||||||
import org.teavm.platform.PlatformQueue;
|
import org.teavm.platform.PlatformQueue;
|
||||||
import org.teavm.platform.PlatformRunnable;
|
import org.teavm.platform.PlatformRunnable;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.EventQueue;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
@ -39,25 +42,26 @@ public class TObject {
|
||||||
Monitor monitor;
|
Monitor monitor;
|
||||||
|
|
||||||
static class Monitor {
|
static class Monitor {
|
||||||
|
static final int MASK = 0x80000000;
|
||||||
|
|
||||||
PlatformQueue<PlatformRunnable> enteringThreads;
|
PlatformQueue<PlatformRunnable> enteringThreads;
|
||||||
PlatformQueue<NotifyListener> notifyListeners;
|
PlatformQueue<NotifyListener> notifyListeners;
|
||||||
TThread owner;
|
TThread owner;
|
||||||
int count;
|
int count;
|
||||||
|
int id;
|
||||||
|
|
||||||
public Monitor() {
|
Monitor() {
|
||||||
this.owner = TThread.currentThread();
|
this.owner = TThread.currentThread();
|
||||||
enteringThreads = Platform.createQueue();
|
|
||||||
notifyListeners = Platform.createQueue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotifyListener extends PlatformRunnable {
|
interface NotifyListener extends PlatformRunnable, EventQueue.Event {
|
||||||
boolean expired();
|
boolean expired();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitorEnterSync(TObject o) {
|
static void monitorEnterSync(TObject o) {
|
||||||
if (o.monitor == null) {
|
if (o.monitor == null) {
|
||||||
o.monitor = new Monitor();
|
createMonitor(o);
|
||||||
}
|
}
|
||||||
if (o.monitor.owner == null) {
|
if (o.monitor.owner == null) {
|
||||||
o.monitor.owner = TThread.currentThread();
|
o.monitor.owner = TThread.currentThread();
|
||||||
|
@ -83,7 +87,7 @@ public class TObject {
|
||||||
|
|
||||||
static void monitorEnter(TObject o, int count) {
|
static void monitorEnter(TObject o, int count) {
|
||||||
if (o.monitor == null) {
|
if (o.monitor == null) {
|
||||||
o.monitor = new Monitor();
|
createMonitor(o);
|
||||||
}
|
}
|
||||||
if (o.monitor.owner == null) {
|
if (o.monitor.owner == null) {
|
||||||
o.monitor.owner = TThread.currentThread();
|
o.monitor.owner = TThread.currentThread();
|
||||||
|
@ -95,13 +99,23 @@ public class TObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void createMonitor(TObject o) {
|
||||||
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
int hashCode = hashCodeLowLevel(o);
|
||||||
|
o.monitor = new Monitor();
|
||||||
|
o.monitor.id = hashCode;
|
||||||
|
} else {
|
||||||
|
o.monitor = new Monitor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
static native void monitorEnterWait(TObject o, int count);
|
static native void monitorEnterWait(TObject o, int count);
|
||||||
|
|
||||||
static void monitorEnterWait(final TObject o, final int count, final AsyncCallback<Void> callback) {
|
static void monitorEnterWait(TObject o, int count, AsyncCallback<Void> callback) {
|
||||||
final TThread thread = TThread.currentThread();
|
TThread thread = TThread.currentThread();
|
||||||
if (o.monitor == null) {
|
if (o.monitor == null) {
|
||||||
o.monitor = new Monitor();
|
createMonitor(o);
|
||||||
TThread.setCurrentThread(thread);
|
TThread.setCurrentThread(thread);
|
||||||
o.monitor.count += count;
|
o.monitor.count += count;
|
||||||
callback.complete(null);
|
callback.complete(null);
|
||||||
|
@ -113,7 +127,12 @@ public class TObject {
|
||||||
callback.complete(null);
|
callback.complete(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
o.monitor.enteringThreads.add(() -> {
|
|
||||||
|
Monitor monitor = o.monitor;
|
||||||
|
if (monitor.enteringThreads == null) {
|
||||||
|
monitor.enteringThreads = Platform.createQueue();
|
||||||
|
}
|
||||||
|
monitor.enteringThreads.add(() -> {
|
||||||
TThread.setCurrentThread(thread);
|
TThread.setCurrentThread(thread);
|
||||||
o.monitor.owner = thread;
|
o.monitor.owner = thread;
|
||||||
o.monitor.count += count;
|
o.monitor.count += count;
|
||||||
|
@ -122,47 +141,73 @@ public class TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Sync
|
@Sync
|
||||||
static void monitorExit(final TObject o) {
|
static void monitorExit(TObject o) {
|
||||||
monitorExit(o, 1);
|
monitorExit(o, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Sync
|
@Sync
|
||||||
static void monitorExit(final TObject o, int count) {
|
static void monitorExit(TObject o, int count) {
|
||||||
if (o.isEmptyMonitor() || o.monitor.owner != TThread.currentThread()) {
|
if (o.isEmptyMonitor() || o.monitor.owner != TThread.currentThread()) {
|
||||||
throw new TIllegalMonitorStateException();
|
throw new TIllegalMonitorStateException();
|
||||||
}
|
}
|
||||||
o.monitor.count -= count;
|
|
||||||
if (o.monitor.count > 0) {
|
Monitor monitor = o.monitor;
|
||||||
|
monitor.count -= count;
|
||||||
|
if (monitor.count > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
o.monitor.owner = null;
|
monitor.owner = null;
|
||||||
if (!o.monitor.enteringThreads.isEmpty()) {
|
if (monitor.enteringThreads != null && !monitor.enteringThreads.isEmpty()) {
|
||||||
Platform.postpone(() -> {
|
if (PlatformDetector.isLowLevel()) {
|
||||||
if (o.isEmptyMonitor() || o.monitor.owner != null) {
|
EventQueue.offer(() -> waitForOtherThreads(o));
|
||||||
return;
|
} else {
|
||||||
}
|
Platform.postpone(() -> waitForOtherThreads(o));
|
||||||
if (!o.monitor.enteringThreads.isEmpty()) {
|
}
|
||||||
o.monitor.enteringThreads.remove().run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
o.isEmptyMonitor();
|
o.isEmptyMonitor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void waitForOtherThreads(TObject o) {
|
||||||
|
if (o.isEmptyMonitor() || o.monitor.owner != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Monitor monitor = o.monitor;
|
||||||
|
if (monitor.enteringThreads != null && !monitor.enteringThreads.isEmpty()) {
|
||||||
|
PlatformQueue<PlatformRunnable> enteringThreads = monitor.enteringThreads;
|
||||||
|
PlatformRunnable r = enteringThreads.remove();
|
||||||
|
if (enteringThreads == null) {
|
||||||
|
monitor.enteringThreads = null;
|
||||||
|
}
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean isEmptyMonitor() {
|
boolean isEmptyMonitor() {
|
||||||
|
Monitor monitor = this.monitor;
|
||||||
if (monitor == null) {
|
if (monitor == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (monitor.owner == null && monitor.enteringThreads.isEmpty() && monitor.notifyListeners.isEmpty()) {
|
if (monitor.owner == null
|
||||||
monitor = null;
|
&& (monitor.enteringThreads == null || monitor.enteringThreads.isEmpty())
|
||||||
|
&& (monitor.notifyListeners == null || monitor.notifyListeners.isEmpty())) {
|
||||||
|
deleteMonitor();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deleteMonitor() {
|
||||||
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
int id = monitor.id;
|
||||||
|
setHashCodeLowLevel(this, id);
|
||||||
|
} else {
|
||||||
|
monitor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static boolean holdsLock(TObject o) {
|
static boolean holdsLock(TObject o) {
|
||||||
return o.monitor != null && o.monitor.owner == TThread.currentThread();
|
return o.monitor != null && o.monitor.owner == TThread.currentThread();
|
||||||
}
|
}
|
||||||
|
@ -200,8 +245,25 @@ public class TObject {
|
||||||
return getClass().getName() + "@" + TInteger.toHexString(identity());
|
return getClass().getName() + "@" + TInteger.toHexString(identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("identityLowLevel")
|
|
||||||
int identity() {
|
int identity() {
|
||||||
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
Monitor monitor = this.monitor;
|
||||||
|
if (monitor == null) {
|
||||||
|
int hashCode = hashCodeLowLevel(this);
|
||||||
|
if (hashCode == 0) {
|
||||||
|
hashCode = identityLowLevel();
|
||||||
|
setHashCodeLowLevel(this, hashCode);
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
} else {
|
||||||
|
int hashCode = monitor.id;
|
||||||
|
if (hashCode == 0) {
|
||||||
|
hashCode = identityLowLevel();
|
||||||
|
monitor.id = hashCode;
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
PlatformObject platformThis = Platform.getPlatformObject(this);
|
PlatformObject platformThis = Platform.getPlatformObject(this);
|
||||||
if (platformThis.getId() == 0) {
|
if (platformThis.getId() == 0) {
|
||||||
platformThis.setId(Platform.nextObjectId());
|
platformThis.setId(Platform.nextObjectId());
|
||||||
|
@ -209,19 +271,48 @@ public class TObject {
|
||||||
return Platform.getPlatformObject(this).getId();
|
return Platform.getPlatformObject(this).getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@DelegateTo("hashCodeLowLevelImpl")
|
||||||
private static int identityLowLevel(RuntimeObject object) {
|
private static native int hashCodeLowLevel(TObject obj);
|
||||||
int result = object.hashCode;
|
|
||||||
|
@Unmanaged
|
||||||
|
private static int hashCodeLowLevelImpl(RuntimeObject obj) {
|
||||||
|
return obj.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DelegateTo("setHashCodeLowLevelImpl")
|
||||||
|
private static native void setHashCodeLowLevel(TObject obj, int value);
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
private static void setHashCodeLowLevelImpl(RuntimeObject obj, int value) {
|
||||||
|
obj.hashCode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
private static int identityLowLevel() {
|
||||||
|
int result = RuntimeObject.nextId++;
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
result = RuntimeObject.nextId++;
|
result = RuntimeObject.nextId++;
|
||||||
if (result == 0) {
|
if (result == Monitor.MASK) {
|
||||||
result = RuntimeObject.nextId++;
|
result = 1;
|
||||||
}
|
}
|
||||||
object.hashCode = result;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DelegateTo("identityOrMonitorLowLevel")
|
||||||
|
private native int identityOrMonitor();
|
||||||
|
|
||||||
|
private static int identityOrMonitorLowLevel(RuntimeObject object) {
|
||||||
|
return object.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DelegateTo("setIdentityLowLevel")
|
||||||
|
native void setIdentity(int id);
|
||||||
|
|
||||||
|
private static void setIdentityLowLevel(RuntimeObject object, int id) {
|
||||||
|
object.hashCode = id;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@DelegateTo("cloneLowLevel")
|
@DelegateTo("cloneLowLevel")
|
||||||
@PluggableDependency(ObjectDependencyPlugin.class)
|
@PluggableDependency(ObjectDependencyPlugin.class)
|
||||||
|
@ -264,13 +355,23 @@ public class TObject {
|
||||||
throw new TIllegalMonitorStateException();
|
throw new TIllegalMonitorStateException();
|
||||||
}
|
}
|
||||||
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||||
|
if (listeners == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (!listeners.isEmpty()) {
|
while (!listeners.isEmpty()) {
|
||||||
NotifyListener listener = listeners.remove();
|
NotifyListener listener = listeners.remove();
|
||||||
if (!listener.expired()) {
|
if (!listener.expired()) {
|
||||||
Platform.postpone(listener);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.offer(listener);
|
||||||
|
} else {
|
||||||
|
Platform.postpone(listener);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
monitor.notifyListeners = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Sync
|
@Sync
|
||||||
|
@ -280,12 +381,20 @@ public class TObject {
|
||||||
throw new TIllegalMonitorStateException();
|
throw new TIllegalMonitorStateException();
|
||||||
}
|
}
|
||||||
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||||
|
if (listeners == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (!listeners.isEmpty()) {
|
while (!listeners.isEmpty()) {
|
||||||
NotifyListener listener = listeners.remove();
|
NotifyListener listener = listeners.remove();
|
||||||
if (!listener.expired()) {
|
if (!listener.expired()) {
|
||||||
Platform.postpone(listener);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.offer(listener);
|
||||||
|
} else {
|
||||||
|
Platform.postpone(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
monitor.notifyListeners = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rename("wait")
|
@Rename("wait")
|
||||||
|
@ -309,17 +418,23 @@ public class TObject {
|
||||||
private native void waitImpl(long timeout, int nanos) throws TInterruptedException;
|
private native void waitImpl(long timeout, int nanos) throws TInterruptedException;
|
||||||
|
|
||||||
public final void waitImpl(long timeout, int nanos, AsyncCallback<Void> callback) {
|
public final void waitImpl(long timeout, int nanos, AsyncCallback<Void> callback) {
|
||||||
|
Monitor monitor = this.monitor;
|
||||||
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
|
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
|
||||||
|
if (monitor.notifyListeners == null) {
|
||||||
|
monitor.notifyListeners = Platform.createQueue();
|
||||||
|
}
|
||||||
monitor.notifyListeners.add(listener);
|
monitor.notifyListeners.add(listener);
|
||||||
TThread.currentThread().interruptHandler = listener;
|
TThread.currentThread().interruptHandler = listener;
|
||||||
if (timeout > 0 || nanos > 0) {
|
if (timeout > 0 || nanos > 0) {
|
||||||
listener.timerId = Platform.schedule(listener, timeout >= Integer.MAX_VALUE ? Integer.MAX_VALUE
|
int timeoutToSchedule = timeout >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) timeout;
|
||||||
: (int) timeout);
|
listener.timerId = PlatformDetector.isLowLevel()
|
||||||
|
? EventQueue.offer(listener, timeoutToSchedule + System.currentTimeMillis())
|
||||||
|
: Platform.schedule(listener, timeoutToSchedule);
|
||||||
}
|
}
|
||||||
monitorExit(this, monitor.count);
|
monitorExit(this, monitor.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NotifyListenerImpl implements NotifyListener, TimerHandler, PlatformRunnable,
|
static class NotifyListenerImpl implements NotifyListener, TimerHandler, PlatformRunnable,
|
||||||
TThreadInterruptHandler {
|
TThreadInterruptHandler {
|
||||||
final TObject obj;
|
final TObject obj;
|
||||||
final AsyncCallback<Void> callback;
|
final AsyncCallback<Void> callback;
|
||||||
|
@ -329,7 +444,7 @@ public class TObject {
|
||||||
boolean performed;
|
boolean performed;
|
||||||
int lockCount;
|
int lockCount;
|
||||||
|
|
||||||
public NotifyListenerImpl(TObject obj, AsyncCallback<Void> callback, int lockCount) {
|
NotifyListenerImpl(TObject obj, AsyncCallback<Void> callback, int lockCount) {
|
||||||
this.obj = obj;
|
this.obj = obj;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.lockCount = lockCount;
|
this.lockCount = lockCount;
|
||||||
|
@ -344,11 +459,19 @@ public class TObject {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimer() {
|
public void onTimer() {
|
||||||
Platform.postpone(() -> {
|
if (PlatformDetector.isLowLevel()) {
|
||||||
if (!expired()) {
|
EventQueue.offer(() -> {
|
||||||
run();
|
if (!expired()) {
|
||||||
}
|
run();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.postpone(() -> {
|
||||||
|
if (!expired()) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -358,7 +481,11 @@ public class TObject {
|
||||||
}
|
}
|
||||||
performed = true;
|
performed = true;
|
||||||
if (timerId >= 0) {
|
if (timerId >= 0) {
|
||||||
Platform.killSchedule(timerId);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.kill(timerId);
|
||||||
|
} else {
|
||||||
|
Platform.killSchedule(timerId);
|
||||||
|
}
|
||||||
timerId = -1;
|
timerId = -1;
|
||||||
}
|
}
|
||||||
TThread.setCurrentThread(currentThread);
|
TThread.setCurrentThread(currentThread);
|
||||||
|
@ -372,10 +499,18 @@ public class TObject {
|
||||||
}
|
}
|
||||||
performed = true;
|
performed = true;
|
||||||
if (timerId >= 0) {
|
if (timerId >= 0) {
|
||||||
Platform.killSchedule(timerId);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.kill(timerId);
|
||||||
|
} else {
|
||||||
|
Platform.killSchedule(timerId);
|
||||||
|
}
|
||||||
timerId = -1;
|
timerId = -1;
|
||||||
}
|
}
|
||||||
Platform.postpone(() -> callback.error(new TInterruptedException()));
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.offer(() -> callback.error(new TInterruptedException()));
|
||||||
|
} else {
|
||||||
|
Platform.postpone(() -> callback.error(new TInterruptedException()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
import org.teavm.platform.PlatformRunnable;
|
import org.teavm.platform.PlatformRunnable;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.runtime.EventQueue;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
public class TThread extends TObject implements TRunnable {
|
public class TThread extends TObject implements TRunnable {
|
||||||
private static TThread mainThread = new TThread("main");
|
private static TThread mainThread = new TThread("main");
|
||||||
|
@ -56,20 +59,26 @@ public class TThread extends TObject implements TRunnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
Platform.startThread(() -> {
|
if (PlatformDetector.isLowLevel()) {
|
||||||
try {
|
EventQueue.offer(() -> Fiber.start(this::runThread));
|
||||||
activeCount++;
|
} else {
|
||||||
setCurrentThread(TThread.this);
|
Platform.startThread(this::runThread);
|
||||||
TThread.this.run();
|
}
|
||||||
} finally {
|
}
|
||||||
synchronized (finishedLock) {
|
|
||||||
finishedLock.notifyAll();
|
private void runThread() {
|
||||||
}
|
try {
|
||||||
alive = false;
|
activeCount++;
|
||||||
activeCount--;
|
setCurrentThread(TThread.this);
|
||||||
setCurrentThread(mainThread);
|
TThread.this.run();
|
||||||
|
} finally {
|
||||||
|
synchronized (finishedLock) {
|
||||||
|
finishedLock.notifyAll();
|
||||||
}
|
}
|
||||||
});
|
alive = false;
|
||||||
|
activeCount--;
|
||||||
|
setCurrentThread(mainThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setCurrentThread(TThread thread) {
|
static void setCurrentThread(TThread thread) {
|
||||||
|
@ -130,10 +139,17 @@ public class TThread extends TObject implements TRunnable {
|
||||||
static native void switchContext(TThread thread);
|
static native void switchContext(TThread thread);
|
||||||
|
|
||||||
private static void switchContext(final TThread thread, final AsyncCallback<Void> callback) {
|
private static void switchContext(final TThread thread, final AsyncCallback<Void> callback) {
|
||||||
Platform.postpone(() -> {
|
if (PlatformDetector.isLowLevel()) {
|
||||||
setCurrentThread(thread);
|
EventQueue.offer(() -> {
|
||||||
callback.complete(null);
|
setCurrentThread(thread);
|
||||||
});
|
callback.complete(null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.postpone(() -> {
|
||||||
|
setCurrentThread(thread);
|
||||||
|
callback.complete(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
|
@ -176,19 +192,24 @@ public class TThread extends TObject implements TRunnable {
|
||||||
|
|
||||||
private static void sleep(long millis, final AsyncCallback<Void> callback) {
|
private static void sleep(long millis, final AsyncCallback<Void> callback) {
|
||||||
final TThread current = currentThread();
|
final TThread current = currentThread();
|
||||||
int intMillis = millis < Integer.MAX_VALUE ? (int) millis : Integer.MAX_VALUE;
|
|
||||||
SleepHandler handler = new SleepHandler(current, callback);
|
SleepHandler handler = new SleepHandler(current, callback);
|
||||||
handler.scheduleId = Platform.schedule(handler, intMillis);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
current.interruptHandler = handler;
|
handler.scheduleId = EventQueue.offer(handler, System.currentTimeMillis() + millis);
|
||||||
|
current.interruptHandler = handler;
|
||||||
|
} else {
|
||||||
|
int intMillis = millis < Integer.MAX_VALUE ? (int) millis : Integer.MAX_VALUE;
|
||||||
|
handler.scheduleId = Platform.schedule(handler, intMillis);
|
||||||
|
current.interruptHandler = handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SleepHandler implements PlatformRunnable, TThreadInterruptHandler {
|
static class SleepHandler implements PlatformRunnable, EventQueue.Event, TThreadInterruptHandler {
|
||||||
private TThread thread;
|
private TThread thread;
|
||||||
private AsyncCallback<Void> callback;
|
private AsyncCallback<Void> callback;
|
||||||
private boolean isInterrupted;
|
private boolean isInterrupted;
|
||||||
int scheduleId;
|
int scheduleId;
|
||||||
|
|
||||||
public SleepHandler(TThread thread, AsyncCallback<Void> callback) {
|
SleepHandler(TThread thread, AsyncCallback<Void> callback) {
|
||||||
this.thread = thread;
|
this.thread = thread;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
@ -197,8 +218,13 @@ public class TThread extends TObject implements TRunnable {
|
||||||
public void interrupted() {
|
public void interrupted() {
|
||||||
thread.interruptedFlag = false;
|
thread.interruptedFlag = false;
|
||||||
isInterrupted = true;
|
isInterrupted = true;
|
||||||
Platform.killSchedule(scheduleId);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
Platform.postpone(() -> callback.error(new TInterruptedException()));
|
EventQueue.kill(scheduleId);
|
||||||
|
EventQueue.offer(() -> callback.error(new TInterruptedException()));
|
||||||
|
} else {
|
||||||
|
Platform.killSchedule(scheduleId);
|
||||||
|
Platform.postpone(() -> callback.error(new TInterruptedException()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,10 +27,10 @@ import java.util.Map;
|
||||||
import org.teavm.classlib.java.net.THttpURLConnection;
|
import org.teavm.classlib.java.net.THttpURLConnection;
|
||||||
import org.teavm.classlib.java.net.TURL;
|
import org.teavm.classlib.java.net.TURL;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.jso.ajax.XMLHttpRequest;
|
import org.teavm.jso.ajax.XMLHttpRequest;
|
||||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||||
import org.teavm.jso.typedarrays.Int8Array;
|
import org.teavm.jso.typedarrays.Int8Array;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
|
||||||
|
|
||||||
public class TXHRURLConnection extends THttpURLConnection {
|
public class TXHRURLConnection extends THttpURLConnection {
|
||||||
private XMLHttpRequest xhr;
|
private XMLHttpRequest xhr;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.classlib.java.lang.TInterruptedException;
|
import org.teavm.classlib.java.lang.TInterruptedException;
|
||||||
import org.teavm.classlib.java.lang.TThread;
|
import org.teavm.classlib.java.lang.TThread;
|
||||||
import org.teavm.classlib.java.lang.TThreadInterruptHandler;
|
import org.teavm.classlib.java.lang.TThreadInterruptHandler;
|
||||||
|
@ -27,11 +28,12 @@ import org.teavm.classlib.java.util.TAbstractQueue;
|
||||||
import org.teavm.classlib.java.util.TCollection;
|
import org.teavm.classlib.java.util.TCollection;
|
||||||
import org.teavm.classlib.java.util.TIterator;
|
import org.teavm.classlib.java.util.TIterator;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.interop.Sync;
|
import org.teavm.interop.Sync;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
import org.teavm.platform.PlatformQueue;
|
import org.teavm.platform.PlatformQueue;
|
||||||
import org.teavm.platform.PlatformRunnable;
|
import org.teavm.platform.PlatformRunnable;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.runtime.EventQueue;
|
||||||
|
|
||||||
public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlockingQueue<E> {
|
public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlockingQueue<E> {
|
||||||
private Object[] array;
|
private Object[] array;
|
||||||
|
@ -418,7 +420,11 @@ public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlocki
|
||||||
if (waitHandlers != null) {
|
if (waitHandlers != null) {
|
||||||
while (!waitHandlers.isEmpty()) {
|
while (!waitHandlers.isEmpty()) {
|
||||||
WaitHandler handler = waitHandlers.remove();
|
WaitHandler handler = waitHandlers.remove();
|
||||||
Platform.postpone(() -> handler.changed());
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.offer(handler::changed);
|
||||||
|
} else {
|
||||||
|
Platform.postpone(handler::changed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
waitHandlers = null;
|
waitHandlers = null;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +442,9 @@ public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlocki
|
||||||
waitHandlers.add(handler);
|
waitHandlers.add(handler);
|
||||||
if (timeLimit > 0) {
|
if (timeLimit > 0) {
|
||||||
int timeout = Math.max(0, (int) (timeLimit - System.currentTimeMillis()));
|
int timeout = Math.max(0, (int) (timeLimit - System.currentTimeMillis()));
|
||||||
handler.timerId = Platform.schedule(handler, timeout);
|
handler.timerId = PlatformDetector.isLowLevel()
|
||||||
|
? EventQueue.offer(handler, timeLimit)
|
||||||
|
: Platform.schedule(handler, timeout);
|
||||||
} else {
|
} else {
|
||||||
handler.timerId = -1;
|
handler.timerId = -1;
|
||||||
}
|
}
|
||||||
|
@ -444,7 +452,7 @@ public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlocki
|
||||||
TThread.currentThread().interruptHandler = handler;
|
TThread.currentThread().interruptHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WaitHandler implements PlatformRunnable, TThreadInterruptHandler {
|
class WaitHandler implements PlatformRunnable, TThreadInterruptHandler, EventQueue.Event {
|
||||||
AsyncCallback<Boolean> callback;
|
AsyncCallback<Boolean> callback;
|
||||||
boolean complete;
|
boolean complete;
|
||||||
int timerId;
|
int timerId;
|
||||||
|
@ -475,7 +483,11 @@ public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlocki
|
||||||
}
|
}
|
||||||
complete = true;
|
complete = true;
|
||||||
if (timerId >= 0) {
|
if (timerId >= 0) {
|
||||||
Platform.killSchedule(timerId);
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.kill(timerId);
|
||||||
|
} else {
|
||||||
|
Platform.killSchedule(timerId);
|
||||||
|
}
|
||||||
timerId = -1;
|
timerId = -1;
|
||||||
}
|
}
|
||||||
TThread.currentThread().interruptHandler = null;
|
TThread.currentThread().interruptHandler = null;
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.ast.decompilation.Decompiler;
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.c.analyze.CDependencyListener;
|
import org.teavm.backend.c.analyze.CDependencyListener;
|
||||||
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
||||||
|
@ -51,6 +52,7 @@ import org.teavm.backend.c.intrinsic.FunctionIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.GCIntrinsic;
|
import org.teavm.backend.c.intrinsic.GCIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.IntegerIntrinsic;
|
import org.teavm.backend.c.intrinsic.IntegerIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||||
|
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
||||||
import org.teavm.backend.c.intrinsic.IntrinsicFactory;
|
import org.teavm.backend.c.intrinsic.IntrinsicFactory;
|
||||||
import org.teavm.backend.c.intrinsic.LongIntrinsic;
|
import org.teavm.backend.c.intrinsic.LongIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.MutatorIntrinsic;
|
import org.teavm.backend.c.intrinsic.MutatorIntrinsic;
|
||||||
|
@ -61,6 +63,7 @@ import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
|
import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
||||||
|
import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
|
||||||
import org.teavm.dependency.ClassDependency;
|
import org.teavm.dependency.ClassDependency;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
@ -96,8 +99,11 @@ import org.teavm.model.lowlevel.NullCheckTransformation;
|
||||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||||
import org.teavm.model.transformation.ClassPatch;
|
import org.teavm.model.transformation.ClassPatch;
|
||||||
|
import org.teavm.model.util.AsyncMethodFinder;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.EventQueue;
|
||||||
import org.teavm.runtime.ExceptionHandling;
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
@ -121,6 +127,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
||||||
private int minHeapSize = 32 * 1024 * 1024;
|
private int minHeapSize = 32 * 1024 * 1024;
|
||||||
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
||||||
|
private Set<MethodReference> asyncMethods;
|
||||||
|
|
||||||
public void setMinHeapSize(int minHeapSize) {
|
public void setMinHeapSize(int minHeapSize) {
|
||||||
this.minHeapSize = minHeapSize;
|
this.minHeapSize = minHeapSize;
|
||||||
|
@ -201,6 +208,30 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
dependencyAnalyzer.linkField(field.getReference());
|
dependencyAnalyzer.linkField(field.getReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "isResuming", boolean.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "current", Fiber.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "startMain", void.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(EventQueue.class, "process", void.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class,
|
||||||
|
void.class)).use();
|
||||||
|
|
||||||
|
ClassReader fiberClass = dependencyAnalyzer.getClassSource().get(Fiber.class.getName());
|
||||||
|
for (MethodReader method : fiberClass.getMethods()) {
|
||||||
|
if (method.getName().startsWith("pop") || method.getName().equals("push")) {
|
||||||
|
dependencyAnalyzer.linkMethod(method.getReference()).use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void analyzeBeforeOptimizations(ListableClassReaderSource classSource) {
|
||||||
|
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
||||||
|
controller.getDiagnostics());
|
||||||
|
asyncFinder.find(classSource);
|
||||||
|
asyncMethods = new HashSet<>(asyncFinder.getAsyncMethods());
|
||||||
|
asyncMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,6 +245,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
classInitializerEliminator.apply(program);
|
classInitializerEliminator.apply(program);
|
||||||
classInitializerTransformer.transform(program);
|
classInitializerTransformer.transform(program);
|
||||||
nullCheckTransformation.apply(program, method.getResultType());
|
nullCheckTransformation.apply(program, method.getResultType());
|
||||||
|
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods)
|
||||||
|
.apply(program, method.getReference());
|
||||||
shadowStackTransformer.apply(program, method);
|
shadowStackTransformer.apply(program, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +275,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
intrinsics.add(new ExceptionHandlingIntrinsic());
|
intrinsics.add(new ExceptionHandlingIntrinsic());
|
||||||
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
|
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
|
||||||
intrinsics.add(new RuntimeClassIntrinsic());
|
intrinsics.add(new RuntimeClassIntrinsic());
|
||||||
|
intrinsics.add(new FiberIntrinsic());
|
||||||
intrinsics.add(new LongIntrinsic());
|
intrinsics.add(new LongIntrinsic());
|
||||||
intrinsics.add(new IntegerIntrinsic());
|
intrinsics.add(new IntegerIntrinsic());
|
||||||
|
|
||||||
|
@ -250,7 +284,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
|
|
||||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||||
intrinsics, generators);
|
intrinsics, generators, asyncMethods::contains);
|
||||||
|
|
||||||
BufferedCodeWriter codeWriter = new BufferedCodeWriter();
|
BufferedCodeWriter codeWriter = new BufferedCodeWriter();
|
||||||
copyResource(codeWriter, "runtime.c");
|
copyResource(codeWriter, "runtime.c");
|
||||||
|
@ -425,7 +459,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
writer.println("initStaticFields();");
|
writer.println("initStaticFields();");
|
||||||
generateStaticInitializerCalls(context, writer, classes);
|
generateStaticInitializerCalls(context, writer, classes);
|
||||||
writer.println(context.getNames().forClassInitializer("java.lang.String") + "();");
|
writer.println(context.getNames().forClassInitializer("java.lang.String") + "();");
|
||||||
generateCallToMainMethod(context, writer);
|
generateFiberStart(context, writer);
|
||||||
|
|
||||||
writer.outdent().println("}");
|
writer.outdent().println("}");
|
||||||
}
|
}
|
||||||
|
@ -481,10 +515,49 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
writer.outdent().println("}");
|
writer.outdent().println("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateCallToMainMethod(GenerationContext context, CodeWriter writer) {
|
private void generateFiberStart(GenerationContext context, CodeWriter writer) {
|
||||||
|
String startName = context.getNames().forMethod(new MethodReference(Fiber.class, "startMain", void.class));
|
||||||
|
String processName = context.getNames().forMethod(new MethodReference(EventQueue.class, "process", void.class));
|
||||||
|
writer.println(startName + "();");
|
||||||
|
writer.println(processName + "();");
|
||||||
|
}
|
||||||
|
|
||||||
|
class FiberIntrinsic implements Intrinsic {
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(MethodReference method) {
|
||||||
|
if (!method.getClassName().equals(Fiber.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "runMain":
|
||||||
|
case "setCurrentThread":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "runMain":
|
||||||
|
generateCallToMainMethod(context.names(), context.writer());
|
||||||
|
break;
|
||||||
|
case "setCurrentThread":
|
||||||
|
String methodName = context.names().forMethod(new MethodReference(Thread.class,
|
||||||
|
"setCurrentThread", Thread.class, void.class));
|
||||||
|
context.writer().print(methodName).print("(");
|
||||||
|
context.emit(invocation.getArguments().get(0));
|
||||||
|
context.writer().print(")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateCallToMainMethod(NameProvider names, CodeWriter writer) {
|
||||||
TeaVMEntryPoint entryPoint = controller.getEntryPoints().get("main");
|
TeaVMEntryPoint entryPoint = controller.getEntryPoints().get("main");
|
||||||
if (entryPoint != null) {
|
if (entryPoint != null) {
|
||||||
String mainMethod = context.getNames().forMethod(entryPoint.getMethod());
|
String mainMethod = names.forMethod(entryPoint.getMethod());
|
||||||
writer.println(mainMethod + "(NULL);");
|
writer.println(mainMethod + "(NULL);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,6 +569,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAsyncSupported() {
|
public boolean isAsyncSupported() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,6 @@ public class ClassGenerator {
|
||||||
codeGenerator.generateMethodSignature(forwardDeclarationsWriter, method.getReference(),
|
codeGenerator.generateMethodSignature(forwardDeclarationsWriter, method.getReference(),
|
||||||
method.hasModifier(ElementModifier.STATIC), false);
|
method.hasModifier(ElementModifier.STATIC), false);
|
||||||
forwardDeclarationsWriter.println(";");
|
forwardDeclarationsWriter.println(";");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInitializer(ClassHolder cls) {
|
private void generateInitializer(ClassHolder cls) {
|
||||||
|
|
|
@ -65,14 +65,17 @@ import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.VirtualTable;
|
import org.teavm.model.classes.VirtualTable;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.ExceptionHandling;
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
|
||||||
public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
private static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class,
|
private static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class,
|
||||||
|
@ -83,6 +86,14 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
"allocateMultiArray", RuntimeClass.class, Address.class, int.class, RuntimeArray.class);
|
"allocateMultiArray", RuntimeClass.class, Address.class, int.class, RuntimeArray.class);
|
||||||
private static final MethodReference THROW_EXCEPTION_METHOD = new MethodReference(ExceptionHandling.class,
|
private static final MethodReference THROW_EXCEPTION_METHOD = new MethodReference(ExceptionHandling.class,
|
||||||
"throwException", Throwable.class, void.class);
|
"throwException", Throwable.class, void.class);
|
||||||
|
private static final MethodReference MONITOR_ENTER = new MethodReference(Object.class, "monitorEnter",
|
||||||
|
Object.class, void.class);
|
||||||
|
private static final MethodReference MONITOR_EXIT = new MethodReference(Object.class, "monitorExit",
|
||||||
|
Object.class, void.class);
|
||||||
|
private static final MethodReference MONITOR_ENTER_SYNC = new MethodReference(Object.class, "monitorEnterSync",
|
||||||
|
Object.class, void.class);
|
||||||
|
private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, "monitorExitSync",
|
||||||
|
Object.class, void.class);
|
||||||
|
|
||||||
private GenerationContext context;
|
private GenerationContext context;
|
||||||
private NameProvider names;
|
private NameProvider names;
|
||||||
|
@ -91,6 +102,9 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
private int[] maxTemporaryVariableLevel = new int[5];
|
private int[] maxTemporaryVariableLevel = new int[5];
|
||||||
private MethodReference callingMethod;
|
private MethodReference callingMethod;
|
||||||
private Set<? super String> includes;
|
private Set<? super String> includes;
|
||||||
|
private int currentPart;
|
||||||
|
private boolean end;
|
||||||
|
private boolean async;
|
||||||
|
|
||||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer, Set<? super String> includes) {
|
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer, Set<? super String> includes) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
@ -99,6 +113,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
this.includes = includes;
|
this.includes = includes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAsync(boolean async) {
|
||||||
|
this.async = async;
|
||||||
|
}
|
||||||
|
|
||||||
public int[] getTemporaries() {
|
public int[] getTemporaries() {
|
||||||
return maxTemporaryVariableLevel;
|
return maxTemporaryVariableLevel;
|
||||||
}
|
}
|
||||||
|
@ -539,16 +557,31 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(QualificationExpr expr) {
|
public void visit(QualificationExpr expr) {
|
||||||
|
FieldReference field = expr.getField();
|
||||||
|
if (isMonitorField(field)) {
|
||||||
|
String tmp = allocTemporaryVariable(CVariableType.INT);
|
||||||
|
writer.print("(" + tmp + " = FIELD(");
|
||||||
|
expr.getQualified().acceptVisitor(this);
|
||||||
|
field = new FieldReference(RuntimeObject.class.getName(), "hashCode");
|
||||||
|
writer.print(", ").print(names.forClass(field.getClassName()) + ", "
|
||||||
|
+ names.forMemberField(field) + ")");
|
||||||
|
writer.print(", UNPACK_MONITOR(" + tmp + "))");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (expr.getQualified() != null) {
|
if (expr.getQualified() != null) {
|
||||||
writer.print("FIELD(");
|
writer.print("FIELD(");
|
||||||
expr.getQualified().acceptVisitor(this);
|
expr.getQualified().acceptVisitor(this);
|
||||||
writer.print(", ").print(names.forClass(expr.getField().getClassName()) + ", "
|
writer.print(", ").print(names.forClass(field.getClassName()) + ", " + names.forMemberField(field) + ")");
|
||||||
+ names.forMemberField(expr.getField()) + ")");
|
|
||||||
} else {
|
} else {
|
||||||
writer.print(names.forStaticField(expr.getField()));
|
writer.print(names.forStaticField(field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isMonitorField(FieldReference field) {
|
||||||
|
return field.getClassName().equals("java.lang.Object") && field.getFieldName().equals("monitor");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(NewExpr expr) {
|
public void visit(NewExpr expr) {
|
||||||
allocObject(expr.getConstructedClass());
|
allocObject(expr.getConstructedClass());
|
||||||
|
@ -630,11 +663,30 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(AssignmentStatement statement) {
|
public void visit(AssignmentStatement statement) {
|
||||||
if (statement.getLeftValue() != null) {
|
if (statement.getLeftValue() != null) {
|
||||||
|
if (statement.getLeftValue() instanceof QualificationExpr) {
|
||||||
|
QualificationExpr qualification = (QualificationExpr) statement.getLeftValue();
|
||||||
|
FieldReference field = qualification.getField();
|
||||||
|
if (isMonitorField(field)) {
|
||||||
|
writer.print("FIELD(");
|
||||||
|
qualification.getQualified().acceptVisitor(this);
|
||||||
|
field = new FieldReference(RuntimeObject.class.getName(), "hashCode");
|
||||||
|
writer.print(", ").print(names.forClass(field.getClassName()) + ", "
|
||||||
|
+ names.forMemberField(field) + ") = PACK_MONITOR(");
|
||||||
|
statement.getRightValue().acceptVisitor(this);
|
||||||
|
writer.println(");");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
statement.getLeftValue().acceptVisitor(this);
|
statement.getLeftValue().acceptVisitor(this);
|
||||||
writer.print(" = ");
|
writer.print(" = ");
|
||||||
}
|
}
|
||||||
statement.getRightValue().acceptVisitor(this);
|
statement.getRightValue().acceptVisitor(this);
|
||||||
writer.println(";");
|
writer.println(";");
|
||||||
|
|
||||||
|
if (statement.isAsync()) {
|
||||||
|
emitSuspendChecker();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -643,9 +695,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visitMany(List<Statement> statements) {
|
private void visitMany(List<Statement> statements) {
|
||||||
for (Statement statement : statements) {
|
if (statements.isEmpty()) {
|
||||||
statement.acceptVisitor(this);
|
return;
|
||||||
}
|
}
|
||||||
|
boolean oldEnd = end;
|
||||||
|
for (int i = 0; i < statements.size() - 1; ++i) {
|
||||||
|
end = false;
|
||||||
|
statements.get(i).acceptVisitor(this);
|
||||||
|
}
|
||||||
|
end = oldEnd;
|
||||||
|
statements.get(statements.size() - 1).acceptVisitor(this);
|
||||||
|
end = oldEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -688,8 +748,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.indent();
|
writer.indent();
|
||||||
visitMany(clause.getBody());
|
boolean oldEnd = end;
|
||||||
writer.println("break;");
|
for (Statement part : clause.getBody()) {
|
||||||
|
end = false;
|
||||||
|
part.acceptVisitor(this);
|
||||||
|
}
|
||||||
|
end = oldEnd;
|
||||||
writer.outdent();
|
writer.outdent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +780,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
writer.println(") {").indent();
|
writer.println(") {").indent();
|
||||||
|
|
||||||
visitMany(statement.getBody());
|
boolean oldEnd = end;
|
||||||
|
for (Statement part : statement.getBody()) {
|
||||||
|
end = false;
|
||||||
|
part.acceptVisitor(this);
|
||||||
|
}
|
||||||
|
end = oldEnd;
|
||||||
|
|
||||||
if (statement.getId() != null) {
|
if (statement.getId() != null) {
|
||||||
writer.outdent().println("cnt_" + statement.getId() + ":;").indent();
|
writer.outdent().println("cnt_" + statement.getId() + ":;").indent();
|
||||||
|
@ -779,7 +848,6 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(TryCatchStatement statement) {
|
public void visit(TryCatchStatement statement) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -788,10 +856,21 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MonitorEnterStatement statement) {
|
public void visit(MonitorEnterStatement statement) {
|
||||||
|
writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("(");
|
||||||
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
|
writer.println(");");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MonitorExitStatement statement) {
|
public void visit(MonitorExitStatement statement) {
|
||||||
|
writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("(");
|
||||||
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
|
writer.println(");");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void emitSuspendChecker() {
|
||||||
|
String suspendingName = names.forMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class));
|
||||||
|
writer.println("if (" + suspendingName + "(fiber)) goto exit_loop;");
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.backend.c.generate;
|
package org.teavm.backend.c.generate;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.teavm.ast.MethodNode;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.VariableNode;
|
import org.teavm.ast.VariableNode;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
@ -43,16 +44,21 @@ public class CodeGenerator {
|
||||||
writer.print(" {").indent().println();
|
writer.print(" {").indent().println();
|
||||||
|
|
||||||
localsWriter = writer.fragment();
|
localsWriter = writer.fragment();
|
||||||
|
CodeGenerationVisitor visitor = generateMethodBody(methodNode);
|
||||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer, includes);
|
|
||||||
visitor.setCallingMethod(methodNode.getReference());
|
|
||||||
methodNode.getBody().acceptVisitor(visitor);
|
|
||||||
|
|
||||||
generateLocals(methodNode, visitor.getTemporaries());
|
generateLocals(methodNode, visitor.getTemporaries());
|
||||||
|
|
||||||
writer.outdent().println("}");
|
writer.outdent().println("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private CodeGenerationVisitor generateMethodBody(RegularMethodNode methodNode) {
|
||||||
|
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer, includes);
|
||||||
|
visitor.setAsync(context.isAsync(methodNode.getReference()));
|
||||||
|
visitor.setCallingMethod(methodNode.getReference());
|
||||||
|
methodNode.getBody().acceptVisitor(visitor);
|
||||||
|
return visitor;
|
||||||
|
}
|
||||||
|
|
||||||
public void generateMethodSignature(CodeWriter writer, MethodReference methodRef, boolean isStatic,
|
public void generateMethodSignature(CodeWriter writer, MethodReference methodRef, boolean isStatic,
|
||||||
boolean withNames) {
|
boolean withNames) {
|
||||||
writer.print("static ");
|
writer.print("static ");
|
||||||
|
@ -91,7 +97,7 @@ public class CodeGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateLocals(RegularMethodNode methodNode, int[] temporaryCount) {
|
private void generateLocals(MethodNode methodNode, int[] temporaryCount) {
|
||||||
int start = methodNode.getReference().parameterCount() + 1;
|
int start = methodNode.getReference().parameterCount() + 1;
|
||||||
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
||||||
VariableNode variableNode = methodNode.getVariables().get(i);
|
VariableNode variableNode = methodNode.getVariables().get(i);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import org.teavm.backend.c.generators.Generator;
|
import org.teavm.backend.c.generators.Generator;
|
||||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
@ -39,10 +40,12 @@ public class GenerationContext {
|
||||||
private List<Intrinsic> intrinsics;
|
private List<Intrinsic> intrinsics;
|
||||||
private List<Generator> generators;
|
private List<Generator> generators;
|
||||||
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
||||||
|
private Predicate<MethodReference> asyncMethods;
|
||||||
|
|
||||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
||||||
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators) {
|
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
||||||
|
Predicate<MethodReference> asyncMethods) {
|
||||||
this.virtualTableProvider = virtualTableProvider;
|
this.virtualTableProvider = virtualTableProvider;
|
||||||
this.characteristics = characteristics;
|
this.characteristics = characteristics;
|
||||||
this.dependencies = dependencies;
|
this.dependencies = dependencies;
|
||||||
|
@ -52,6 +55,7 @@ public class GenerationContext {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.intrinsics = new ArrayList<>(intrinsics);
|
this.intrinsics = new ArrayList<>(intrinsics);
|
||||||
this.generators = new ArrayList<>(generators);
|
this.generators = new ArrayList<>(generators);
|
||||||
|
this.asyncMethods = asyncMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addIntrinsic(Intrinsic intrinsic) {
|
public void addIntrinsic(Intrinsic intrinsic) {
|
||||||
|
@ -99,4 +103,8 @@ public class GenerationContext {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAsync(MethodReference method) {
|
||||||
|
return asyncMethods.test(method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ public class PlatformIntrinsic implements Intrinsic {
|
||||||
switch (method.getName()) {
|
switch (method.getName()) {
|
||||||
case "getPlatformObject":
|
case "getPlatformObject":
|
||||||
case "asJavaClass":
|
case "asJavaClass":
|
||||||
case "createQueue":
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -43,9 +42,6 @@ public class PlatformIntrinsic implements Intrinsic {
|
||||||
case "asJavaClass":
|
case "asJavaClass":
|
||||||
context.emit(invocation.getArguments().get(0));
|
context.emit(invocation.getArguments().get(0));
|
||||||
break;
|
break;
|
||||||
case "createQueue":
|
|
||||||
context.writer().print("NULL");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,502 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.lowlevel.transform;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.IntIntMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphSplittingBackend;
|
||||||
|
import org.teavm.common.GraphUtils;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchTableEntry;
|
||||||
|
import org.teavm.model.util.BasicBlockMapper;
|
||||||
|
import org.teavm.model.util.BasicBlockSplitter;
|
||||||
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
|
import org.teavm.model.util.LivenessAnalyzer;
|
||||||
|
import org.teavm.model.util.PhiUpdater;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
import org.teavm.model.util.TypeInferer;
|
||||||
|
import org.teavm.model.util.UsageExtractor;
|
||||||
|
import org.teavm.model.util.VariableType;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
|
public class CoroutineTransformation {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private LivenessAnalyzer livenessAnalysis = new LivenessAnalyzer();
|
||||||
|
private TypeInferer variableTypes = new TypeInferer();
|
||||||
|
private Set<MethodReference> asyncMethods;
|
||||||
|
private Program program;
|
||||||
|
private Variable fiberVar;
|
||||||
|
private BasicBlockSplitter splitter;
|
||||||
|
private SwitchInstruction resumeSwitch;
|
||||||
|
private int parameterCount;
|
||||||
|
private ValueType returnType;
|
||||||
|
|
||||||
|
public CoroutineTransformation(ClassReaderSource classSource, Set<MethodReference> asyncMethods) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
this.asyncMethods = asyncMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Program program, MethodReference methodReference) {
|
||||||
|
if (methodReference.getClassName().equals(Fiber.class.getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasJob = false;
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
if (hasSplitInstructions(block)) {
|
||||||
|
hasJob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasJob) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.program = program;
|
||||||
|
parameterCount = methodReference.parameterCount();
|
||||||
|
returnType = methodReference.getReturnType();
|
||||||
|
variableTypes.inferTypes(program, methodReference);
|
||||||
|
livenessAnalysis.analyze(program);
|
||||||
|
splitter = new BasicBlockSplitter(program);
|
||||||
|
int basicBlockCount = program.basicBlockCount();
|
||||||
|
createSplitPrologue();
|
||||||
|
for (int i = 1; i <= basicBlockCount; ++i) {
|
||||||
|
processBlock(program.basicBlockAt(i));
|
||||||
|
}
|
||||||
|
splitter.fixProgram();
|
||||||
|
processIrreducibleCfg();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSplitPrologue() {
|
||||||
|
fiberVar = program.createVariable();
|
||||||
|
fiberVar.setLabel("fiber");
|
||||||
|
|
||||||
|
BasicBlock firstBlock = program.basicBlockAt(0);
|
||||||
|
BasicBlock continueBlock = splitter.split(firstBlock, null);
|
||||||
|
BasicBlock switchStateBlock = program.createBasicBlock();
|
||||||
|
TextLocation location = continueBlock.getFirstInstruction().getLocation();
|
||||||
|
|
||||||
|
InvokeInstruction getFiber = new InvokeInstruction();
|
||||||
|
getFiber.setType(InvocationType.SPECIAL);
|
||||||
|
getFiber.setMethod(new MethodReference(Fiber.class, "current", Fiber.class));
|
||||||
|
getFiber.setReceiver(fiberVar);
|
||||||
|
getFiber.setLocation(location);
|
||||||
|
firstBlock.add(getFiber);
|
||||||
|
|
||||||
|
InvokeInstruction isResuming = new InvokeInstruction();
|
||||||
|
isResuming.setType(InvocationType.SPECIAL);
|
||||||
|
isResuming.setMethod(new MethodReference(Fiber.class, "isResuming", boolean.class));
|
||||||
|
isResuming.setInstance(fiberVar);
|
||||||
|
isResuming.setReceiver(program.createVariable());
|
||||||
|
isResuming.setLocation(location);
|
||||||
|
firstBlock.add(isResuming);
|
||||||
|
|
||||||
|
BranchingInstruction jumpIfResuming = new BranchingInstruction(BranchingCondition.NOT_EQUAL);
|
||||||
|
jumpIfResuming.setOperand(isResuming.getReceiver());
|
||||||
|
jumpIfResuming.setConsequent(switchStateBlock);
|
||||||
|
jumpIfResuming.setAlternative(continueBlock);
|
||||||
|
firstBlock.add(jumpIfResuming);
|
||||||
|
|
||||||
|
InvokeInstruction popInt = new InvokeInstruction();
|
||||||
|
popInt.setType(InvocationType.SPECIAL);
|
||||||
|
popInt.setMethod(new MethodReference(Fiber.class, "popInt", int.class));
|
||||||
|
popInt.setInstance(fiberVar);
|
||||||
|
popInt.setReceiver(program.createVariable());
|
||||||
|
popInt.setLocation(location);
|
||||||
|
switchStateBlock.add(popInt);
|
||||||
|
|
||||||
|
resumeSwitch = new SwitchInstruction();
|
||||||
|
resumeSwitch.setDefaultTarget(continueBlock);
|
||||||
|
resumeSwitch.setCondition(popInt.getReceiver());
|
||||||
|
resumeSwitch.setLocation(location);
|
||||||
|
switchStateBlock.add(resumeSwitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processBlock(BasicBlock block) {
|
||||||
|
Map<Instruction, BitSet> splitInstructions = collectSplitInstructions(block);
|
||||||
|
List<Instruction> instructionList = new ArrayList<>(splitInstructions.keySet());
|
||||||
|
Collections.reverse(instructionList);
|
||||||
|
for (Instruction instruction : instructionList) {
|
||||||
|
BasicBlock intermediate = splitter.split(block, instruction.getPrevious());
|
||||||
|
BasicBlock next = splitter.split(intermediate, instruction);
|
||||||
|
createSplitPoint(block, intermediate, next, splitInstructions.get(instruction));
|
||||||
|
block = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Instruction, BitSet> collectSplitInstructions(BasicBlock block) {
|
||||||
|
if (!hasSplitInstructions(block)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
BitSet live = new BitSet();
|
||||||
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
|
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
||||||
|
for (BasicBlock successor : transitionExtractor.getTargets()) {
|
||||||
|
live.or(livenessAnalysis.liveIn(successor.getIndex()));
|
||||||
|
}
|
||||||
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
|
live.or(livenessAnalysis.liveIn(tryCatch.getHandler().getIndex()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Instruction, BitSet> result = new LinkedHashMap<>();
|
||||||
|
UsageExtractor use = new UsageExtractor();
|
||||||
|
DefinitionExtractor def = new DefinitionExtractor();
|
||||||
|
for (Instruction instruction = block.getLastInstruction(); instruction != null;
|
||||||
|
instruction = instruction.getPrevious()) {
|
||||||
|
instruction.acceptVisitor(def);
|
||||||
|
if (def.getDefinedVariables() != null) {
|
||||||
|
for (Variable var : def.getDefinedVariables()) {
|
||||||
|
live.clear(var.getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instruction.acceptVisitor(use);
|
||||||
|
if (use.getUsedVariables() != null) {
|
||||||
|
for (Variable var : use.getUsedVariables()) {
|
||||||
|
live.set(var.getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSplitInstruction(instruction)) {
|
||||||
|
result.put(instruction, (BitSet) live.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSplitInstructions(BasicBlock block) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (isSplitInstruction(instruction)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSplitInstruction(Instruction instruction) {
|
||||||
|
if (instruction instanceof InvokeInstruction) {
|
||||||
|
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||||
|
MethodReference method = findRealMethod(invoke.getMethod());
|
||||||
|
if (method.getClassName().equals(Fiber.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return asyncMethods.contains(method);
|
||||||
|
} else if (instruction instanceof InitClassInstruction) {
|
||||||
|
return isSplittingClassInitializer(((InitClassInstruction) instruction).getClassName());
|
||||||
|
} else {
|
||||||
|
return instruction instanceof MonitorEnterInstruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSplitPoint(BasicBlock block, BasicBlock intermediate, BasicBlock next, BitSet liveVars) {
|
||||||
|
int stateNumber = resumeSwitch.getEntries().size();
|
||||||
|
Instruction splitInstruction = intermediate.getFirstInstruction();
|
||||||
|
|
||||||
|
JumpInstruction jumpToIntermediate = new JumpInstruction();
|
||||||
|
jumpToIntermediate.setTarget(intermediate);
|
||||||
|
jumpToIntermediate.setLocation(splitInstruction.getLocation());
|
||||||
|
block.add(jumpToIntermediate);
|
||||||
|
|
||||||
|
BasicBlock restoreBlock = program.createBasicBlock();
|
||||||
|
BasicBlock saveBlock = program.createBasicBlock();
|
||||||
|
|
||||||
|
SwitchTableEntry switchTableEntry = new SwitchTableEntry();
|
||||||
|
switchTableEntry.setCondition(stateNumber);
|
||||||
|
switchTableEntry.setTarget(restoreBlock);
|
||||||
|
resumeSwitch.getEntries().add(switchTableEntry);
|
||||||
|
|
||||||
|
InvokeInstruction isSuspending = new InvokeInstruction();
|
||||||
|
isSuspending.setType(InvocationType.SPECIAL);
|
||||||
|
isSuspending.setMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class));
|
||||||
|
isSuspending.setInstance(fiberVar);
|
||||||
|
isSuspending.setReceiver(program.createVariable());
|
||||||
|
isSuspending.setLocation(splitInstruction.getLocation());
|
||||||
|
intermediate.add(isSuspending);
|
||||||
|
|
||||||
|
BranchingInstruction branchIfSuspending = new BranchingInstruction(BranchingCondition.NOT_EQUAL);
|
||||||
|
branchIfSuspending.setOperand(isSuspending.getReceiver());
|
||||||
|
branchIfSuspending.setConsequent(saveBlock);
|
||||||
|
branchIfSuspending.setAlternative(next);
|
||||||
|
branchIfSuspending.setLocation(splitInstruction.getLocation());
|
||||||
|
intermediate.add(branchIfSuspending);
|
||||||
|
|
||||||
|
restoreBlock.addAll(restoreState(liveVars));
|
||||||
|
JumpInstruction doneRestoring = new JumpInstruction();
|
||||||
|
doneRestoring.setTarget(intermediate);
|
||||||
|
restoreBlock.add(doneRestoring);
|
||||||
|
for (Instruction instruction : restoreBlock) {
|
||||||
|
instruction.setLocation(splitInstruction.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Instruction instruction : saveState(liveVars)) {
|
||||||
|
instruction.setLocation(splitInstruction.getLocation());
|
||||||
|
saveBlock.add(instruction);
|
||||||
|
}
|
||||||
|
for (Instruction instruction : saveStateNumber(stateNumber)) {
|
||||||
|
instruction.setLocation(splitInstruction.getLocation());
|
||||||
|
saveBlock.add(instruction);
|
||||||
|
}
|
||||||
|
createReturnInstructions(splitInstruction.getLocation(), saveBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> saveState(BitSet vars) {
|
||||||
|
List<Instruction> instructions = new ArrayList<>();
|
||||||
|
for (int var = vars.nextSetBit(0); var >= 0; var = vars.nextSetBit(var + 1)) {
|
||||||
|
saveVariable(var, instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> saveStateNumber(int number) {
|
||||||
|
IntegerConstantInstruction constant = new IntegerConstantInstruction();
|
||||||
|
constant.setReceiver(program.createVariable());
|
||||||
|
constant.setConstant(number);
|
||||||
|
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", int.class, void.class));
|
||||||
|
invoke.setInstance(fiberVar);
|
||||||
|
invoke.setArguments(constant.getReceiver());
|
||||||
|
|
||||||
|
return Arrays.asList(constant, invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> restoreState(BitSet vars) {
|
||||||
|
List<Instruction> instructions = new ArrayList<>();
|
||||||
|
int[] varArray = new int[vars.cardinality()];
|
||||||
|
int j = 0;
|
||||||
|
for (int i = vars.nextSetBit(0); i >= 0; i = vars.nextSetBit(i + 1)) {
|
||||||
|
varArray[j++] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = varArray.length - 1; i >= 0; --i) {
|
||||||
|
restoreVariable(varArray[i], instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveVariable(int var, List<Instruction> instructions) {
|
||||||
|
VariableType type = variableTypes.typeOf(var);
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setInstance(fiberVar);
|
||||||
|
invoke.setArguments(program.variableAt(var));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case INT:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", int.class, void.class));
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", long.class, void.class));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", float.class, void.class));
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", double.class, void.class));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "push", Object.class, void.class));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.add(invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreVariable(int var, List<Instruction> instructions) {
|
||||||
|
VariableType type = variableTypes.typeOf(var);
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setInstance(fiberVar);
|
||||||
|
invoke.setReceiver(program.variableAt(var));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case INT:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "popInt", int.class));
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "popLong", long.class));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "popFloat", float.class));
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "popDouble", double.class));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class, "popObject", Object.class));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.add(invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSplittingClassInitializer(String className) {
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReader method = cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID));
|
||||||
|
return method != null && asyncMethods.contains(method.getReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodReference findRealMethod(MethodReference method) {
|
||||||
|
String clsName = method.getClassName();
|
||||||
|
while (clsName != null) {
|
||||||
|
ClassReader cls = classSource.get(clsName);
|
||||||
|
if (cls == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MethodReader methodReader = cls.getMethod(method.getDescriptor());
|
||||||
|
if (methodReader != null) {
|
||||||
|
return new MethodReference(clsName, method.getDescriptor());
|
||||||
|
}
|
||||||
|
clsName = cls.getParent();
|
||||||
|
if (clsName != null && clsName.equals(cls.getName())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createReturnInstructions(TextLocation location, BasicBlock block) {
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
exit.setLocation(location);
|
||||||
|
if (returnType == ValueType.VOID) {
|
||||||
|
block.add(exit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exit.setValueToReturn(program.createVariable());
|
||||||
|
Instruction returnValue = createReturnValueInstruction(exit.getValueToReturn());
|
||||||
|
returnValue.setLocation(location);
|
||||||
|
block.add(returnValue);
|
||||||
|
block.add(exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instruction createReturnValueInstruction(Variable target) {
|
||||||
|
if (returnType instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) returnType).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
case CHARACTER:
|
||||||
|
case SHORT:
|
||||||
|
case INTEGER: {
|
||||||
|
IntegerConstantInstruction instruction = new IntegerConstantInstruction();
|
||||||
|
instruction.setReceiver(target);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
case LONG: {
|
||||||
|
LongConstantInstruction instruction = new LongConstantInstruction();
|
||||||
|
instruction.setReceiver(target);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
case FLOAT: {
|
||||||
|
FloatConstantInstruction instruction = new FloatConstantInstruction();
|
||||||
|
instruction.setReceiver(target);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
case DOUBLE: {
|
||||||
|
DoubleConstantInstruction instruction = new DoubleConstantInstruction();
|
||||||
|
instruction.setReceiver(target);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NullConstantInstruction instruction = new NullConstantInstruction();
|
||||||
|
instruction.setReceiver(target);
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processIrreducibleCfg() {
|
||||||
|
Graph graph = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
if (!GraphUtils.isIrreducible(graph)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SplittingBackend splittingBackend = new SplittingBackend();
|
||||||
|
int[] weights = new int[graph.size()];
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
weights[i] = program.basicBlockAt(i).instructionCount();
|
||||||
|
}
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, splittingBackend);
|
||||||
|
new PhiUpdater().updatePhis(program, parameterCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SplittingBackend implements GraphSplittingBackend {
|
||||||
|
@Override
|
||||||
|
public int[] split(int[] domain, int[] nodes) {
|
||||||
|
int[] copies = new int[nodes.length];
|
||||||
|
IntIntMap map = new IntIntHashMap();
|
||||||
|
for (int i = 0; i < nodes.length; ++i) {
|
||||||
|
int node = nodes[i];
|
||||||
|
BasicBlock block = program.basicBlockAt(node);
|
||||||
|
BasicBlock blockCopy = program.createBasicBlock();
|
||||||
|
ProgramUtils.copyBasicBlock(block, blockCopy);
|
||||||
|
copies[i] = blockCopy.getIndex();
|
||||||
|
map.put(nodes[i], copies[i] + 1);
|
||||||
|
}
|
||||||
|
BasicBlockMapper copyBlockMapper = new BasicBlockMapper((int block) -> {
|
||||||
|
int mappedIndex = map.get(block);
|
||||||
|
return mappedIndex == 0 ? block : mappedIndex - 1;
|
||||||
|
});
|
||||||
|
for (int copy : copies) {
|
||||||
|
copyBlockMapper.transform(program.basicBlockAt(copy));
|
||||||
|
}
|
||||||
|
for (int domainNode : domain) {
|
||||||
|
copyBlockMapper.transform(program.basicBlockAt(domainNode));
|
||||||
|
}
|
||||||
|
return copies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,6 +51,8 @@ import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.instructions.LongConstantInstruction;
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.instructions.RaiseInstruction;
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
import org.teavm.model.instructions.SwitchInstruction;
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
|
@ -258,7 +260,8 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
private boolean isCallInstruction(Instruction insn) {
|
private boolean isCallInstruction(Instruction insn) {
|
||||||
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
||||||
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||||
|
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction) {
|
||||||
return true;
|
return true;
|
||||||
} else if (insn instanceof InvokeInstruction) {
|
} else if (insn instanceof InvokeInstruction) {
|
||||||
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
||||||
|
|
|
@ -50,6 +50,8 @@ import org.teavm.model.instructions.InitClassInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||||
import org.teavm.model.instructions.RaiseInstruction;
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
import org.teavm.model.util.DefinitionExtractor;
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
import org.teavm.model.util.GraphColorer;
|
import org.teavm.model.util.GraphColorer;
|
||||||
|
@ -155,7 +157,8 @@ public class GCShadowStackContributor {
|
||||||
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|
||||||
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|
||||||
|| insn instanceof ConstructMultiArrayInstruction
|
|| insn instanceof ConstructMultiArrayInstruction
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||||
|
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction) {
|
||||||
if (insn instanceof InvokeInstruction
|
if (insn instanceof InvokeInstruction
|
||||||
&& !characteristics.isManaged(((InvokeInstruction) insn).getMethod())) {
|
&& !characteristics.isManaged(((InvokeInstruction) insn).getMethod())) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ProgramReader;
|
import org.teavm.model.ProgramReader;
|
||||||
import org.teavm.model.VariableReader;
|
import org.teavm.model.VariableReader;
|
||||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
public class AsyncMethodFinder {
|
public class AsyncMethodFinder {
|
||||||
private Set<MethodReference> asyncMethods = new HashSet<>();
|
private Set<MethodReference> asyncMethods = new HashSet<>();
|
||||||
|
@ -142,6 +143,10 @@ public class AsyncMethodFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(MethodReference methodRef, CallStack stack) {
|
private void add(MethodReference methodRef, CallStack stack) {
|
||||||
|
if (methodRef.getClassName().equals(Fiber.class.getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!asyncMethods.add(methodRef)) {
|
if (!asyncMethods.add(methodRef)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class BasicBlockSplitter {
|
||||||
public BasicBlock split(BasicBlock block, Instruction afterInstruction) {
|
public BasicBlock split(BasicBlock block, Instruction afterInstruction) {
|
||||||
initIfNecessary();
|
initIfNecessary();
|
||||||
|
|
||||||
if (afterInstruction.getBasicBlock() != block) {
|
if (afterInstruction != null && afterInstruction.getBasicBlock() != block) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,10 +88,18 @@ public class BasicBlockSplitter {
|
||||||
isLastInSequence.add((byte) 1);
|
isLastInSequence.add((byte) 1);
|
||||||
|
|
||||||
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
while (afterInstruction.getNext() != null) {
|
if (afterInstruction != null) {
|
||||||
Instruction nextInstruction = afterInstruction.getNext();
|
while (afterInstruction.getNext() != null) {
|
||||||
nextInstruction.delete();
|
Instruction nextInstruction = afterInstruction.getNext();
|
||||||
splitBlock.add(nextInstruction);
|
nextInstruction.delete();
|
||||||
|
splitBlock.add(nextInstruction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (block.getFirstInstruction() != null) {
|
||||||
|
Instruction instruction = block.getFirstInstruction();
|
||||||
|
instruction.delete();
|
||||||
|
splitBlock.add(instruction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return splitBlock;
|
return splitBlock;
|
||||||
|
|
147
core/src/main/java/org/teavm/runtime/EventQueue.java
Normal file
147
core/src/main/java/org/teavm/runtime/EventQueue.java
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.runtime;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.interop.Import;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
|
||||||
|
@StaticInit
|
||||||
|
public final class EventQueue {
|
||||||
|
private static Node[] data = new Node[16];
|
||||||
|
private static int size;
|
||||||
|
private static boolean finished;
|
||||||
|
private static int idGenerator;
|
||||||
|
|
||||||
|
private EventQueue() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int offer(Event action) {
|
||||||
|
return offer(action, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int offer(Event action, long time) {
|
||||||
|
ensureCapacity(size + 1);
|
||||||
|
int current = size;
|
||||||
|
while (current > 0) {
|
||||||
|
int parent = (current - 1) / 2;
|
||||||
|
if (time < data[parent].time) {
|
||||||
|
data[current] = data[parent];
|
||||||
|
current = parent;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int id = idGenerator++;
|
||||||
|
data[current] = new Node(id, action, time);
|
||||||
|
++size;
|
||||||
|
if (current == 0) {
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void kill(int id) {
|
||||||
|
for (int i = 0; i < data.length; ++i) {
|
||||||
|
if (data[i].id == id) {
|
||||||
|
remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void process() {
|
||||||
|
while (!finished) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop() {
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void next() {
|
||||||
|
while (data.length == 0) {
|
||||||
|
waitUntil(System.currentTimeMillis() + 1000);
|
||||||
|
}
|
||||||
|
Node node = data[0];
|
||||||
|
waitUntil(node.time);
|
||||||
|
if (node.time <= System.currentTimeMillis()) {
|
||||||
|
remove(0);
|
||||||
|
node.event.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void remove(int index) {
|
||||||
|
Node item = data[size - 1];
|
||||||
|
while (true) {
|
||||||
|
int left = index * 2 + 1;
|
||||||
|
int right = left + 1;
|
||||||
|
int next;
|
||||||
|
if (left >= size) {
|
||||||
|
break;
|
||||||
|
} else if (right >= size || data[left].time < data[right].time) {
|
||||||
|
next = left;
|
||||||
|
} else {
|
||||||
|
next = right;
|
||||||
|
}
|
||||||
|
if (item.time <= data[next].time) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data[index] = data[next];
|
||||||
|
index = next;
|
||||||
|
}
|
||||||
|
data[index] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void waitUntil(long time) {
|
||||||
|
long diff = time - System.currentTimeMillis();
|
||||||
|
if (diff <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
waitFor(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Import(name = "teavm_waitFor")
|
||||||
|
private static native void waitFor(long time);
|
||||||
|
|
||||||
|
@Import(name = "teavm_interrupt")
|
||||||
|
private static native void interrupt();
|
||||||
|
|
||||||
|
private static void ensureCapacity(int capacity) {
|
||||||
|
if (data.length >= capacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
capacity = Math.max(capacity, data.length * 3 / 2);
|
||||||
|
data = Arrays.copyOf(data, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Node {
|
||||||
|
final int id;
|
||||||
|
final Event event;
|
||||||
|
final long time;
|
||||||
|
|
||||||
|
Node(int id, Event event, long time) {
|
||||||
|
this.id = id;
|
||||||
|
this.event = event;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Event {
|
||||||
|
void run();
|
||||||
|
}
|
||||||
|
}
|
243
core/src/main/java/org/teavm/runtime/Fiber.java
Normal file
243
core/src/main/java/org/teavm/runtime/Fiber.java
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.runtime;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
|
@StaticInit
|
||||||
|
public class Fiber {
|
||||||
|
public static final int STATE_RUNNING = 0;
|
||||||
|
public static final int STATE_SUSPENDING = 1;
|
||||||
|
public static final int STATE_RESUMING = 2;
|
||||||
|
|
||||||
|
private int[] intValues;
|
||||||
|
private int intTop;
|
||||||
|
private long[] longValues;
|
||||||
|
private int longTop;
|
||||||
|
private float[] floatValues;
|
||||||
|
private int floatTop;
|
||||||
|
private double[] doubleValues;
|
||||||
|
private int doubleTop;
|
||||||
|
private Object[] objectValues;
|
||||||
|
private int objectTop;
|
||||||
|
private int state;
|
||||||
|
private FiberRunner runner;
|
||||||
|
private Object result;
|
||||||
|
private Throwable exception;
|
||||||
|
|
||||||
|
private static Fiber current;
|
||||||
|
|
||||||
|
private Fiber(FiberRunner runner) {
|
||||||
|
this.runner = runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(int value) {
|
||||||
|
if (intValues == null) {
|
||||||
|
intValues = new int[4];
|
||||||
|
} else if (intTop + 1 == intValues.length) {
|
||||||
|
intValues = Arrays.copyOf(intValues, intValues.length * 3 / 2);
|
||||||
|
}
|
||||||
|
intValues[intTop++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(long value) {
|
||||||
|
if (longValues == null) {
|
||||||
|
longValues = new long[4];
|
||||||
|
} else if (longTop + 1 == longValues.length) {
|
||||||
|
longValues = Arrays.copyOf(longValues, longValues.length * 3 / 2);
|
||||||
|
}
|
||||||
|
longValues[longTop++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(float value) {
|
||||||
|
if (floatValues == null) {
|
||||||
|
floatValues = new float[4];
|
||||||
|
} else if (floatTop + 1 == floatValues.length) {
|
||||||
|
floatValues = Arrays.copyOf(floatValues, floatValues.length * 3 / 2);
|
||||||
|
}
|
||||||
|
floatValues[floatTop++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(double value) {
|
||||||
|
if (doubleValues == null) {
|
||||||
|
doubleValues = new double[4];
|
||||||
|
} else if (doubleTop + 4 == doubleValues.length) {
|
||||||
|
doubleValues = Arrays.copyOf(doubleValues, doubleValues.length * 3 / 2);
|
||||||
|
}
|
||||||
|
doubleValues[doubleTop++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(Object value) {
|
||||||
|
if (objectValues == null) {
|
||||||
|
objectValues = new Object[4];
|
||||||
|
} else if (objectTop + 4 == objectValues.length) {
|
||||||
|
objectValues = Arrays.copyOf(objectValues, objectValues.length * 3 / 2);
|
||||||
|
}
|
||||||
|
objectValues[objectTop++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public int popInt() {
|
||||||
|
return intValues[--intTop];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public long popLong() {
|
||||||
|
return longValues[--longTop];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public float popFloat() {
|
||||||
|
return floatValues[--floatTop];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public double popDouble() {
|
||||||
|
return doubleValues[--doubleTop];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public Object popObject() {
|
||||||
|
Object result = objectValues[--objectTop];
|
||||||
|
objectValues[objectTop] = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static Fiber current() {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public boolean isSuspending() {
|
||||||
|
return state == STATE_SUSPENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public boolean isResuming() {
|
||||||
|
return state == STATE_RESUMING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static boolean getBoolean(Object v) {
|
||||||
|
return v != null ? (Boolean) v : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static byte getByte(Object v) {
|
||||||
|
return v != null ? (Byte) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static short getShort(Object v) {
|
||||||
|
return v != null ? (Short) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static int getInt(Object v) {
|
||||||
|
return v != null ? (Integer) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static char getChar(Object v) {
|
||||||
|
return v != null ? (Character) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static long getLong(Object v) {
|
||||||
|
return v != null ? (Long) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static float getFloat(Object v) {
|
||||||
|
return v != null ? (Float) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static double getDouble(Object v) {
|
||||||
|
return v != null ? (Double) v : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object suspend(AsyncCall call) throws Throwable {
|
||||||
|
Fiber fiber = current();
|
||||||
|
Thread javaThread = Thread.currentThread();
|
||||||
|
if (fiber.isResuming()) {
|
||||||
|
fiber.state = STATE_RUNNING;
|
||||||
|
if (fiber.exception != null) {
|
||||||
|
throw fiber.exception;
|
||||||
|
}
|
||||||
|
return fiber.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fiber.state = STATE_SUSPENDING;
|
||||||
|
call.run(new AsyncCallback<Object>() {
|
||||||
|
@Override
|
||||||
|
public void complete(Object result) {
|
||||||
|
setCurrentThread(javaThread);
|
||||||
|
fiber.result = result;
|
||||||
|
fiber.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Throwable e) {
|
||||||
|
setCurrentThread(javaThread);
|
||||||
|
fiber.exception = e;
|
||||||
|
fiber.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static native void setCurrentThread(Thread thread);
|
||||||
|
|
||||||
|
public static void start(FiberRunner runner) {
|
||||||
|
new Fiber(runner).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void startMain() {
|
||||||
|
start(() -> {
|
||||||
|
runMain();
|
||||||
|
if (!current().isSuspending()) {
|
||||||
|
EventQueue.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static native void runMain();
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
Fiber former = current;
|
||||||
|
current = this;
|
||||||
|
runner.run();
|
||||||
|
current = former;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resume() {
|
||||||
|
state = STATE_RESUMING;
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FiberRunner {
|
||||||
|
void run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AsyncCall {
|
||||||
|
void run(AsyncCallback<?> callback);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -377,6 +378,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||||
|
dependencyAnalyzer.getClassSource(),
|
||||||
|
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||||
|
|
||||||
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
||||||
ListableClassHolderSource classSet;
|
ListableClassHolderSource classSet;
|
||||||
if (isLazy) {
|
if (isLazy) {
|
||||||
|
@ -869,4 +874,24 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return classNames;
|
return classNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ListableClassReaderSourceAdapter implements ListableClassReaderSource {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private Set<String> classes;
|
||||||
|
|
||||||
|
ListableClassReaderSourceAdapter(ClassReaderSource classSource, Set<String> classes) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
this.classes = Collections.unmodifiableSet(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getClassNames() {
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassReader get(String name) {
|
||||||
|
return classes.contains(name) ? classSource.get(name) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.vm.spi.TeaVMHostExtension;
|
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
@ -38,6 +39,9 @@ public interface TeaVMTarget {
|
||||||
|
|
||||||
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);
|
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);
|
||||||
|
|
||||||
|
default void analyzeBeforeOptimizations(ListableClassReaderSource classSource) {
|
||||||
|
}
|
||||||
|
|
||||||
void beforeOptimizations(Program program, MethodReader method);
|
void beforeOptimizations(Program program, MethodReader method);
|
||||||
|
|
||||||
void afterOptimizations(Program program, MethodReader method);
|
void afterOptimizations(Program program, MethodReader method);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <stdalign.h>
|
#include <stdalign.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -29,6 +30,8 @@ typedef struct JavaArray JavaArray;
|
||||||
typedef struct JavaClass JavaClass;
|
typedef struct JavaClass JavaClass;
|
||||||
typedef struct JavaString JavaString;
|
typedef struct JavaString JavaString;
|
||||||
|
|
||||||
|
static void* gc_heapAddress = NULL;
|
||||||
|
|
||||||
#define PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - TeaVM_beforeClasses) >> 3))
|
#define PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - TeaVM_beforeClasses) >> 3))
|
||||||
#define UNPACK_CLASS(cls) ((JavaClass*) (TeaVM_beforeClasses + ((cls) << 3)))
|
#define UNPACK_CLASS(cls) ((JavaClass*) (TeaVM_beforeClasses + ((cls) << 3)))
|
||||||
#define CLASS_OF(obj) (UNPACK_CLASS(((JavaObject*) (obj))->header))
|
#define CLASS_OF(obj) (UNPACK_CLASS(((JavaObject*) (obj))->header))
|
||||||
|
@ -42,6 +45,11 @@ typedef struct JavaString JavaString;
|
||||||
#define TO_SHORT(i) ((((i) << 16) >> 16))
|
#define TO_SHORT(i) ((((i) << 16) >> 16))
|
||||||
#define TO_CHAR(i) ((char16_t) (i))
|
#define TO_CHAR(i) ((char16_t) (i))
|
||||||
|
|
||||||
|
#define PACK_MONITOR(ref) (((int32_t) ((uintptr_t) (ref) - (uintptr_t) gc_heapAddress) / sizeof(int)) | 0x80000000)
|
||||||
|
#define UNPACK_MONITOR(ref) ((ref & 0x80000000) != 0 \
|
||||||
|
? (void*) (((uintptr_t) ((ref & 0x7FFFFFFF) * sizeof(int)) + (uintptr_t) gc_heapAddress)) \
|
||||||
|
: NULL)
|
||||||
|
|
||||||
static inline int32_t compare_i32(int32_t a, int32_t b) {
|
static inline int32_t compare_i32(int32_t a, int32_t b) {
|
||||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||||
}
|
}
|
||||||
|
@ -100,7 +108,6 @@ static void** stackTop;
|
||||||
|
|
||||||
static void* gc_gcStorageAddress = NULL;
|
static void* gc_gcStorageAddress = NULL;
|
||||||
static int32_t gc_gcStorageSize = INT32_C(0);
|
static int32_t gc_gcStorageSize = INT32_C(0);
|
||||||
static void* gc_heapAddress = NULL;
|
|
||||||
static void* gc_regionsAddress = NULL;
|
static void* gc_regionsAddress = NULL;
|
||||||
static int32_t gc_regionSize = INT32_C(32768);
|
static int32_t gc_regionSize = INT32_C(32768);
|
||||||
static int32_t gc_regionMaxCount = INT32_C(0);
|
static int32_t gc_regionMaxCount = INT32_C(0);
|
||||||
|
@ -156,8 +163,31 @@ static inline void* teavm_lookupResourceValue(TeaVM_ResourceMap *map, JavaString
|
||||||
|
|
||||||
static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *);
|
static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
static timer_t teavm_queueTimer;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void TeaVM_beforeInit() {
|
static void TeaVM_beforeInit() {
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
struct sigaction sigact;
|
||||||
|
sigact.sa_flags = 0;
|
||||||
|
sigact.sa_handler = NULL;
|
||||||
|
sigaction(SIGRTMIN, &sigact, NULL);
|
||||||
|
|
||||||
|
sigset_t signals;
|
||||||
|
sigemptyset(&signals );
|
||||||
|
sigaddset(&signals, SIGRTMIN);
|
||||||
|
sigprocmask(SIG_BLOCK, &signals, NULL);
|
||||||
|
|
||||||
|
struct sigevent sev;
|
||||||
|
sev.sigev_notify = SIGEV_SIGNAL;
|
||||||
|
sev.sigev_signo = SIGRTMIN;
|
||||||
|
timer_create(CLOCK_REALTIME, &sev, &teavm_queueTimer);
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
|
@ -294,4 +324,27 @@ static inline float teavm_reinterpretIntToFloat(int32_t v) {
|
||||||
union { int32_t intValue; float floatValue; } conv;
|
union { int32_t intValue; float floatValue; } conv;
|
||||||
conv.intValue = v;
|
conv.intValue = v;
|
||||||
return conv.floatValue;
|
return conv.floatValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
|
||||||
|
static void teavm_waitFor(int64_t timeout) {
|
||||||
|
struct itimerspec its = {0};
|
||||||
|
its.it_value.tv_sec = timeout / 1000;
|
||||||
|
its.it_value.tv_nsec = (timeout % 1000) * 1000000L;
|
||||||
|
timer_settime(teavm_queueTimer, 0, &its, NULL);
|
||||||
|
|
||||||
|
sigset_t signals;
|
||||||
|
sigemptyset(&signals);
|
||||||
|
sigaddset(&signals, SIGRTMIN);
|
||||||
|
siginfo_t actualSignal;
|
||||||
|
sigwaitinfo(&signals, &actualSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void teavm_interrupt() {
|
||||||
|
struct itimerspec its = {0};
|
||||||
|
timer_settime(teavm_queueTimer, 0, &its, NULL);
|
||||||
|
raise(SIGRTMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,13 +13,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.platform.async;
|
package org.teavm.interop;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
* @param <T>
|
|
||||||
*/
|
|
||||||
public interface AsyncCallback<T> {
|
public interface AsyncCallback<T> {
|
||||||
void complete(T result);
|
void complete(T result);
|
||||||
|
|
47
platform/src/main/java/org/teavm/platform/LowLevelQueue.java
Normal file
47
platform/src/main/java/org/teavm/platform/LowLevelQueue.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 konsoletyper.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.platform;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
public class LowLevelQueue<T> extends PlatformQueue<T> {
|
||||||
|
Queue<T> q = new ArrayDeque<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return q.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void push(PlatformObject obj) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
PlatformObject shift() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T e) {
|
||||||
|
q.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T remove() {
|
||||||
|
return q.remove();
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
|
import org.teavm.interop.PlatformMarker;
|
||||||
|
import org.teavm.interop.PlatformMarkers;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
@ -193,7 +195,16 @@ public final class Platform {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(script = "return [];")
|
@JSBody(script = "return [];")
|
||||||
public static native <T> PlatformQueue<T> createQueue();
|
public static <T> PlatformQueue<T> createQueue() {
|
||||||
|
if (isLowLevel()) {
|
||||||
|
return new LowLevelQueue<>();
|
||||||
|
} else {
|
||||||
|
return createQueueJs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(script = "return [];")
|
||||||
|
private static native <T> PlatformQueue<T> createQueueJs();
|
||||||
|
|
||||||
public static PlatformString stringFromCharCode(int charCode) {
|
public static PlatformString stringFromCharCode(int charCode) {
|
||||||
return JSString.fromCharCode(charCode).cast();
|
return JSString.fromCharCode(charCode).cast();
|
||||||
|
@ -230,4 +241,9 @@ public final class Platform {
|
||||||
public static String getName(PlatformClass cls) {
|
public static String getName(PlatformClass cls) {
|
||||||
return cls.getMetadata().getName();
|
return cls.getMetadata().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PlatformMarker(PlatformMarkers.LOW_LEVEL)
|
||||||
|
private static boolean isLowLevel() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,11 @@ public abstract class PlatformQueue<T> implements JSObject {
|
||||||
|
|
||||||
abstract PlatformObject shift();
|
abstract PlatformObject shift();
|
||||||
|
|
||||||
public final void add(T e) {
|
public void add(T e) {
|
||||||
push(wrap(e));
|
push(wrap(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final T remove() {
|
public T remove() {
|
||||||
return unwrap(shift());
|
return unwrap(shift());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
|
@interface AsyncCallClass {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.interop.AsyncCallback;
|
||||||
|
|
||||||
class AsyncCallbackWrapper<T> implements AsyncCallback<T> {
|
class AsyncCallbackWrapper<T> implements AsyncCallback<T> {
|
||||||
private AsyncCallback<T> realAsyncCallback;
|
private AsyncCallback<T> realAsyncCallback;
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldHolder;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
|
public class AsyncLowLevelDependencyListener extends AbstractDependencyListener {
|
||||||
|
private Set<String> generatedClassNames = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (method.getMethod() != null && method.getMethod().getAnnotations().get(Async.class.getName()) != null) {
|
||||||
|
ClassHolder cls = generateCall(method.getMethod());
|
||||||
|
if (cls != null) {
|
||||||
|
agent.submitClass(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassHolder generateCall(MethodReader method) {
|
||||||
|
ClassHolder cls = generateClassDecl(method);
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cls.addMethod(generateConstructor(method, cls.getName()));
|
||||||
|
cls.addMethod(generateRun(method, cls.getName()));
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassHolder generateClassDecl(MethodReader method) {
|
||||||
|
AnnotationReader annot = method.getAnnotations().get(AsyncCallClass.class.getName());
|
||||||
|
String className = annot.getValue("value").getString();
|
||||||
|
if (!generatedClassNames.add(className)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ClassHolder cls = new ClassHolder(className);
|
||||||
|
|
||||||
|
cls.getInterfaces().add(Fiber.class.getName() + "$AsyncCall");
|
||||||
|
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
types.add(ValueType.object(method.getOwnerName()));
|
||||||
|
FieldHolder field = new FieldHolder("instance");
|
||||||
|
field.setType(ValueType.object(method.getOwnerName()));
|
||||||
|
cls.addField(field);
|
||||||
|
}
|
||||||
|
ValueType[] parameterTypes = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
types.add(parameterTypes[i]);
|
||||||
|
FieldHolder field = new FieldHolder("param" + i);
|
||||||
|
field.setType(parameterTypes[i]);
|
||||||
|
cls.addField(field);
|
||||||
|
}
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
||||||
|
cls.addMethod(constructor);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHolder generateConstructor(MethodReader method, String className) {
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
types.add(ValueType.object(method.getOwnerName()));
|
||||||
|
}
|
||||||
|
Collections.addAll(types, method.getParameterTypes());
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
||||||
|
|
||||||
|
Program program = new Program();
|
||||||
|
constructor.setProgram(program);
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
Variable instance = program.createVariable();
|
||||||
|
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
PutFieldInstruction putField = new PutFieldInstruction();
|
||||||
|
putField.setValue(program.createVariable());
|
||||||
|
putField.setField(new FieldReference(className, "instance"));
|
||||||
|
putField.setFieldType(ValueType.object(method.getOwnerName()));
|
||||||
|
putField.setInstance(instance);
|
||||||
|
block.add(putField);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
PutFieldInstruction putField = new PutFieldInstruction();
|
||||||
|
putField.setValue(program.createVariable());
|
||||||
|
putField.setField(new FieldReference(className, "param" + i));
|
||||||
|
putField.setFieldType(method.parameterType(i));
|
||||||
|
putField.setInstance(instance);
|
||||||
|
block.add(putField);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.add(new ExitInstruction());
|
||||||
|
return constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHolder generateRun(MethodReader method, String className) {
|
||||||
|
MethodHolder runMethod = new MethodHolder("run", ValueType.parse(AsyncCallback.class), ValueType.VOID);
|
||||||
|
Program program = new Program();
|
||||||
|
runMethod.setProgram(program);
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
Variable instance = program.createVariable();
|
||||||
|
Variable callback = program.createVariable();
|
||||||
|
|
||||||
|
InvokeInstruction call = new InvokeInstruction();
|
||||||
|
call.setType(InvocationType.SPECIAL);
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
ValueType[] parameterTypes = method.getParameterTypes();
|
||||||
|
List<Variable> arguments = new ArrayList<>(call.getArguments());
|
||||||
|
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
GetFieldInstruction getField = new GetFieldInstruction();
|
||||||
|
getField.setReceiver(program.createVariable());
|
||||||
|
getField.setInstance(instance);
|
||||||
|
getField.setField(new FieldReference(className, "instance"));
|
||||||
|
getField.setFieldType(ValueType.object(method.getOwnerName()));
|
||||||
|
block.add(getField);
|
||||||
|
call.setInstance(getField.getReceiver());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
GetFieldInstruction getField = new GetFieldInstruction();
|
||||||
|
getField.setReceiver(program.createVariable());
|
||||||
|
getField.setInstance(instance);
|
||||||
|
getField.setField(new FieldReference(className, "param" + i));
|
||||||
|
getField.setFieldType(parameterTypes[i]);
|
||||||
|
block.add(getField);
|
||||||
|
arguments.add(getField.getReceiver());
|
||||||
|
types.add(parameterTypes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
types.add(ValueType.parse(AsyncCallback.class));
|
||||||
|
arguments.add(callback);
|
||||||
|
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
call.setMethod(new MethodReference(method.getOwnerName(), method.getName(), types.toArray(new ValueType[0])));
|
||||||
|
call.setArguments(arguments.toArray(new Variable[0]));
|
||||||
|
block.add(call);
|
||||||
|
|
||||||
|
if (method.getResultType() == ValueType.VOID) {
|
||||||
|
block.add(new ExitInstruction());
|
||||||
|
} else {
|
||||||
|
Variable result = program.createVariable();
|
||||||
|
call.setReceiver(result);
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
exit.setValueToReturn(castToObject(block, result, method.getResultType()));
|
||||||
|
block.add(exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Variable castToObject(BasicBlock block, Variable value, ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setArguments(value);
|
||||||
|
invoke.setReceiver(block.getProgram().createVariable());
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
invoke.setMethod(new MethodReference(Boolean.class, "valueOf", boolean.class, Boolean.class));
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
invoke.setMethod(new MethodReference(Byte.class, "valueOf", byte.class, Byte.class));
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
invoke.setMethod(new MethodReference(Short.class, "valueOf", short.class, Short.class));
|
||||||
|
break;
|
||||||
|
case CHARACTER:
|
||||||
|
invoke.setMethod(new MethodReference(Character.class, "valueOf", char.class, Character.class));
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
invoke.setMethod(new MethodReference(Integer.class, "valueOf", int.class, Integer.class));
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
invoke.setMethod(new MethodReference(Long.class, "valueOf", long.class, Long.class));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
invoke.setMethod(new MethodReference(Float.class, "valueOf", float.class, Float.class));
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
invoke.setMethod(new MethodReference(Double.class, "valueOf", double.class, Double.class));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
block.add(invoke);
|
||||||
|
return invoke.getReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,13 +24,13 @@ import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
|
||||||
|
|
||||||
public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor {
|
public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor {
|
||||||
private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class);
|
private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class);
|
||||||
|
|
|
@ -15,8 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
import org.teavm.model.AnnotationValue;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
@ -24,12 +30,27 @@ import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
public class AsyncMethodProcessor implements ClassHolderTransformer {
|
public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
|
private boolean lowLevel;
|
||||||
|
|
||||||
|
public AsyncMethodProcessor(boolean lowLevel) {
|
||||||
|
this.lowLevel = lowLevel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
|
int suffix = 0;
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.hasModifier(ElementModifier.NATIVE)
|
if (method.hasModifier(ElementModifier.NATIVE)
|
||||||
&& method.getAnnotations().get(Async.class.getName()) != null
|
&& method.getAnnotations().get(Async.class.getName()) != null
|
||||||
|
@ -50,7 +71,112 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
method.getReference(), asyncMethod.getReference());
|
method.getReference(), asyncMethod.getReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lowLevel) {
|
||||||
|
generateLowLevelCall(method, suffix++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateLowLevelCall(MethodHolder method, int suffix) {
|
||||||
|
String className = method.getOwnerName() + "$" + method.getName() + "$" + suffix;
|
||||||
|
AnnotationHolder classNameAnnot = new AnnotationHolder(AsyncCallClass.class.getName());
|
||||||
|
classNameAnnot.getValues().put("value", new AnnotationValue(className));
|
||||||
|
method.getAnnotations().add(classNameAnnot);
|
||||||
|
|
||||||
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
|
|
||||||
|
Program program = new Program();
|
||||||
|
method.setProgram(program);
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
|
||||||
|
InvokeInstruction constructorInvocation = new InvokeInstruction();
|
||||||
|
constructorInvocation.setType(InvocationType.SPECIAL);
|
||||||
|
List<ValueType> signature = new ArrayList<>();
|
||||||
|
|
||||||
|
Variable instanceVar = program.createVariable();
|
||||||
|
List<Variable> arguments = new ArrayList<>(constructorInvocation.getArguments());
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
arguments.add(instanceVar);
|
||||||
|
signature.add(ValueType.object(method.getOwnerName()));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
arguments.add(program.createVariable());
|
||||||
|
signature.add(method.parameterType(i));
|
||||||
|
}
|
||||||
|
signature.add(ValueType.VOID);
|
||||||
|
|
||||||
|
ConstructInstruction newInstruction = new ConstructInstruction();
|
||||||
|
newInstruction.setReceiver(program.createVariable());
|
||||||
|
newInstruction.setType(className);
|
||||||
|
block.add(newInstruction);
|
||||||
|
|
||||||
|
constructorInvocation.setInstance(newInstruction.getReceiver());
|
||||||
|
constructorInvocation.setMethod(new MethodReference(className, "<init>", signature.toArray(new ValueType[0])));
|
||||||
|
constructorInvocation.setArguments(arguments.toArray(new Variable[0]));
|
||||||
|
block.add(constructorInvocation);
|
||||||
|
|
||||||
|
InvokeInstruction suspendInvocation = new InvokeInstruction();
|
||||||
|
suspendInvocation.setType(InvocationType.SPECIAL);
|
||||||
|
suspendInvocation.setMethod(new MethodReference(Fiber.class, "suspend", Fiber.AsyncCall.class, Object.class));
|
||||||
|
suspendInvocation.setArguments(newInstruction.getReceiver());
|
||||||
|
suspendInvocation.setReceiver(program.createVariable());
|
||||||
|
block.add(suspendInvocation);
|
||||||
|
|
||||||
|
Variable result = suspendInvocation.getReceiver();
|
||||||
|
ExitInstruction exitInstruction = new ExitInstruction();
|
||||||
|
ValueType returnType = method.getResultType();
|
||||||
|
if (returnType instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) returnType).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
result = castPrimitive(block, result, "Boolean", returnType);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
result = castPrimitive(block, result, "Byte", returnType);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
result = castPrimitive(block, result, "Short", returnType);
|
||||||
|
break;
|
||||||
|
case CHARACTER:
|
||||||
|
result = castPrimitive(block, result, "Char", returnType);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
result = castPrimitive(block, result, "Int", returnType);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
result = castPrimitive(block, result, "Float", returnType);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
result = castPrimitive(block, result, "Long", returnType);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
result = castPrimitive(block, result, "Double", returnType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (returnType == ValueType.VOID) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
CastInstruction cast = new CastInstruction();
|
||||||
|
cast.setValue(result);
|
||||||
|
cast.setTargetType(returnType);
|
||||||
|
cast.setReceiver(program.createVariable());
|
||||||
|
block.add(cast);
|
||||||
|
result = cast.getReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
exitInstruction.setValueToReturn(result);
|
||||||
|
block.add(exitInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Variable castPrimitive(BasicBlock block, Variable value, String name, ValueType type) {
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.setMethod(new MethodReference(Fiber.class.getName(), "get" + name,
|
||||||
|
ValueType.object("java.lang.Object"), type));
|
||||||
|
invoke.setArguments(value);
|
||||||
|
invoke.setReceiver(block.getProgram().createVariable());
|
||||||
|
block.add(invoke);
|
||||||
|
return invoke.getReceiver();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class PlatformPlugin implements TeaVMPlugin {
|
||||||
return method.getAnnotations().get(Async.class.getName()) != null
|
return method.getAnnotations().get(Async.class.getName()) != null
|
||||||
? new AsyncMethodGenerator() : null;
|
? new AsyncMethodGenerator() : null;
|
||||||
});
|
});
|
||||||
|
host.add(new AsyncDependencyListener());
|
||||||
jsHost.addVirtualMethods(new AsyncMethodGenerator());
|
jsHost.addVirtualMethods(new AsyncMethodGenerator());
|
||||||
} else if (!isBootstrap()) {
|
} else if (!isBootstrap()) {
|
||||||
host.add(new StringAmplifierTransformer());
|
host.add(new StringAmplifierTransformer());
|
||||||
|
@ -101,12 +102,15 @@ public class PlatformPlugin implements TeaVMPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
host.add(new AsyncMethodProcessor());
|
host.add(new AsyncMethodProcessor(host.getExtension(TeaVMJavaScriptHost.class) == null));
|
||||||
host.add(new NewInstanceDependencySupport());
|
host.add(new NewInstanceDependencySupport());
|
||||||
host.add(new ClassLookupDependencySupport());
|
host.add(new ClassLookupDependencySupport());
|
||||||
host.add(new EnumDependencySupport());
|
host.add(new EnumDependencySupport());
|
||||||
host.add(new PlatformDependencyListener());
|
host.add(new PlatformDependencyListener());
|
||||||
host.add(new AsyncDependencyListener());
|
|
||||||
|
if (host.getExtension(TeaVMJavaScriptHost.class) == null) {
|
||||||
|
host.add(new AsyncLowLevelDependencyListener());
|
||||||
|
}
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
||||||
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
||||||
|
|
|
@ -23,10 +23,10 @@ import java.util.function.Supplier;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.junit.SkipJVM;
|
import org.teavm.junit.SkipJVM;
|
||||||
import org.teavm.junit.TeaVMTestRunner;
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
|
||||||
|
|
||||||
@RunWith(TeaVMTestRunner.class)
|
@RunWith(TeaVMTestRunner.class)
|
||||||
public class VMTest {
|
public class VMTest {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user