mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Fix monitor methods. Improve JSO to handle abstract classes
This commit is contained in:
parent
9d112817b8
commit
b6cb9bfd4a
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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.classlib.java.lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public class TIllegalMonitorStateException extends TRuntimeException {
|
||||||
|
private static final long serialVersionUID = 7694307746228488658L;
|
||||||
|
|
||||||
|
public TIllegalMonitorStateException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TIllegalMonitorStateException(TString message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,14 +16,13 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import org.teavm.dom.browser.TimerHandler;
|
import org.teavm.dom.browser.TimerHandler;
|
||||||
import org.teavm.dom.browser.Window;
|
|
||||||
import org.teavm.javascript.spi.Async;
|
import org.teavm.javascript.spi.Async;
|
||||||
import org.teavm.javascript.spi.Rename;
|
import org.teavm.javascript.spi.Rename;
|
||||||
import org.teavm.javascript.spi.Superclass;
|
import org.teavm.javascript.spi.Superclass;
|
||||||
import org.teavm.jso.JS;
|
import org.teavm.javascript.spi.Sync;
|
||||||
import org.teavm.jso.JSArray;
|
|
||||||
import org.teavm.jso.JSObject;
|
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
|
import org.teavm.platform.PlatformQueue;
|
||||||
|
import org.teavm.platform.PlatformRunnable;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.platform.async.AsyncCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,51 +31,97 @@ import org.teavm.platform.async.AsyncCallback;
|
||||||
*/
|
*/
|
||||||
@Superclass("")
|
@Superclass("")
|
||||||
public class TObject {
|
public class TObject {
|
||||||
private static final Window window = (Window)JS.getGlobal();
|
Monitor monitor;
|
||||||
private JSArray<NotifyListener> notifyListeners;
|
|
||||||
private Lock lock;
|
|
||||||
|
|
||||||
private static class Lock {
|
static class Monitor {
|
||||||
|
PlatformQueue<PlatformRunnable> enteringThreads;
|
||||||
|
PlatformQueue<NotifyListener> notifyListeners;
|
||||||
TThread owner;
|
TThread owner;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
public Lock() {
|
public Monitor() {
|
||||||
this.owner = TThread.currentThread();
|
this.owner = TThread.currentThread();
|
||||||
count = 1;
|
enteringThreads = Platform.createQueue();
|
||||||
|
notifyListeners = Platform.createQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotifyListener extends JSObject {
|
interface NotifyListener extends PlatformRunnable {
|
||||||
boolean handleNotify();
|
boolean expired();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitorEnter(TObject o) {
|
static void monitorEnter(TObject o) {
|
||||||
if (o.lock == null) {
|
monitorEnter(o, 1);
|
||||||
o.lock = new Lock();
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
static native void monitorEnter(TObject o, int count);
|
||||||
|
|
||||||
|
static void monitorEnter(final TObject o, final int count, final AsyncCallback<Void> callback) {
|
||||||
|
if (o.monitor == null) {
|
||||||
|
o.monitor = new Monitor();
|
||||||
|
}
|
||||||
|
if (o.monitor.owner == null) {
|
||||||
|
o.monitor.owner = TThread.currentThread();
|
||||||
|
}
|
||||||
|
if (o.monitor.owner != TThread.currentThread()) {
|
||||||
|
final TThread thread = TThread.currentThread();
|
||||||
|
o.monitor.enteringThreads.add(new PlatformRunnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
TThread.setCurrentThread(thread);
|
||||||
|
o.monitor.owner = thread;
|
||||||
|
o.monitor.count += count;
|
||||||
|
callback.complete(null);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
o.monitor.count += count;
|
||||||
|
callback.complete(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sync
|
||||||
|
static void monitorExit(final TObject o) {
|
||||||
|
monitorExit(o, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Sync
|
||||||
|
static void monitorExit(final TObject o, int count) {
|
||||||
|
if (o.isEmptyMonitor() || o.monitor.owner != TThread.currentThread()) {
|
||||||
|
throw new TIllegalMonitorStateException();
|
||||||
|
}
|
||||||
|
o.monitor.count -= count;
|
||||||
|
if (o.monitor.count > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (o.lock.owner != TThread.currentThread()) {
|
|
||||||
while (o.lock != null) {
|
o.monitor.owner = null;
|
||||||
try {
|
Platform.startThread(new PlatformRunnable() {
|
||||||
o.lock.wait();
|
@Override public void run() {
|
||||||
} catch (InterruptedException ex) {
|
if (o.isEmptyMonitor() || o.monitor.owner != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!o.monitor.enteringThreads.isEmpty()) {
|
||||||
|
o.monitor.enteringThreads.remove().run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.lock = new Lock();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEmptyMonitor() {
|
||||||
|
if (monitor == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (monitor.owner == null && monitor.enteringThreads.isEmpty() && monitor.notifyListeners.isEmpty()) {
|
||||||
|
monitor = null;
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
o.lock.count++;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitorExit(TObject o){
|
static boolean holdsLock(TObject o) {
|
||||||
if (o.lock != null && o.lock.count-- == 0) {
|
return o.monitor != null && o.monitor.owner == TThread.currentThread();
|
||||||
o.lock.notifyAll();
|
|
||||||
o.lock = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean holdsLock(TObject o){
|
|
||||||
return o.lock != null && o.lock.owner == TThread.currentThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rename("fakeInit")
|
@Rename("fakeInit")
|
||||||
|
@ -123,30 +168,35 @@ public class TObject {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sync
|
||||||
@Rename("notify")
|
@Rename("notify")
|
||||||
public final void notify0() {
|
public final void notify0() {
|
||||||
|
if (!holdsLock(this)) {
|
||||||
|
throw new TIllegalMonitorStateException();
|
||||||
|
}
|
||||||
TThread thread = TThread.currentThread();
|
TThread thread = TThread.currentThread();
|
||||||
if (notifyListeners != null) {
|
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||||
while (notifyListeners.getLength() > 0 && notifyListeners.shift().handleNotify()) {
|
while (!listeners.isEmpty()) {
|
||||||
// repeat loop
|
NotifyListener listener = listeners.remove();
|
||||||
}
|
if (!listener.expired()) {
|
||||||
if (notifyListeners.getLength() == 0) {
|
Platform.startThread(listener);
|
||||||
notifyListeners = null;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TThread.setCurrentThread(thread);
|
TThread.setCurrentThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sync
|
||||||
@Rename("notifyAll")
|
@Rename("notifyAll")
|
||||||
public final void notifyAll0(){
|
public final void notifyAll0() {
|
||||||
if (notifyListeners != null){
|
if (!holdsLock(this)) {
|
||||||
JSArray<NotifyListener> listeners = window.newArray();
|
throw new TIllegalMonitorStateException();
|
||||||
while (notifyListeners.getLength() > 0) {
|
}
|
||||||
listeners.push(notifyListeners.shift());
|
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||||
}
|
while (!listeners.isEmpty()) {
|
||||||
notifyListeners = null;
|
NotifyListener listener = listeners.remove();
|
||||||
while (listeners.getLength() > 0) {
|
if (!listener.expired()) {
|
||||||
listeners.shift().handleNotify();
|
Platform.startThread(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,49 +216,51 @@ public class TObject {
|
||||||
|
|
||||||
@Rename("wait")
|
@Rename("wait")
|
||||||
public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) {
|
public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) {
|
||||||
if (notifyListeners == null) {
|
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
|
||||||
notifyListeners = window.newArray();
|
monitor.notifyListeners.add(listener);
|
||||||
|
if (timeout > 0 || nanos > 0) {
|
||||||
|
listener.timerId = Platform.schedule(listener, timeout >= Integer.MAX_VALUE ? Integer.MAX_VALUE :
|
||||||
|
(int)timeout);
|
||||||
}
|
}
|
||||||
final NotifyListenerImpl listener = new NotifyListenerImpl(callback);
|
monitorExit(this, monitor.count);
|
||||||
notifyListeners.push(listener);
|
|
||||||
if (timeout == 0 && nanos == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
listener.timerId = window.setTimeout(listener, timeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NotifyListenerImpl implements NotifyListener, TimerHandler {
|
private static class NotifyListenerImpl implements NotifyListener, TimerHandler, PlatformRunnable {
|
||||||
|
final TObject obj;
|
||||||
final AsyncCallback<Void> callback;
|
final AsyncCallback<Void> callback;
|
||||||
final TThread currentThread = TThread.currentThread();
|
final TThread currentThread = TThread.currentThread();
|
||||||
int timerId = -1;
|
int timerId = -1;
|
||||||
boolean finished;
|
boolean expired;
|
||||||
|
int lockCount;
|
||||||
|
|
||||||
public NotifyListenerImpl(AsyncCallback<Void> callback) {
|
public NotifyListenerImpl(TObject obj, AsyncCallback<Void> callback, int lockCount) {
|
||||||
|
this.obj = obj;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.lockCount = lockCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleNotify() {
|
public boolean expired() {
|
||||||
if (finished) {
|
boolean result = expired;
|
||||||
return false;
|
expired = true;
|
||||||
}
|
return result;
|
||||||
TThread.setCurrentThread(currentThread);
|
|
||||||
if (timerId >= 0) {
|
|
||||||
window.clearTimeout(timerId);
|
|
||||||
timerId = -1;
|
|
||||||
}
|
|
||||||
finished = true;
|
|
||||||
try {
|
|
||||||
callback.complete(null);
|
|
||||||
} finally {
|
|
||||||
TThread.setCurrentThread(TThread.getMainThread());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimer() {
|
public void onTimer() {
|
||||||
handleNotify();
|
if (!expired()) {
|
||||||
|
Platform.startThread(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (timerId >= 0) {
|
||||||
|
Platform.killSchedule(timerId);
|
||||||
|
timerId = -1;
|
||||||
|
}
|
||||||
|
TThread.setCurrentThread(currentThread);
|
||||||
|
monitorEnter(obj, lockCount, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.teavm.dom.browser.Window;
|
||||||
import org.teavm.javascript.spi.Async;
|
import org.teavm.javascript.spi.Async;
|
||||||
import org.teavm.jso.JS;
|
import org.teavm.jso.JS;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
|
import org.teavm.platform.PlatformRunnable;
|
||||||
import org.teavm.platform.async.AsyncCallback;
|
import org.teavm.platform.async.AsyncCallback;
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ public class TThread extends TObject implements TRunnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(){
|
public void start(){
|
||||||
Platform.startThread(new Runnable() {
|
Platform.startThread(new PlatformRunnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -98,14 +99,7 @@ public class TThread extends TObject implements TRunnable {
|
||||||
public static native void yield();
|
public static native void yield();
|
||||||
|
|
||||||
private static void yield(final AsyncCallback<Void> callback) {
|
private static void yield(final AsyncCallback<Void> callback) {
|
||||||
final TThread current = currentThread();
|
callback.complete(null);
|
||||||
window.setTimeout(new TimerHandler() {
|
|
||||||
@Override public void onTimer() {
|
|
||||||
setCurrentThread(current);
|
|
||||||
callback.complete(null);
|
|
||||||
setCurrentThread(mainThread);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
|
@ -140,7 +134,6 @@ public class TThread extends TObject implements TRunnable {
|
||||||
@Override public void onTimer() {
|
@Override public void onTimer() {
|
||||||
setCurrentThread(current);
|
setCurrentThread(current);
|
||||||
callback.complete(null);
|
callback.complete(null);
|
||||||
setCurrentThread(mainThread);
|
|
||||||
}
|
}
|
||||||
}, millis);
|
}, millis);
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,6 +205,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
||||||
renderRuntimeObjcls();
|
renderRuntimeObjcls();
|
||||||
renderRuntimeNullCheck();
|
renderRuntimeNullCheck();
|
||||||
renderRuntimeIntern();
|
renderRuntimeIntern();
|
||||||
|
renderRuntimeThreads();
|
||||||
} catch (NamingException e) {
|
} catch (NamingException e) {
|
||||||
throw new RenderingException("Error rendering runtime methods. See a cause for details", e);
|
throw new RenderingException("Error rendering runtime methods. See a cause for details", e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -270,6 +271,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
||||||
writer.append("function $rt_objcls() { return ").appendClass("java.lang.Object").append("; }").newLine();
|
writer.append("function $rt_objcls() { return ").appendClass("java.lang.Object").append("; }").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renderRuntimeThreads() throws IOException {
|
||||||
|
writer.append("function $rt_getThread()").ws().append("{").indent().softNewLine();
|
||||||
|
writer.append("return ").appendMethodBody(Thread.class, "currentThread", Thread.class).append("();")
|
||||||
|
.softNewLine();
|
||||||
|
writer.outdent().append("}").newLine();
|
||||||
|
|
||||||
|
writer.append("function $rt_setThread(t)").ws().append("{").indent().softNewLine();
|
||||||
|
writer.append("return ").appendMethodBody(Thread.class, "setCurrentThread", Thread.class, void.class)
|
||||||
|
.append("(t);").softNewLine();
|
||||||
|
writer.outdent().append("}").newLine();
|
||||||
|
}
|
||||||
|
|
||||||
public void render(List<ClassNode> classes) throws RenderingException {
|
public void render(List<ClassNode> classes) throws RenderingException {
|
||||||
for (ClassNode cls : classes) {
|
for (ClassNode cls : classes) {
|
||||||
renderDeclaration(cls);
|
renderDeclaration(cls);
|
||||||
|
|
|
@ -15,10 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.javascript.spi;
|
package org.teavm.javascript.spi;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.*;
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -26,5 +23,6 @@ import java.lang.annotation.Target;
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
|
@Inherited
|
||||||
public @interface Sync {
|
public @interface Sync {
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ public class AsyncProgramSplitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void split(Program program) {
|
public void split(Program program) {
|
||||||
|
System.out.println("Splitting program");
|
||||||
|
System.out.println(new ListingBuilder().buildListing(program, " "));
|
||||||
|
|
||||||
parts.clear();
|
parts.clear();
|
||||||
Program initialProgram = createStubCopy(program);
|
Program initialProgram = createStubCopy(program);
|
||||||
Part initialPart = new Part();
|
Part initialPart = new Part();
|
||||||
|
@ -145,6 +148,13 @@ public class AsyncProgramSplitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.size(); ++i) {
|
||||||
|
Program part = parts.get(i).program;
|
||||||
|
System.out.println("Part " + i);
|
||||||
|
System.out.println(new ListingBuilder().buildListing(part, " "));
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
partMap.clear();
|
partMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -464,8 +464,9 @@ function $rt_asyncAdapter(f) {
|
||||||
var result;
|
var result;
|
||||||
var args = Array.prototype.slice.apply(arguments);
|
var args = Array.prototype.slice.apply(arguments);
|
||||||
var $return = args.pop();
|
var $return = args.pop();
|
||||||
|
args.unshift(this);
|
||||||
try {
|
try {
|
||||||
result = f.apply(this, args);
|
result = f.apply(null, args);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return $return($rt_asyncError(e));
|
return $return($rt_asyncError(e));
|
||||||
}
|
}
|
||||||
|
@ -510,7 +511,9 @@ function $rt_continue(f) {
|
||||||
return function() {
|
return function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
|
var thread = $rt_getThread();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
$rt_setThread(thread);
|
||||||
f.apply(self, args);
|
f.apply(self, args);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
|
@ -132,7 +132,7 @@ class JSODependencyListener implements DependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodAchieved(DependencyAgent agent, MethodDependency methodDep, CallLocation location) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,9 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
processor = new JavascriptNativeProcessor(innerSource);
|
processor = new JavascriptNativeProcessor(innerSource);
|
||||||
processor.setDiagnostics(diagnostics);
|
processor.setDiagnostics(diagnostics);
|
||||||
processor.processClass(cls);
|
processor.processClass(cls);
|
||||||
|
if (processor.isNative(cls.getName())) {
|
||||||
|
processor.processFinalMethods(cls);
|
||||||
|
}
|
||||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||||
processor.processJSBody(cls, method);
|
processor.processJSBody(cls, method);
|
||||||
|
|
|
@ -21,6 +21,8 @@ import org.teavm.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.jso.*;
|
import org.teavm.jso.*;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.instructions.*;
|
||||||
|
import org.teavm.model.util.InstructionVariableMapper;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -39,6 +41,10 @@ class JavascriptNativeProcessor {
|
||||||
nativeRepos = new NativeJavascriptClassRepository(classSource);
|
nativeRepos = new NativeJavascriptClassRepository(classSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNative(String className) {
|
||||||
|
return nativeRepos.isJavaScriptClass(className);
|
||||||
|
}
|
||||||
|
|
||||||
public void setDiagnostics(Diagnostics diagnostics) {
|
public void setDiagnostics(Diagnostics diagnostics) {
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +68,47 @@ class JavascriptNativeProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void processFinalMethods(ClassHolder cls) {
|
||||||
|
// TODO: don't allow final methods to override anything
|
||||||
|
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||||
|
if (method.hasModifier(ElementModifier.FINAL) && method.getProgram() != null) {
|
||||||
|
ValueType[] staticSignature = getStaticSignature(method.getReference());
|
||||||
|
MethodHolder callerMethod = new MethodHolder(new MethodDescriptor(method.getName() + "$static",
|
||||||
|
staticSignature));
|
||||||
|
callerMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
|
final Program program = ProgramUtils.copy(method.getProgram());
|
||||||
|
program.createVariable();
|
||||||
|
InstructionVariableMapper variableMapper = new InstructionVariableMapper() {
|
||||||
|
@Override protected Variable map(Variable var) {
|
||||||
|
return program.variableAt(var.getIndex() + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (int i = program.variableCount() - 1; i > 0; --i) {
|
||||||
|
program.variableAt(i).getDebugNames().addAll(program.variableAt(i - 1).getDebugNames());
|
||||||
|
program.variableAt(i - 1).getDebugNames().clear();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
for (Instruction insn : block.getInstructions()) {
|
||||||
|
insn.acceptVisitor(variableMapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callerMethod.setProgram(program);
|
||||||
|
cls.addMethod(callerMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueType[] getStaticSignature(MethodReference method) {
|
||||||
|
ValueType[] signature = method.getSignature();
|
||||||
|
ValueType[] staticSignature = new ValueType[signature.length + 1];
|
||||||
|
for (int i = 0; i < signature.length; ++i) {
|
||||||
|
staticSignature[i + 1] = signature[i];
|
||||||
|
}
|
||||||
|
staticSignature[0] = ValueType.object(method.getClassName());
|
||||||
|
return staticSignature;
|
||||||
|
}
|
||||||
|
|
||||||
public void processProgram(MethodHolder methodToProcess) {
|
public void processProgram(MethodHolder methodToProcess) {
|
||||||
program = methodToProcess.getProgram();
|
program = methodToProcess.getProgram();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
@ -78,6 +125,17 @@ class JavascriptNativeProcessor {
|
||||||
}
|
}
|
||||||
replacement.clear();
|
replacement.clear();
|
||||||
MethodReader method = getMethod(invoke.getMethod());
|
MethodReader method = getMethod(invoke.getMethod());
|
||||||
|
if (method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (method.hasModifier(ElementModifier.FINAL)) {
|
||||||
|
invoke.setMethod(new MethodReference(method.getOwnerName(), method.getName() + "$static",
|
||||||
|
getStaticSignature(method.getReference())));
|
||||||
|
invoke.setType(InvocationType.SPECIAL);
|
||||||
|
invoke.getArguments().add(0, invoke.getInstance());
|
||||||
|
invoke.setInstance(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
||||||
if (method.getAnnotations().get(JSProperty.class.getName()) != null) {
|
if (method.getAnnotations().get(JSProperty.class.getName()) != null) {
|
||||||
if (isProperGetter(method.getDescriptor())) {
|
if (isProperGetter(method.getDescriptor())) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ class NativeJavascriptClassRepository {
|
||||||
|
|
||||||
private boolean figureOutIfJavaScriptClass(String className) {
|
private boolean figureOutIfJavaScriptClass(String className) {
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
if (cls == null || !cls.hasModifier(ElementModifier.INTERFACE)) {
|
if (cls == null || !(cls.hasModifier(ElementModifier.INTERFACE) || cls.hasModifier(ElementModifier.ABSTRACT))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
|
|
|
@ -94,12 +94,24 @@ public final class Platform {
|
||||||
|
|
||||||
@GeneratedBy(PlatformGenerator.class)
|
@GeneratedBy(PlatformGenerator.class)
|
||||||
@PluggableDependency(PlatformGenerator.class)
|
@PluggableDependency(PlatformGenerator.class)
|
||||||
public static native void startThread(Runnable runnable);
|
public static native void startThread(PlatformRunnable runnable);
|
||||||
|
|
||||||
private static void launchThread(Runnable runnable) {
|
private static void launchThread(PlatformRunnable runnable) {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GeneratedBy(PlatformGenerator.class)
|
||||||
|
@PluggableDependency(PlatformGenerator.class)
|
||||||
|
public static native int schedule(PlatformRunnable runnable, int timeout);
|
||||||
|
|
||||||
|
public static void killSchedule(int id) {
|
||||||
|
((PlatformHelper)JS.getGlobal()).killSchedule(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> PlatformQueue<T> createQueue() {
|
||||||
|
return ((PlatformHelper)JS.getGlobal()).newQueue();
|
||||||
|
}
|
||||||
|
|
||||||
public static PlatformString stringFromCharCode(int charCode) {
|
public static PlatformString stringFromCharCode(int charCode) {
|
||||||
return ((PlatformHelper)JS.getGlobal()).getStringClass().fromCharCode(charCode);
|
return ((PlatformHelper)JS.getGlobal()).getStringClass().fromCharCode(charCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.platform;
|
package org.teavm.platform;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSConstructor;
|
||||||
import org.teavm.jso.JSMethod;
|
import org.teavm.jso.JSMethod;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
|
@ -29,4 +30,10 @@ interface PlatformHelper extends JSObject {
|
||||||
|
|
||||||
@JSProperty("String")
|
@JSProperty("String")
|
||||||
PlatformStringClass getStringClass();
|
PlatformStringClass getStringClass();
|
||||||
|
|
||||||
|
@JSConstructor("Array")
|
||||||
|
<T> PlatformQueue<T> newQueue();
|
||||||
|
|
||||||
|
@JSMethod("clearTimeout")
|
||||||
|
void killSchedule(int scheduleId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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;
|
||||||
|
|
||||||
|
import org.teavm.dependency.PluggableDependency;
|
||||||
|
import org.teavm.javascript.spi.InjectedBy;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSProperty;
|
||||||
|
import org.teavm.platform.plugin.PlatformQueueGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public abstract class PlatformQueue<T> implements JSObject {
|
||||||
|
@JSProperty
|
||||||
|
public abstract int getLength();
|
||||||
|
|
||||||
|
public final boolean isEmpty() {
|
||||||
|
return getLength() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void push(PlatformObject obj);
|
||||||
|
|
||||||
|
abstract PlatformObject shift();
|
||||||
|
|
||||||
|
public final void add(T e) {
|
||||||
|
push(wrap(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final T remove() {
|
||||||
|
return unwrap(shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PlatformObject wrap(Object obj) {
|
||||||
|
return Platform.getPlatformObject(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectedBy(PlatformQueueGenerator.class)
|
||||||
|
@PluggableDependency(PlatformQueueGenerator.class)
|
||||||
|
private static native <S> S unwrap(PlatformObject obj);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public interface PlatformRunnable {
|
||||||
|
void run();
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.teavm.javascript.spi.Injector;
|
||||||
import org.teavm.javascript.spi.InjectorContext;
|
import org.teavm.javascript.spi.InjectorContext;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
|
import org.teavm.platform.PlatformRunnable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -41,9 +42,10 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
||||||
case "clone":
|
case "clone":
|
||||||
method.getVariable(1).connect(method.getResult());
|
method.getVariable(1).connect(method.getResult());
|
||||||
break;
|
break;
|
||||||
case "startThread": {
|
case "startThread":
|
||||||
|
case "schedule": {
|
||||||
MethodDependency launchMethod = agent.linkMethod(new MethodReference(Platform.class,
|
MethodDependency launchMethod = agent.linkMethod(new MethodReference(Platform.class,
|
||||||
"launchThread", Runnable.class, void.class), null);
|
"launchThread", PlatformRunnable.class, void.class), null);
|
||||||
method.getVariable(1).connect(launchMethod.getVariable(1));
|
method.getVariable(1).connect(launchMethod.getVariable(1));
|
||||||
launchMethod.use();
|
launchMethod.use();
|
||||||
break;
|
break;
|
||||||
|
@ -78,7 +80,10 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
||||||
generateClone(context, writer);
|
generateClone(context, writer);
|
||||||
break;
|
break;
|
||||||
case "startThread":
|
case "startThread":
|
||||||
generateStartThread(context, writer);
|
generateSchedule(context, writer, false);
|
||||||
|
break;
|
||||||
|
case "schedule":
|
||||||
|
generateSchedule(context, writer, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,11 +134,12 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
||||||
writer.append("return copy;").softNewLine();
|
writer.append("return copy;").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateStartThread(GeneratorContext context, SourceWriter writer) throws IOException {
|
private void generateSchedule(GeneratorContext context, SourceWriter writer, boolean timeout) throws IOException {
|
||||||
String runnable = context.getParameterName(1);
|
String runnable = context.getParameterName(1);
|
||||||
writer.append("window.setTimeout(function()").ws().append("{").indent().softNewLine();
|
writer.append("return window.setTimeout(function()").ws().append("{").indent().softNewLine();
|
||||||
writer.append("$rt_rootInvocationAdapter(").appendMethodBody(Platform.class, "launchThread", Runnable.class,
|
writer.append("$rt_rootInvocationAdapter(").appendMethodBody(Platform.class, "launchThread",
|
||||||
void.class).append(")(").append(runnable).append(");").softNewLine();
|
PlatformRunnable.class, void.class).append(")(").append(runnable).append(");").softNewLine();
|
||||||
writer.outdent().append("},").ws().append("0);").softNewLine();
|
writer.outdent().append("},").ws().append(timeout ? context.getParameterName(2) : "0")
|
||||||
|
.append(");").softNewLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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.io.IOException;
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.javascript.spi.Injector;
|
||||||
|
import org.teavm.javascript.spi.InjectorContext;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.platform.PlatformObject;
|
||||||
|
import org.teavm.platform.PlatformQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public class PlatformQueueGenerator implements Injector, DependencyPlugin {
|
||||||
|
@Override
|
||||||
|
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||||
|
MethodDependency addMethod = agent.linkMethod(new MethodReference(PlatformQueue.class, "wrap",
|
||||||
|
Object.class, PlatformObject.class), null);
|
||||||
|
addMethod.getVariable(1).connect(method.getResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ public final class AsyncProgram {
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException {
|
public static void main(String[] args) throws InterruptedException {
|
||||||
report(Arrays.toString(args));
|
report(Arrays.toString(args));
|
||||||
|
findPrimes();
|
||||||
withoutAsync();
|
withoutAsync();
|
||||||
report("");
|
report("");
|
||||||
withAsync();
|
withAsync();
|
||||||
|
@ -64,11 +65,36 @@ public final class AsyncProgram {
|
||||||
report("Now trying wait...");
|
report("Now trying wait...");
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
lock.wait(20000);
|
lock.wait(20000);
|
||||||
}
|
}
|
||||||
report("Finished main thread");
|
report("Finished main thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void findPrimes() {
|
||||||
|
report("Finding primes");
|
||||||
|
boolean[] prime = new boolean[1000];
|
||||||
|
prime[2] = true;
|
||||||
|
prime[3] = true;
|
||||||
|
nextPrime: for (int i = 5; i < prime.length; i += 2) {
|
||||||
|
int maxPrime = (int)Math.sqrt(i);
|
||||||
|
for (int j = 3; j <= maxPrime; j += 2) {
|
||||||
|
Thread.yield();
|
||||||
|
if (prime[j] && i % j == 0) {
|
||||||
|
continue nextPrime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prime[i] = true;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
if (prime[i]) {
|
||||||
|
sb.append(i).append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
private static void report(String message) {
|
private static void report(String message) {
|
||||||
long current = System.currentTimeMillis() - start;
|
long current = System.currentTimeMillis() - start;
|
||||||
System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
|
System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user