Working on support of async <clinit> methods

This commit is contained in:
Alexey Andreev 2016-11-17 23:15:00 +03:00
parent 8e2814f984
commit 764c9bbb1e
4 changed files with 135 additions and 30 deletions

View File

@ -363,16 +363,7 @@ public class Renderer implements RenderingManager {
}
if (needsClinit) {
writer.append("function ").appendClass(cls.getName()).append("_$callClinit()").ws()
.append("{").softNewLine().indent();
writer.appendClass(cls.getName()).append("_$callClinit").ws().append("=").ws()
.append("function(){};").newLine();
for (MethodNode method : clinitMethods) {
renderBody(method, true);
}
writer.appendMethodBody(new MethodReference(cls.getName(), clinit.getDescriptor()))
.append("();").softNewLine();
writer.outdent().append("}").newLine();
renderCallClinit(clinit, cls, clinitMethods);
}
if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) {
for (MethodNode method : cls.getMethods()) {
@ -396,6 +387,71 @@ public class Renderer implements RenderingManager {
debugEmitter.emitClass(null);
}
private void renderCallClinit(MethodReader clinit, ClassNode cls, List<MethodNode> clinitMethods)
throws IOException {
boolean isAsync = asyncMethods.contains(clinit.getReference());
if (isAsync) {
writer.append("var ").appendClass(cls.getName()).append("_$clinitCalled").ws().append("=").ws()
.append("false;").softNewLine();
}
writer.append("function ").appendClass(cls.getName()).append("_$callClinit()").ws()
.append("{").softNewLine().indent();
if (isAsync) {
writer.append("var ").append(context.pointerName()).ws().append("=").ws()
.append("0").append(";").softNewLine();
writer.append("if").ws().append("(").appendFunction("$rt_resuming").append("())").ws().append("{")
.indent().softNewLine();
writer.append(context.pointerName()).ws().append("=").ws().appendFunction("$rt_nativeThread")
.append("().pop();").softNewLine();
writer.outdent().append("}").ws();
writer.append("else if").ws().append("(").appendClass(cls.getName()).append("_$clinitCalled)").ws()
.append("{").indent().softNewLine();
writer.append("return;").softNewLine();
writer.outdent().append("}").softNewLine();
renderAsyncPrologue();
writer.append("case 0:").indent().softNewLine();
writer.appendClass(cls.getName()).append("_$clinitCalled").ws().append('=').ws().append("true;")
.softNewLine();
} else {
renderEraseClinit(cls);
for (MethodNode method : clinitMethods) {
renderBody(method, true);
}
}
if (isAsync) {
writer.append(context.pointerName()).ws().append("=").ws().append("1;").softNewLine();
writer.outdent().append("case 1:").indent().softNewLine();
}
writer.appendMethodBody(new MethodReference(cls.getName(), clinit.getDescriptor()))
.append("();").softNewLine();
if (isAsync) {
writer.append("if").ws().append("(").appendFunction("$rt_suspending").append("())").ws().append("{")
.indent().softNewLine();
writer.append("break " + context.mainLoopName() + ";").softNewLine();
writer.outdent().append("}").softNewLine();
renderEraseClinit(cls);
writer.append("return;").softNewLine().outdent();
renderAsyncEpilogue();
writer.appendFunction("$rt_nativeThread").append("().push(" + context.pointerName() + ");").softNewLine();
}
writer.outdent().append("}").newLine();
}
private void renderEraseClinit(ClassNode cls) throws IOException {
writer.appendClass(cls.getName()).append("_$callClinit").ws().append("=").ws()
.append("function(){};").newLine();
}
private void renderClassMetadata(List<ClassNode> classes) {
try {
writer.append("$rt_metadata([");
@ -598,6 +654,18 @@ public class Renderer implements RenderingManager {
debugEmitter.emitMethod(null);
}
private void renderAsyncPrologue() throws IOException {
writer.append(context.mainLoopName()).append(":").ws().append("while").ws().append("(true)")
.ws().append("{").ws();
writer.append("switch").ws().append("(").append(context.pointerName()).append(")").ws()
.append('{').softNewLine();
}
private void renderAsyncEpilogue() throws IOException {
writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine();
writer.append("}}").softNewLine();
}
private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
private boolean async;
private StatementRenderer statementRenderer;
@ -726,10 +794,8 @@ public class Renderer implements RenderingManager {
if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
writer.append("try").ws().append('{').indent().softNewLine();
}
writer.append(context.mainLoopName()).append(":").ws().append("while").ws().append("(true)")
.ws().append("{").ws();
writer.append("switch").ws().append("(").append(context.pointerName()).append(")").ws()
.append('{').softNewLine();
renderAsyncPrologue();
for (int i = 0; i < methodNode.getBody().size(); ++i) {
writer.append("case ").append(i).append(":").indent().softNewLine();
if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
@ -746,8 +812,7 @@ public class Renderer implements RenderingManager {
part.getStatement().acceptVisitor(statementRenderer);
writer.outdent();
}
writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine();
writer.append("}}").softNewLine();
renderAsyncEpilogue();
if (methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();

View File

@ -406,10 +406,25 @@ function $rt_metadata(data) {
for (var j = 0; j < names.length; j = (j + 1) | 0) {
window[names[j]] = (function(cls, name) {
return function() {
var clinit = cls.$clinit;
cls.$clinit = function() {};
clinit();
return window[name].apply(window, arguments);
var ptr = 0;
if ($rt_resuming()) {
ptr = $rt_nativeThread().pop();
}
main: while (true) {
switch (ptr) {
case 0:
clinit = cls.$clinit;
clinit();
ptr = 1;
case 1:
var result = window[name].apply(window, arguments);
if ($rt_suspend()) {
break;
}
return result;
}
}
$rt_nativeThread().push(ptr);
}
})(cls, names[j]);
}

View File

@ -19,7 +19,6 @@ import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.dependency.PluggableDependency;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.Async;
import org.teavm.interop.Sync;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationValue;
import org.teavm.model.CallLocation;
@ -32,10 +31,6 @@ import org.teavm.model.MethodHolder;
import org.teavm.model.ValueType;
import org.teavm.platform.async.AsyncCallback;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodProcessor implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
@ -65,11 +60,6 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
annot.getValues().put("value", new AnnotationValue(ValueType.parse(AsyncMethodGenerator.class)));
method.getAnnotations().add(annot);
}
} else if (method.getName().equals("<clinit>")) {
if (method.getAnnotations().get(Sync.class.getName()) == null) {
AnnotationHolder annot = new AnnotationHolder(Sync.class.getName());
method.getAnnotations().add(annot);
}
}
}
}

View File

@ -116,6 +116,41 @@ public class VMTest {
assertEquals(x, id(23));
}
@Test
public void asyncClinit() {
assertEquals(0, initCount);
assertEquals("foo", AsyncClinitClass.foo());
assertEquals(1, initCount);
assertEquals(AsyncClinitClass.state, "ok");
assertEquals("bar", AsyncClinitClass.bar());
assertEquals(1, initCount);
assertEquals(AsyncClinitClass.state, "ok");
}
static int initCount = 0;
private static class AsyncClinitClass {
static String state = "";
static {
initCount++;
try {
Thread.sleep(1);
state += "ok";
} catch (InterruptedException e) {
state += "error";
}
}
public static String foo() {
return "foo";
}
public static String bar() {
return "bar";
}
}
private void throwException() {
throw new RuntimeException();
}