Fix support of async <clinit> when called by getstatic/putstatic

This commit is contained in:
Alexey Andreev 2016-11-20 17:50:47 +03:00
parent c023ac2053
commit 24efd1f49c
5 changed files with 61 additions and 26 deletions

View File

@ -20,6 +20,7 @@ import org.teavm.model.TextLocation;
public class InitClassStatement extends Statement { public class InitClassStatement extends Statement {
private TextLocation location; private TextLocation location;
private String className; private String className;
private boolean async;
public String getClassName() { public String getClassName() {
return className; return className;
@ -37,6 +38,14 @@ public class InitClassStatement extends Statement {
this.location = location; this.location = location;
} }
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
@Override @Override
public void acceptVisitor(StatementVisitor visitor) { public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -328,11 +328,7 @@ class StatementGenerator implements InstructionVisitor {
Map<Integer, List<Integer>> switchMap = new HashMap<>(); Map<Integer, List<Integer>> switchMap = new HashMap<>();
for (int i = 0; i < insn.getEntries().size(); ++i) { for (int i = 0; i < insn.getEntries().size(); ++i) {
SwitchTableEntry entry = insn.getEntries().get(i); SwitchTableEntry entry = insn.getEntries().get(i);
List<Integer> conditions = switchMap.get(entry.getTarget().getIndex()); List<Integer> conditions = switchMap.computeIfAbsent(entry.getTarget().getIndex(), k -> new ArrayList<>());
if (conditions == null) {
conditions = new ArrayList<>();
switchMap.put(entry.getTarget().getIndex(), conditions);
}
conditions.add(entry.getCondition()); conditions.add(entry.getCondition());
} }
List<Integer> targets = new ArrayList<>(switchMap.keySet()); List<Integer> targets = new ArrayList<>(switchMap.keySet());
@ -590,6 +586,8 @@ class StatementGenerator implements InstructionVisitor {
public void visit(InitClassInstruction insn) { public void visit(InitClassInstruction insn) {
InitClassStatement stmt = Statement.initClass(insn.getClassName()); InitClassStatement stmt = Statement.initClass(insn.getClassName());
stmt.setLocation(currentLocation); stmt.setLocation(currentLocation);
stmt.setAsync(async);
async = false;
statements.add(stmt); statements.add(stmt);
} }

View File

@ -121,18 +121,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
this.async = async; this.async = async;
} }
public MethodNode getCurrentMethod() {
return currentMethod;
}
public void setCurrentMethod(MethodNode currentMethod) { public void setCurrentMethod(MethodNode currentMethod) {
this.currentMethod = currentMethod; this.currentMethod = currentMethod;
} }
public int getCurrentPart() {
return currentPart;
}
public void setCurrentPart(int currentPart) { public void setCurrentPart(int currentPart) {
this.currentPart = currentPart; this.currentPart = currentPart;
} }
@ -433,6 +425,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
} }
writer.appendClass(statement.getClassName()).append("_$callClinit();").softNewLine(); writer.appendClass(statement.getClassName()).append("_$callClinit();").softNewLine();
if (statement.isAsync()) {
emitSuspendChecker();
}
if (statement.getLocation() != null) { if (statement.getLocation() != null) {
popLocation(); popLocation();
} }

View File

@ -15,17 +15,35 @@
*/ */
package org.teavm.model.util; package org.teavm.model.util;
import java.util.*; import java.util.ArrayDeque;
import org.teavm.common.*; import java.util.ArrayList;
import org.teavm.model.*; import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.teavm.common.Graph;
import org.teavm.common.GraphSplittingBackend;
import org.teavm.common.GraphUtils;
import org.teavm.common.IntegerArray;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction; import org.teavm.model.instructions.MonitorEnterInstruction;
/**
*
* @author Alexey Andreev
*/
public class AsyncProgramSplitter { public class AsyncProgramSplitter {
private List<Part> parts = new ArrayList<>(); private List<Part> parts = new ArrayList<>();
private Map<Long, Integer> partMap = new HashMap<>(); private Map<Long, Integer> partMap = new HashMap<>();
@ -68,6 +86,10 @@ public class AsyncProgramSplitter {
if (!asyncMethods.contains(findRealMethod(invoke.getMethod()))) { if (!asyncMethods.contains(findRealMethod(invoke.getMethod()))) {
continue; continue;
} }
} else if (insn instanceof InitClassInstruction) {
if (!isSplittingClassInitializer(((InitClassInstruction) insn).getClassName())) {
continue;
}
} else if (!(insn instanceof MonitorEnterInstruction)) { } else if (!(insn instanceof MonitorEnterInstruction)) {
continue; continue;
} }
@ -164,6 +186,16 @@ public class AsyncProgramSplitter {
partMap.clear(); partMap.clear();
} }
private boolean isSplittingClassInitializer(String className) {
ClassReader cls = classSource.get(className);
if (cls == null) {
return false;
}
MethodReader method = cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID));
return method != null && asyncMethods.contains(method.getReference());
}
private MethodReference findRealMethod(MethodReference method) { private MethodReference findRealMethod(MethodReference method) {
String clsName = method.getClassName(); String clsName = method.getClassName();
while (clsName != null) { while (clsName != null) {
@ -215,10 +247,6 @@ public class AsyncProgramSplitter {
return Arrays.copyOf(result, result.length); return Arrays.copyOf(result, result.length);
} }
public int getBlockSuccessor(int index, int blockIndex) {
return parts.get(index).blockSuccessors[blockIndex];
}
public int[] getSplitPoints(int index) { public int[] getSplitPoints(int index) {
return parts.get(index).splitPoints.clone(); return parts.get(index).splitPoints.clone();
} }

View File

@ -121,10 +121,15 @@ public class VMTest {
assertEquals(0, initCount); assertEquals(0, initCount);
assertEquals("foo", AsyncClinitClass.foo()); assertEquals("foo", AsyncClinitClass.foo());
assertEquals(1, initCount); assertEquals(1, initCount);
assertEquals(AsyncClinitClass.state, "ok"); assertEquals("ok", AsyncClinitClass.state);
assertEquals("bar", AsyncClinitClass.bar()); assertEquals("bar", AsyncClinitClass.bar());
assertEquals(1, initCount); assertEquals(1, initCount);
assertEquals(AsyncClinitClass.state, "ok"); assertEquals("ok", AsyncClinitClass.state);
}
@Test
public void asyncClinitField() {
assertEquals("ok", AsyncClinitClass.state);
} }
static int initCount = 0; static int initCount = 0;