diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 96a957fd8..8e11c9aff 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -15,6 +15,7 @@ */ 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; @@ -32,40 +33,50 @@ import org.teavm.platform.async.AsyncCallback; @Superclass("") public class TObject { private static final Window window = (Window)JS.getGlobal(); - private TThread owner; - private TObject monitorLock; - private int monitorCount; private JSArray notifyListeners; + private Lock lock; - interface NotifyListener extends JSObject { - void handleNotify(); + private static class Lock { + TThread owner; + int count; + + public Lock() { + this.owner = TThread.currentThread(); + count = 1; + } } - static void monitorEnter(TObject o){ - if (o.monitorLock == null ){ - o.monitorLock = new TObject(); - } - while (o.owner != null && o.owner != TThread.currentThread()) { - try { - o.monitorLock.wait(); - } catch (InterruptedException ex) { + interface NotifyListener extends JSObject { + boolean handleNotify(); + } - } + static void monitorEnter(TObject o) { + if (o.lock == null) { + o.lock = new Lock(); + return; + } + if (o.lock.owner != TThread.currentThread()) { + while (o.lock != null) { + try { + o.lock.wait(); + } catch (InterruptedException ex) { + } + } + o.lock = new Lock(); + } else { + o.lock.count++; } - o.owner = TThread.currentThread(); - o.monitorCount++; } static void monitorExit(TObject o){ - o.monitorCount--; - if (o.monitorCount == 0 && o.monitorLock != null) { - o.owner = null; - o.monitorLock.notifyAll(); + if (o.lock != null && o.lock.count-- == 0) { + o.lock.notifyAll(); + o.lock = null; } } static boolean holdsLock(TObject o){ - return o.owner == TThread.currentThread(); + return o.lock != null && o.lock.owner == TThread.currentThread(); } @Rename("fakeInit") @@ -114,12 +125,16 @@ public class TObject { @Rename("notify") public final void notify0(){ - if (notifyListeners != null && notifyListeners.getLength() > 0){ - notifyListeners.shift().handleNotify(); + if (notifyListeners != null) { + while (notifyListeners.getLength() > 0 && notifyListeners.shift().handleNotify()) { + // repeat loop + } + if (notifyListeners.getLength() == 0) { + notifyListeners = null; + } } } - @Rename("notifyAll") public final void notifyAll0(){ if (notifyListeners != null){ @@ -127,6 +142,7 @@ public class TObject { while (notifyListeners.getLength() > 0) { listeners.push(notifyListeners.shift()); } + notifyListeners = null; while (listeners.getLength() > 0) { listeners.shift().handleNotify(); } @@ -144,7 +160,7 @@ public class TObject { @Async @Rename("wait") - public native final void wait0(long timeout, int nanos) throws TInterruptedException; + private native final void wait0(long timeout, int nanos) throws TInterruptedException; @Rename("wait") public final void wait0(long timeout, int nanos, final AsyncCallback callback) { @@ -152,24 +168,55 @@ public class TObject { notifyListeners = window.newArray(); } final TThread currentThread = TThread.currentThread(); - notifyListeners.push(new NotifyListener() { - @Override - public void handleNotify() { - TThread.setCurrentThread(currentThread); - try { - callback.complete(null); - } finally { - TThread.setCurrentThread(TThread.getMainThread()); - } + final NotifyListenerImpl listener = new NotifyListenerImpl(callback, currentThread); + notifyListeners.push(listener); + if (timeout == 0 && nanos == 0) { + return; + } + listener.timerId = window.setTimeout(listener, timeout); + } + + private static class NotifyListenerImpl implements NotifyListener, TimerHandler { + final AsyncCallback callback; + final TThread currentThread; + int timerId = -1; + boolean finished; + + public NotifyListenerImpl(AsyncCallback callback, TThread currentThread) { + this.callback = callback; + this.currentThread = currentThread; + } + + @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; + } + + @Override + public void onTimer() { + handleNotify(); + } } @Rename("wait") public final void wait0() throws TInterruptedException { try { wait(0l); - } catch ( InterruptedException ex){ + } catch (InterruptedException ex) { throw new TInterruptedException(); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 28df21e31..71eb43831 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -73,31 +73,29 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public void visit(MonitorEnterStatement statement) { - if (async){ - try { - MethodReference monitorEnterRef = new MethodReference( - Object.class, "monitorEnter", Object.class, void.class); - writer.appendMethodBody(monitorEnterRef).append("("); - statement.getObjectRef().acceptVisitor(this); - writer.append(");").softNewLine(); - } catch (IOException ex){ - throw new RenderingException("IO error occured", ex); - } + try { + MethodReference monitorEnterRef = new MethodReference( + Object.class, "monitorEnter", Object.class, void.class); + writer.appendMethodBody(monitorEnterRef).append("("); + statement.getObjectRef().acceptVisitor(this); + writer.append(",").ws(); + writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')'); + writer.append(");").softNewLine(); + } catch (IOException ex){ + throw new RenderingException("IO error occured", ex); } } @Override public void visit(MonitorExitStatement statement) { - if (async){ - try { - MethodReference monitorExitRef = new MethodReference( - Object.class, "monitorExit", Object.class, void.class); - writer.appendMethodBody(monitorExitRef).append("("); - statement.getObjectRef().acceptVisitor(this); - writer.append(");").softNewLine(); - } catch (IOException ex){ - throw new RenderingException("IO error occured", ex); - } + try { + MethodReference monitorExitRef = new MethodReference( + Object.class, "monitorExit", Object.class, void.class); + writer.appendMethodBody(monitorExitRef).append("("); + statement.getObjectRef().acceptVisitor(this); + writer.append(");").softNewLine(); + } catch (IOException ex){ + throw new RenderingException("IO error occured", ex); } } @@ -938,7 +936,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext StringBuilder sb = new StringBuilder(); int index = blockIdMap.size(); do { - sb.append(variablePartNames.charAt(index % variableNames.length())); + sb.append(variablePartNames.charAt(index % variablePartNames.length())); index /= variablePartNames.length(); } while (index > 0); name = "$b" + sb; diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index d341d27f6..6ac0e2638 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -669,11 +669,10 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(MonitorEnterInstruction insn) { - MonitorEnterStatement stmt = new MonitorEnterStatement(); stmt.setLocation(currentLocation); - stmt.setObjectRef(Expr.var(insn.getObjectRef().getIndex())); + stmt.setAsyncTarget(asyncTarget); statements.add(stmt); } @@ -681,7 +680,6 @@ class StatementGenerator implements InstructionVisitor { public void visit(MonitorExitInstruction insn) { MonitorExitStatement stmt = new MonitorExitStatement(); stmt.setLocation(currentLocation); - stmt.setObjectRef(Expr.var(insn.getObjectRef().getIndex())); statements.add(stmt); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorEnterStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorEnterStatement.java index 9051f32f0..476d7820a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorEnterStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorEnterStatement.java @@ -20,41 +20,36 @@ package org.teavm.javascript.ast; * @author shannah */ public class MonitorEnterStatement extends Statement { - private NodeLocation location; private Expr objectRef; + private Integer asyncTarget; @Override public void acceptVisitor(StatementVisitor visitor) { visitor.visit(this); } - /** - * @return the location - */ public NodeLocation getLocation() { return location; } - /** - * @param location the location to set - */ public void setLocation(NodeLocation location) { this.location = location; } - /** - * @return the objectRef - */ public Expr getObjectRef() { return objectRef; } - /** - * @param objectRef the objectRef to set - */ public void setObjectRef(Expr objectRef) { this.objectRef = objectRef; } - + + public Integer getAsyncTarget() { + return asyncTarget; + } + + public void setAsyncTarget(Integer asyncTarget) { + this.asyncTarget = asyncTarget; + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorExitStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorExitStatement.java index 2f6d61fdc..f7d597103 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorExitStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/MonitorExitStatement.java @@ -20,41 +20,28 @@ package org.teavm.javascript.ast; * @author shannah */ public class MonitorExitStatement extends Statement { - private NodeLocation location; private Expr objectRef; - + private Integer asyncTarget; + @Override public void acceptVisitor(StatementVisitor visitor) { visitor.visit(this); } - /** - * @return the location - */ public NodeLocation getLocation() { return location; } - /** - * @param location the location to set - */ public void setLocation(NodeLocation location) { this.location = location; } - /** - * @return the objectRef - */ public Expr getObjectRef() { return objectRef; } - /** - * @param objectRef the objectRef to set - */ public void setObjectRef(Expr objectRef) { this.objectRef = objectRef; } - } diff --git a/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java b/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java index 78f89b64c..d494d66bc 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java +++ b/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java @@ -24,6 +24,7 @@ import org.teavm.javascript.spi.Async; import org.teavm.javascript.spi.InjectedBy; import org.teavm.javascript.spi.Sync; import org.teavm.model.*; +import org.teavm.model.instructions.*; /** * @@ -61,6 +62,17 @@ public class AsyncMethodFinder { } if (method.getAnnotations().get(Async.class.getName()) != null) { add(method.getReference()); + } else if (method.getProgram() != null) { + ProgramReader program = method.getProgram(); + AsyncInstructionFinder insnFinder = new AsyncInstructionFinder(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlockReader block = program.basicBlockAt(i); + block.readAllInstructions(insnFinder); + if (insnFinder.hasAsync) { + add(method.getReference()); + break; + } + } } } } @@ -174,4 +186,166 @@ public class AsyncMethodFinder { } } } + + private class AsyncInstructionFinder implements InstructionReader { + boolean hasAsync; + + @Override + public void location(InstructionLocation location) { + } + + @Override + public void nop() { + } + + @Override + public void classConstant(VariableReader receiver, ValueType cst) { + } + + @Override + public void nullConstant(VariableReader receiver) { + } + + @Override + public void integerConstant(VariableReader receiver, int cst) { + } + + @Override + public void longConstant(VariableReader receiver, long cst) { + } + + @Override + public void floatConstant(VariableReader receiver, float cst) { + } + + @Override + public void doubleConstant(VariableReader receiver, double cst) { + } + + @Override + public void stringConstant(VariableReader receiver, String cst) { + } + + @Override + public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, + NumericOperandType type) { + } + + @Override + public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) { + } + + @Override + public void assign(VariableReader receiver, VariableReader assignee) { + } + + @Override + public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { + } + + @Override + public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType, + NumericOperandType targetType) { + } + + @Override + public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type, + CastIntegerDirection targetType) { + } + + @Override + public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent, + BasicBlockReader alternative) { + } + + @Override + public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second, + BasicBlockReader consequent, BasicBlockReader alternative) { + } + + @Override + public void jump(BasicBlockReader target) { + } + + @Override + public void choose(VariableReader condition, List table, + BasicBlockReader defaultTarget) { + } + + @Override + public void exit(VariableReader valueToReturn) { + } + + @Override + public void raise(VariableReader exception) { + } + + @Override + public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { + } + + @Override + public void createArray(VariableReader receiver, ValueType itemType, + List dimensions) { + } + + @Override + public void create(VariableReader receiver, String type) { + } + + @Override + public void getField(VariableReader receiver, VariableReader instance, FieldReference field, + ValueType fieldType) { + } + + @Override + public void putField(VariableReader instance, FieldReference field, VariableReader value) { + } + + @Override + public void arrayLength(VariableReader receiver, VariableReader array) { + } + + @Override + public void cloneArray(VariableReader receiver, VariableReader array) { + } + + @Override + public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) { + } + + @Override + public void getElement(VariableReader receiver, VariableReader array, VariableReader index) { + } + + @Override + public void putElement(VariableReader array, VariableReader index, VariableReader value) { + } + + @Override + public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, + List arguments, InvocationType type) { + } + + @Override + public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { + } + + @Override + public void initClass(String className) { + } + + @Override + public void nullCheck(VariableReader receiver, VariableReader value) { + } + + @Override + public void monitorEnter(VariableReader objectRef) { + hasAsync = true; + } + + @Override + public void monitorExit(VariableReader objectRef) { + } + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java index 9734e2c19..16c02e49d 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java +++ b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java @@ -19,6 +19,7 @@ import java.util.*; import org.teavm.model.*; import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.JumpInstruction; +import org.teavm.model.instructions.MonitorEnterInstruction; /** * @@ -58,61 +59,67 @@ public class AsyncProgramSplitter { int last = 0; for (int i = 0; i < sourceBlock.getInstructions().size(); ++i) { Instruction insn = sourceBlock.getInstructions().get(i); + Integer receiver; if (insn instanceof InvokeInstruction) { InvokeInstruction invoke = (InvokeInstruction)insn; if (!asyncMethods.contains(invoke.getMethod())) { continue; } - - // If we met asynchronous invocation... - // Copy portion of current block from last occurence (or from start) to i'th instruction. - targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, - last, i + 1, targetBlock.getProgram())); - targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, - targetBlock.getProgram())); - for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) { - if (tryCatch.getHandler() != null) { - Step next = new Step(); - next.source = tryCatch.getHandler().getIndex(); - next.targetPart = step.targetPart; - queue.add(next); - } - } - last = i + 1; - - // If this instruction already separates program, end with current block and refer to the - // existing part - long key = ((long)step.source << 32) | i; - if (partMap.containsKey(key)) { - step.targetPart.blockSuccessors[targetBlock.getIndex()] = partMap.get(key); - continue taskLoop; - } - - // Create a new part - Program nextProgram = createStubCopy(program); - Part part = new Part(); - part.input = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null; - part.program = nextProgram; - int partId = parts.size(); - parts.add(part); - part.blockSuccessors = new int[program.basicBlockCount() + 1]; - Arrays.fill(part.blockSuccessors, -1); - - // Mark current instruction as a separator and remember which part is in charge. - partMap.put(key, partId); - step.targetPart.blockSuccessors[targetBlock.getIndex()] = partId; - - // Continue with a new block in the new part - targetBlock = nextProgram.createBasicBlock(); - if (step.source > 0) { - JumpInstruction jumpToNextBlock = new JumpInstruction(); - jumpToNextBlock.setTarget(targetBlock); - nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock); - nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, - nextProgram)); - } - step.targetPart = part; + receiver = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null; + } else if (insn instanceof MonitorEnterInstruction) { + receiver = null; + } else { + continue; } + + // If we met asynchronous invocation... + // Copy portion of current block from last occurrence (or from start) to i'th instruction. + targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, + last, i + 1, targetBlock.getProgram())); + targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, + targetBlock.getProgram())); + for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) { + if (tryCatch.getHandler() != null) { + Step next = new Step(); + next.source = tryCatch.getHandler().getIndex(); + next.targetPart = step.targetPart; + queue.add(next); + } + } + last = i + 1; + + // If this instruction already separates program, end with current block and refer to the + // existing part + long key = ((long)step.source << 32) | i; + if (partMap.containsKey(key)) { + step.targetPart.blockSuccessors[targetBlock.getIndex()] = partMap.get(key); + continue taskLoop; + } + + // Create a new part + Program nextProgram = createStubCopy(program); + Part part = new Part(); + part.input = receiver; + part.program = nextProgram; + int partId = parts.size(); + parts.add(part); + part.blockSuccessors = new int[program.basicBlockCount() + 1]; + Arrays.fill(part.blockSuccessors, -1); + + // Mark current instruction as a separator and remember which part is in charge. + partMap.put(key, partId); + step.targetPart.blockSuccessors[targetBlock.getIndex()] = partId; + + // Continue with a new block in the new part + targetBlock = nextProgram.createBasicBlock(); + if (step.source > 0) { + JumpInstruction jumpToNextBlock = new JumpInstruction(); + jumpToNextBlock.setTarget(targetBlock); + nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock); + nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, + nextProgram)); + } + step.targetPart = part; } targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, last, sourceBlock.getInstructions().size(), targetBlock.getProgram())); diff --git a/teavm-core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/teavm-core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index b154e3a46..e9beafe5c 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -313,12 +313,10 @@ public class MissingItemsProcessor { @Override public void visit(MonitorEnterInstruction insn) { - } @Override public void visit(MonitorExitInstruction insn) { - } }; } diff --git a/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java b/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java index c65d9da33..ac794867b 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java @@ -214,6 +214,6 @@ public class UsageExtractor implements InstructionVisitor { @Override public void visit(MonitorExitInstruction insn) { - usedVariables = new Variable[] {insn.getObjectRef()}; + usedVariables = new Variable[] {insn.getObjectRef() }; } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java b/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java index 99e9a1cbe..60da5b551 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java +++ b/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java @@ -382,12 +382,12 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(MonitorEnterInstruction insn) { - + } @Override public void visit(MonitorExitInstruction insn) { - + } } @@ -574,12 +574,10 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(MonitorEnterInstruction insn) { - } @Override public void visit(MonitorExitInstruction insn) { - } } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java index 41deb3cb5..c80fd2ec6 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java +++ b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java @@ -207,12 +207,12 @@ public final class VariableEscapeAnalyzer { @Override public void visit(MonitorEnterInstruction insn) { - + escaping[insn.getObjectRef().getIndex()] = true; } @Override public void visit(MonitorExitInstruction insn) { - + escaping[insn.getObjectRef().getIndex()] = true; } } }