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 57cb9e0cc..0ee0a8189 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 @@ -50,6 +50,24 @@ public class TObject { boolean expired(); } + static void monitorEnterSync(TObject o) { + if (o.monitor == null) { + o.monitor = new Monitor(); + } + if (o.monitor.owner == null) { + o.monitor.owner = TThread.currentThread(); + } + o.monitor.count++; + } + + static void monitorExitSync(TObject o) { + if (o.isEmptyMonitor() || o.monitor.owner != TThread.currentThread()) { + throw new TIllegalMonitorStateException(); + } + --o.monitor.count; + o.isEmptyMonitor(); + } + static void monitorEnter(TObject o) { monitorEnter(o, 1); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java new file mode 100644 index 000000000..e3b50ce9d --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java @@ -0,0 +1,26 @@ +/* + * 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.ref; + +/** + * + * @author Alexey Andreev + */ +public class TReferenceQueue { + public TReference poll() { + return null; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java index d91c55dd6..b14232f8f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java @@ -26,6 +26,10 @@ public class TWeakReference extends TReference { this.value = value; } + public TWeakReference(T value, @SuppressWarnings("unused") TReferenceQueue queue) { + this.value = value; + } + @Override public T get() { return value; diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimer.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimer.java index 9b1d12049..500afd299 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimer.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimer.java @@ -18,8 +18,8 @@ package org.teavm.classlib.java.util; import org.teavm.classlib.java.lang.TIllegalStateException; import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TString; -import org.teavm.dependency.PluggableDependency; -import org.teavm.javascript.spi.GeneratedBy; +import org.teavm.platform.Platform; +import org.teavm.platform.PlatformRunnable; /** * @@ -53,7 +53,11 @@ public class TTimer extends TObject { task.nativeTimerId = scheduleOnce(task, (int)delay); } - @GeneratedBy(TimerNativeGenerator.class) - @PluggableDependency(TimerNativeGenerator.class) - private static native int scheduleOnce(TTimerTask task, int delay); + private static int scheduleOnce(final TTimerTask task, int delay) { + return Platform.schedule(new PlatformRunnable() { + @Override public void run() { + TTimerTask.performOnce(task); + } + }, delay); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimerTask.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimerTask.java index 1271e3e20..088ac45e0 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimerTask.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimerTask.java @@ -34,11 +34,11 @@ public abstract class TTimerTask extends TObject implements TRunnable { return true; } - void performOnce() { - if (timer != null) { - run(); - timer.tasks.remove(this); - timer = null; + static void performOnce(TTimerTask task) { + if (task.timer != null) { + task.run(); + task.timer.tasks.remove(task); + task.timer = null; } } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java deleted file mode 100644 index 3a376cd1d..000000000 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014 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.util; - -import java.io.IOException; -import java.util.TimerTask; -import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyAgent; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodDependency; -import org.teavm.javascript.spi.Generator; -import org.teavm.javascript.spi.GeneratorContext; -import org.teavm.model.CallLocation; -import org.teavm.model.MethodReference; - -/** - * - * @author Alexey Andreev - */ -public class TimerNativeGenerator implements Generator, DependencyPlugin { - private static final MethodReference performOnceRef = new MethodReference(TimerTask.class, - "performOnce", void.class); - - @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { - switch (method.getReference().getName()) { - case "scheduleOnce": { - MethodDependency performMethod = agent.linkMethod(performOnceRef, null); - performMethod.use(); - method.getVariable(1).connect(performMethod.getVariable(1)); - break; - } - } - } - - @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - switch (methodRef.getName()) { - case "scheduleOnce": - generateScheduleNative(context, writer); - break; - } - } - - private void generateScheduleNative(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("return setTimeout(function() {").indent().softNewLine(); - writer.append(context.getParameterName(1)).append(".").appendMethod(performOnceRef) - .append("();").softNewLine(); - writer.outdent().append("},").ws().append(context.getParameterName(2)).append(");").softNewLine(); - } -} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 82f3a23a5..699350acb 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -338,9 +338,20 @@ class DependencyGraphBuilder { StringBuilder sb = new StringBuilder(); for (int i = 0; i < dimensions.size(); ++i) { sb.append('['); + itemType = ((ValueType.Array)itemType).getItemType(); + } + String itemTypeStr; + if (itemType instanceof ValueType.Object) { + itemTypeStr = ((ValueType.Object)itemType).getClassName(); + } else { + itemTypeStr = itemType.toString(); + } + sb.append(itemTypeStr); + DependencyNode node = nodes[receiver.getIndex()]; + for (int i = 0; i < dimensions.size(); ++i) { + node.propagate(dependencyChecker.getType(sb.substring(i, sb.length()))); + node = node.getArrayItem(); } - sb.append(itemType); - nodes[receiver.getIndex()].propagate(dependencyChecker.getType(sb.toString())); String className = extractClassName(itemType); if (className != null) { dependencyChecker.linkClass(className, new CallLocation(caller.getMethod(), currentLocation)); @@ -499,6 +510,11 @@ class DependencyGraphBuilder { new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null); nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); methodDep.use(); + + methodDep = dependencyChecker.linkMethod( + new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null); + nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); + methodDep.use(); } @Override @@ -507,6 +523,11 @@ class DependencyGraphBuilder { new MethodReference(Object.class, "monitorExit", Object.class, void.class), null); nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); methodDep.use(); + + methodDep = dependencyChecker.linkMethod( + new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null); + nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); + methodDep.use(); } }; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java index 9c77fe005..12686b2d4 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -120,6 +120,11 @@ public class DependencyNode implements ValueDependencyInfo { if (DependencyChecker.shouldLog) { arrayItemNode.tag = tag + "["; } + arrayItemNode.addConsumer(new DependencyConsumer() { + @Override public void consume(DependencyAgentType type) { + DependencyNode.this.propagate(type); + } + }); } return arrayItemNode; } 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 1a27916e3..7c02724c7 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -2001,17 +2001,22 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public void visit(MonitorEnterStatement statement) { - if (!async) { - return; - } try { - MethodReference monitorEnterRef = new MethodReference( - Object.class, "monitorEnter", Object.class, void.class); - writer.append("return ").append(naming.getFullNameForAsync(monitorEnterRef)).append("("); - statement.getObjectRef().acceptVisitor(this); - writer.append(",").ws(); - writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')'); - writer.append(");").softNewLine(); + if (async) { + MethodReference monitorEnterRef = new MethodReference( + Object.class, "monitorEnter", Object.class, void.class); + writer.append("return ").append(naming.getFullNameForAsync(monitorEnterRef)).append("("); + statement.getObjectRef().acceptVisitor(this); + writer.append(",").ws(); + writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')'); + writer.append(");").softNewLine(); + } else { + MethodReference monitorEnterRef = new MethodReference( + Object.class, "monitorEnterSync", 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); } @@ -2019,15 +2024,20 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public void visit(MonitorExitStatement statement) { - if (!async) { - return; - } try { - MethodReference monitorExitRef = new MethodReference( - Object.class, "monitorExit", Object.class, void.class); - writer.appendMethodBody(monitorExitRef).append("("); - statement.getObjectRef().acceptVisitor(this); - writer.append(");").softNewLine(); + if (async) { + MethodReference monitorExitRef = new MethodReference( + Object.class, "monitorExit", Object.class, void.class); + writer.appendMethodBody(monitorExitRef).append("("); + statement.getObjectRef().acceptVisitor(this); + writer.append(");").softNewLine(); + } else { + MethodReference monitorEnterRef = new MethodReference( + Object.class, "monitorExitSync", 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); } 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 a52672b29..def67f110 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.*; /** * @@ -64,6 +65,25 @@ public class AsyncMethodFinder { } } } + if (hasAsyncMethods()) { + for (String clsName : classSource.getClassNames()) { + ClassReader cls = classSource.get(clsName); + for (MethodReader method : cls.getMethods()) { + if (asyncMethods.contains(method.getReference()) || method.getProgram() == null) { + continue; + } + ProgramReader program = method.getProgram(); + AsyncInstructionReader insnReader = new AsyncInstructionReader(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + program.basicBlockAt(i).readAllInstructions(insnReader); + if (insnReader.async) { + add(method.getReference()); + break; + } + } + } + } + } for (MethodReference method : asyncMethods) { addOverridenToFamily(method); } @@ -80,6 +100,18 @@ public class AsyncMethodFinder { } } + private boolean hasAsyncMethods() { + int count = asyncMethods.size(); + if (asyncMethods.contains(new MethodReference(Object.class, "monitorEnter", Object.class, void.class))) { + --count; + } + if (asyncMethods.contains(new MethodReference(Object.class, "monitorEnter", Object.class, + int.class, void.class))) { + --count; + } + return count > 0; + } + private void add(MethodReference methodRef) { if (!asyncMethods.add(methodRef)) { return; @@ -185,4 +217,166 @@ public class AsyncMethodFinder { } } } + + class AsyncInstructionReader implements InstructionReader { + boolean async; + + @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) { + async = true; + } + + @Override + public void monitorExit(VariableReader objectRef) { + } + } } diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSODependencyListener.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSODependencyListener.java index 75ec8f361..53c605b63 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSODependencyListener.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSODependencyListener.java @@ -84,7 +84,9 @@ class JSODependencyListener implements DependencyListener { for (MethodReader method : cls.getMethods()) { if (exposedCls.inheritedMethods.containsKey(method.getDescriptor()) || exposedCls.methods.containsKey(method.getDescriptor())) { - agent.linkMethod(method.getReference(), null).use(); + MethodDependency methodDep = agent.linkMethod(method.getReference(), null); + methodDep.getVariable(0).propagate(agent.getType(name)); + methodDep.use(); } } return exposedCls;