mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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;
|
||||
|
||||
import org.teavm.dom.browser.TimerHandler;
|
||||
import org.teavm.dom.browser.Window;
|
||||
import org.teavm.javascript.spi.Async;
|
||||
import org.teavm.javascript.spi.Rename;
|
||||
import org.teavm.javascript.spi.Superclass;
|
||||
import org.teavm.jso.JS;
|
||||
import org.teavm.jso.JSArray;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.javascript.spi.Sync;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformQueue;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
import org.teavm.platform.async.AsyncCallback;
|
||||
|
||||
/**
|
||||
|
@ -32,51 +31,97 @@ import org.teavm.platform.async.AsyncCallback;
|
|||
*/
|
||||
@Superclass("")
|
||||
public class TObject {
|
||||
private static final Window window = (Window)JS.getGlobal();
|
||||
private JSArray<NotifyListener> notifyListeners;
|
||||
private Lock lock;
|
||||
Monitor monitor;
|
||||
|
||||
private static class Lock {
|
||||
static class Monitor {
|
||||
PlatformQueue<PlatformRunnable> enteringThreads;
|
||||
PlatformQueue<NotifyListener> notifyListeners;
|
||||
TThread owner;
|
||||
int count;
|
||||
|
||||
public Lock() {
|
||||
public Monitor() {
|
||||
this.owner = TThread.currentThread();
|
||||
count = 1;
|
||||
enteringThreads = Platform.createQueue();
|
||||
notifyListeners = Platform.createQueue();
|
||||
}
|
||||
}
|
||||
|
||||
interface NotifyListener extends JSObject {
|
||||
boolean handleNotify();
|
||||
interface NotifyListener extends PlatformRunnable {
|
||||
boolean expired();
|
||||
}
|
||||
|
||||
static void monitorEnter(TObject o) {
|
||||
if (o.lock == null) {
|
||||
o.lock = new Lock();
|
||||
monitorEnter(o, 1);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
if (o.lock.owner != TThread.currentThread()) {
|
||||
while (o.lock != null) {
|
||||
try {
|
||||
o.lock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
|
||||
o.monitor.owner = null;
|
||||
Platform.startThread(new PlatformRunnable() {
|
||||
@Override public void run() {
|
||||
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 {
|
||||
o.lock.count++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void monitorExit(TObject o){
|
||||
if (o.lock != null && o.lock.count-- == 0) {
|
||||
o.lock.notifyAll();
|
||||
o.lock = null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean holdsLock(TObject o){
|
||||
return o.lock != null && o.lock.owner == TThread.currentThread();
|
||||
static boolean holdsLock(TObject o) {
|
||||
return o.monitor != null && o.monitor.owner == TThread.currentThread();
|
||||
}
|
||||
|
||||
@Rename("fakeInit")
|
||||
|
@ -123,30 +168,35 @@ public class TObject {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Sync
|
||||
@Rename("notify")
|
||||
public final void notify0() {
|
||||
if (!holdsLock(this)) {
|
||||
throw new TIllegalMonitorStateException();
|
||||
}
|
||||
TThread thread = TThread.currentThread();
|
||||
if (notifyListeners != null) {
|
||||
while (notifyListeners.getLength() > 0 && notifyListeners.shift().handleNotify()) {
|
||||
// repeat loop
|
||||
}
|
||||
if (notifyListeners.getLength() == 0) {
|
||||
notifyListeners = null;
|
||||
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||
while (!listeners.isEmpty()) {
|
||||
NotifyListener listener = listeners.remove();
|
||||
if (!listener.expired()) {
|
||||
Platform.startThread(listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
TThread.setCurrentThread(thread);
|
||||
}
|
||||
|
||||
@Sync
|
||||
@Rename("notifyAll")
|
||||
public final void notifyAll0(){
|
||||
if (notifyListeners != null){
|
||||
JSArray<NotifyListener> listeners = window.newArray();
|
||||
while (notifyListeners.getLength() > 0) {
|
||||
listeners.push(notifyListeners.shift());
|
||||
}
|
||||
notifyListeners = null;
|
||||
while (listeners.getLength() > 0) {
|
||||
listeners.shift().handleNotify();
|
||||
public final void notifyAll0() {
|
||||
if (!holdsLock(this)) {
|
||||
throw new TIllegalMonitorStateException();
|
||||
}
|
||||
PlatformQueue<NotifyListener> listeners = monitor.notifyListeners;
|
||||
while (!listeners.isEmpty()) {
|
||||
NotifyListener listener = listeners.remove();
|
||||
if (!listener.expired()) {
|
||||
Platform.startThread(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,49 +216,51 @@ public class TObject {
|
|||
|
||||
@Rename("wait")
|
||||
public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) {
|
||||
if (notifyListeners == null) {
|
||||
notifyListeners = window.newArray();
|
||||
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
|
||||
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);
|
||||
notifyListeners.push(listener);
|
||||
if (timeout == 0 && nanos == 0) {
|
||||
return;
|
||||
}
|
||||
listener.timerId = window.setTimeout(listener, timeout);
|
||||
monitorExit(this, monitor.count);
|
||||
}
|
||||
|
||||
private static class NotifyListenerImpl implements NotifyListener, TimerHandler {
|
||||
private static class NotifyListenerImpl implements NotifyListener, TimerHandler, PlatformRunnable {
|
||||
final TObject obj;
|
||||
final AsyncCallback<Void> callback;
|
||||
final TThread currentThread = TThread.currentThread();
|
||||
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.lockCount = lockCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleNotify() {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
TThread.setCurrentThread(currentThread);
|
||||
if (timerId >= 0) {
|
||||
window.clearTimeout(timerId);
|
||||
timerId = -1;
|
||||
}
|
||||
finished = true;
|
||||
try {
|
||||
callback.complete(null);
|
||||
} finally {
|
||||
TThread.setCurrentThread(TThread.getMainThread());
|
||||
}
|
||||
return true;
|
||||
public boolean expired() {
|
||||
boolean result = expired;
|
||||
expired = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
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.jso.JS;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
import org.teavm.platform.async.AsyncCallback;
|
||||
|
||||
|
||||
|
@ -57,7 +58,7 @@ public class TThread extends TObject implements TRunnable {
|
|||
}
|
||||
|
||||
public void start(){
|
||||
Platform.startThread(new Runnable() {
|
||||
Platform.startThread(new PlatformRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
@ -98,14 +99,7 @@ public class TThread extends TObject implements TRunnable {
|
|||
public static native void yield();
|
||||
|
||||
private static void yield(final AsyncCallback<Void> callback) {
|
||||
final TThread current = currentThread();
|
||||
window.setTimeout(new TimerHandler() {
|
||||
@Override public void onTimer() {
|
||||
setCurrentThread(current);
|
||||
callback.complete(null);
|
||||
setCurrentThread(mainThread);
|
||||
}
|
||||
}, 0);
|
||||
callback.complete(null);
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
|
@ -140,7 +134,6 @@ public class TThread extends TObject implements TRunnable {
|
|||
@Override public void onTimer() {
|
||||
setCurrentThread(current);
|
||||
callback.complete(null);
|
||||
setCurrentThread(mainThread);
|
||||
}
|
||||
}, millis);
|
||||
}
|
||||
|
|
|
@ -205,6 +205,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
renderRuntimeObjcls();
|
||||
renderRuntimeNullCheck();
|
||||
renderRuntimeIntern();
|
||||
renderRuntimeThreads();
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering runtime methods. See a cause for details", 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();
|
||||
}
|
||||
|
||||
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 {
|
||||
for (ClassNode cls : classes) {
|
||||
renderDeclaration(cls);
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
*/
|
||||
package org.teavm.javascript.spi;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -26,5 +23,6 @@ import java.lang.annotation.Target;
|
|||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Inherited
|
||||
public @interface Sync {
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ public class AsyncProgramSplitter {
|
|||
}
|
||||
|
||||
public void split(Program program) {
|
||||
System.out.println("Splitting program");
|
||||
System.out.println(new ListingBuilder().buildListing(program, " "));
|
||||
|
||||
parts.clear();
|
||||
Program initialProgram = createStubCopy(program);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -464,8 +464,9 @@ function $rt_asyncAdapter(f) {
|
|||
var result;
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
var $return = args.pop();
|
||||
args.unshift(this);
|
||||
try {
|
||||
result = f.apply(this, args);
|
||||
result = f.apply(null, args);
|
||||
} catch (e) {
|
||||
return $return($rt_asyncError(e));
|
||||
}
|
||||
|
@ -510,7 +511,9 @@ function $rt_continue(f) {
|
|||
return function() {
|
||||
var self = this;
|
||||
var args = arguments;
|
||||
var thread = $rt_getThread();
|
||||
setTimeout(function() {
|
||||
$rt_setThread(thread);
|
||||
f.apply(self, args);
|
||||
}, 0);
|
||||
};
|
||||
|
|
|
@ -132,7 +132,7 @@ class JSODependencyListener implements DependencyListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodAchieved(DependencyAgent agent, MethodDependency methodDep, CallLocation location) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,9 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
processor = new JavascriptNativeProcessor(innerSource);
|
||||
processor.setDiagnostics(diagnostics);
|
||||
processor.processClass(cls);
|
||||
if (processor.isNative(cls.getName())) {
|
||||
processor.processFinalMethods(cls);
|
||||
}
|
||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
processor.processJSBody(cls, method);
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.teavm.javascript.spi.GeneratedBy;
|
|||
import org.teavm.jso.*;
|
||||
import org.teavm.model.*;
|
||||
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);
|
||||
}
|
||||
|
||||
public boolean isNative(String className) {
|
||||
return nativeRepos.isJavaScriptClass(className);
|
||||
}
|
||||
|
||||
public void setDiagnostics(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) {
|
||||
program = methodToProcess.getProgram();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
|
@ -78,6 +125,17 @@ class JavascriptNativeProcessor {
|
|||
}
|
||||
replacement.clear();
|
||||
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());
|
||||
if (method.getAnnotations().get(JSProperty.class.getName()) != null) {
|
||||
if (isProperGetter(method.getDescriptor())) {
|
||||
|
|
|
@ -46,7 +46,7 @@ class NativeJavascriptClassRepository {
|
|||
|
||||
private boolean figureOutIfJavaScriptClass(String 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;
|
||||
}
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
|
|
|
@ -94,12 +94,24 @@ public final class Platform {
|
|||
|
||||
@GeneratedBy(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();
|
||||
}
|
||||
|
||||
@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) {
|
||||
return ((PlatformHelper)JS.getGlobal()).getStringClass().fromCharCode(charCode);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.platform;
|
||||
|
||||
import org.teavm.jso.JSConstructor;
|
||||
import org.teavm.jso.JSMethod;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
|
@ -29,4 +30,10 @@ interface PlatformHelper extends JSObject {
|
|||
|
||||
@JSProperty("String")
|
||||
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.model.*;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -41,9 +42,10 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
case "clone":
|
||||
method.getVariable(1).connect(method.getResult());
|
||||
break;
|
||||
case "startThread": {
|
||||
case "startThread":
|
||||
case "schedule": {
|
||||
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));
|
||||
launchMethod.use();
|
||||
break;
|
||||
|
@ -78,7 +80,10 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
generateClone(context, writer);
|
||||
break;
|
||||
case "startThread":
|
||||
generateStartThread(context, writer);
|
||||
generateSchedule(context, writer, false);
|
||||
break;
|
||||
case "schedule":
|
||||
generateSchedule(context, writer, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -129,11 +134,12 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
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);
|
||||
writer.append("window.setTimeout(function()").ws().append("{").indent().softNewLine();
|
||||
writer.append("$rt_rootInvocationAdapter(").appendMethodBody(Platform.class, "launchThread", Runnable.class,
|
||||
void.class).append(")(").append(runnable).append(");").softNewLine();
|
||||
writer.outdent().append("},").ws().append("0);").softNewLine();
|
||||
writer.append("return window.setTimeout(function()").ws().append("{").indent().softNewLine();
|
||||
writer.append("$rt_rootInvocationAdapter(").appendMethodBody(Platform.class, "launchThread",
|
||||
PlatformRunnable.class, void.class).append(")(").append(runnable).append(");").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 {
|
||||
report(Arrays.toString(args));
|
||||
findPrimes();
|
||||
withoutAsync();
|
||||
report("");
|
||||
withAsync();
|
||||
|
@ -64,11 +65,36 @@ public final class AsyncProgram {
|
|||
report("Now trying wait...");
|
||||
|
||||
synchronized (lock) {
|
||||
report("Lock acquired");
|
||||
lock.wait(20000);
|
||||
}
|
||||
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) {
|
||||
long current = System.currentTimeMillis() - start;
|
||||
System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
|
||||
|
|
Loading…
Reference in New Issue
Block a user