mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: add option to support exceptions via setjmp/longjmp
This commit is contained in:
parent
114ad986e4
commit
90e00f7eb4
|
@ -25,7 +25,6 @@ import org.teavm.classlib.java.lang.TObject;
|
|||
import org.teavm.dependency.PluggableDependency;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.platform.PlatformClass;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
|
@ -69,7 +68,6 @@ public final class TArray extends TObject {
|
|||
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Unmanaged
|
||||
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
|
||||
return Allocator.allocateArray(cls.arrayType, length).toStructure();
|
||||
}
|
||||
|
|
|
@ -61,9 +61,7 @@ public class Decompiler {
|
|||
private Graph graph;
|
||||
private LoopGraph loopGraph;
|
||||
private GraphIndexer indexer;
|
||||
private int[] loops;
|
||||
private int[] loopSuccessors;
|
||||
private Block[] blockMap;
|
||||
private boolean[] exceptionHandlers;
|
||||
private int lastBlockId;
|
||||
private RangeTree codeTree;
|
||||
|
@ -83,7 +81,6 @@ public class Decompiler {
|
|||
|
||||
static class Block {
|
||||
Block parent;
|
||||
int parentOffset;
|
||||
final IdentifiedStatement statement;
|
||||
final List<Statement> body;
|
||||
final int end;
|
||||
|
@ -100,24 +97,6 @@ public class Decompiler {
|
|||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
void installTo(int index, Block[] blockMap) {
|
||||
if (nodeBackup == null) {
|
||||
nodeToRestore = index;
|
||||
nodeBackup = blockMap[index];
|
||||
} else {
|
||||
nodeToRestore2 = index;
|
||||
nodeBackup2 = blockMap[index];
|
||||
}
|
||||
blockMap[index] = this;
|
||||
}
|
||||
|
||||
void removeFrom(Block[] blockMap) {
|
||||
blockMap[nodeToRestore] = nodeBackup;
|
||||
if (nodeToRestore2 >= 0) {
|
||||
blockMap[nodeToRestore2] = nodeBackup2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TryCatchBookmark {
|
||||
|
@ -207,16 +186,14 @@ public class Decompiler {
|
|||
graph = indexer.getGraph();
|
||||
loopGraph = new LoopGraph(this.graph);
|
||||
unflatCode();
|
||||
blockMap = new Block[program.basicBlockCount() * 2 + 1];
|
||||
stack = new ArrayDeque<>();
|
||||
this.program = program;
|
||||
BlockStatement rootStmt = new BlockStatement();
|
||||
rootStmt.setId("root");
|
||||
stack.push(new Block(rootStmt, rootStmt.getBody(), -1, -1));
|
||||
stack.push(new Block(rootStmt, rootStmt.getBody(), -1, Integer.MAX_VALUE));
|
||||
StatementGenerator generator = new StatementGenerator();
|
||||
generator.classSource = classSource;
|
||||
generator.program = program;
|
||||
generator.blockMap = blockMap;
|
||||
generator.indexer = indexer;
|
||||
parentNode = codeTree.getRoot();
|
||||
currentNode = parentNode.getFirstChild();
|
||||
|
@ -224,18 +201,7 @@ public class Decompiler {
|
|||
fillExceptionHandlers(program);
|
||||
for (int i = 0; i < this.graph.size(); ++i) {
|
||||
int node = i < indexer.size() ? indexer.nodeAt(i) : -1;
|
||||
int next = i + 1;
|
||||
int head = loops[i];
|
||||
if (head != -1 && loopSuccessors[head] == next) {
|
||||
next = head;
|
||||
}
|
||||
|
||||
if (node >= 0) {
|
||||
generator.currentBlock = program.basicBlockAt(node);
|
||||
int tmp = indexer.nodeAt(next);
|
||||
generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null;
|
||||
}
|
||||
|
||||
BasicBlock basicBlock = node >= 0 ? program.basicBlockAt(node) : null;
|
||||
Block block = stack.peek();
|
||||
|
||||
while (parentNode.getEnd() == i) {
|
||||
|
@ -245,16 +211,18 @@ public class Decompiler {
|
|||
for (Block newBlock : createBlocks(i)) {
|
||||
block.body.add(newBlock.statement);
|
||||
newBlock.parent = block;
|
||||
newBlock.parentOffset = block.body.size();
|
||||
stack.push(newBlock);
|
||||
block = newBlock;
|
||||
}
|
||||
createNewBookmarks(generator.currentBlock.getTryCatchBlocks());
|
||||
if (basicBlock != null) {
|
||||
createNewBookmarks(basicBlock.getTryCatchBlocks());
|
||||
}
|
||||
generator.currentBlock = block;
|
||||
|
||||
if (node >= 0) {
|
||||
if (basicBlock != null) {
|
||||
generator.statements.clear();
|
||||
TextLocation lastLocation = null;
|
||||
for (Instruction insn : generator.currentBlock) {
|
||||
for (Instruction insn : basicBlock) {
|
||||
if (insn.getLocation() != null && lastLocation != insn.getLocation()) {
|
||||
lastLocation = insn.getLocation();
|
||||
}
|
||||
|
@ -276,20 +244,17 @@ public class Decompiler {
|
|||
Block oldBlock = block;
|
||||
stack.pop();
|
||||
block = stack.peek();
|
||||
if (block.start >= 0) {
|
||||
int mappedStart = indexer.nodeAt(block.start);
|
||||
if (blockMap[mappedStart] == oldBlock) {
|
||||
blockMap[mappedStart] = block;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < oldBlock.tryCatches.size(); ++j) {
|
||||
for (int j = oldBlock.tryCatches.size() - 1; j >= 0; --j) {
|
||||
TryCatchBookmark bookmark = oldBlock.tryCatches.get(j);
|
||||
TryCatchStatement tryCatchStmt = new TryCatchStatement();
|
||||
tryCatchStmt.setExceptionType(bookmark.exceptionType);
|
||||
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
|
||||
tryCatchStmt.getHandler().add(generator.generateJumpStatement(
|
||||
program.basicBlockAt(bookmark.exceptionHandler)));
|
||||
Statement handlerStatement = generator.generateJumpStatement(block,
|
||||
program.basicBlockAt(bookmark.exceptionHandler));
|
||||
if (handlerStatement != null) {
|
||||
tryCatchStmt.getHandler().add(handlerStatement);
|
||||
}
|
||||
List<Statement> blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size());
|
||||
tryCatchStmt.getProtectedBody().addAll(blockPart);
|
||||
blockPart.clear();
|
||||
|
@ -301,11 +266,16 @@ public class Decompiler {
|
|||
tryCatchBookmarks.subList(tryCatchBookmarks.size() - oldBlock.tryCatches.size(),
|
||||
tryCatchBookmarks.size()).clear();
|
||||
oldBlock.tryCatches.clear();
|
||||
oldBlock.removeFrom(blockMap);
|
||||
}
|
||||
|
||||
if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) {
|
||||
closeExpiredBookmarks(generator, generator.nextBlock.getTryCatchBlocks());
|
||||
if (i < this.graph.size() - 1) {
|
||||
int nextNode = indexer.nodeAt(i + 1);
|
||||
if (nextNode >= 0 && nextNode < program.basicBlockCount()) {
|
||||
BasicBlock nextBlock = program.basicBlockAt(nextNode);
|
||||
if (!isTrivialBlock(nextBlock)) {
|
||||
closeExpiredBookmarks(generator, nextBlock.getTryCatchBlocks());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,37 +334,42 @@ public class Decompiler {
|
|||
removedBookmarks.add(bookmark);
|
||||
}
|
||||
|
||||
Collections.reverse(removedBookmarks);
|
||||
for (TryCatchBookmark bookmark : removedBookmarks) {
|
||||
if (!removedBookmarks.isEmpty()) {
|
||||
Collections.reverse(removedBookmarks);
|
||||
Block block = stack.peek();
|
||||
while (block != bookmark.block) {
|
||||
if (block.body.size() > 1) {
|
||||
|
||||
int lastBookmark = removedBookmarks.size() - 1;
|
||||
boolean first = true;
|
||||
while (lastBookmark >= 0) {
|
||||
for (int j = lastBookmark; j >= 0; --j) {
|
||||
TryCatchBookmark bookmark = removedBookmarks.get(j);
|
||||
|
||||
TryCatchStatement tryCatchStmt = new TryCatchStatement();
|
||||
tryCatchStmt.setExceptionType(bookmark.exceptionType);
|
||||
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
|
||||
tryCatchStmt.getHandler().add(generator.generateJumpStatement(
|
||||
program.basicBlockAt(bookmark.exceptionHandler)));
|
||||
List<Statement> body = block.body.subList(0, block.body.size() - 1);
|
||||
Statement handlerStatement = generator.generateJumpStatement(block,
|
||||
program.basicBlockAt(bookmark.exceptionHandler));
|
||||
if (handlerStatement != null) {
|
||||
tryCatchStmt.getHandler().add(handlerStatement);
|
||||
}
|
||||
|
||||
List<Statement> body = first ? block.body : block.body.subList(0, block.body.size() - 1);
|
||||
if (bookmark.block == block) {
|
||||
body = body.subList(bookmark.offset, body.size());
|
||||
lastBookmark = j - 1;
|
||||
}
|
||||
tryCatchStmt.getProtectedBody().addAll(body);
|
||||
body.clear();
|
||||
body.add(tryCatchStmt);
|
||||
if (!body.isEmpty()) {
|
||||
body.clear();
|
||||
body.add(tryCatchStmt);
|
||||
}
|
||||
}
|
||||
|
||||
block.tryCatches.removeAll(removedBookmarks);
|
||||
|
||||
block = block.parent;
|
||||
first = false;
|
||||
}
|
||||
TryCatchStatement tryCatchStmt = new TryCatchStatement();
|
||||
tryCatchStmt.setExceptionType(bookmark.exceptionType);
|
||||
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
|
||||
Statement jumpToHandler = generator.generateJumpStatement(program.basicBlockAt(bookmark.exceptionHandler));
|
||||
if (jumpToHandler != null) {
|
||||
tryCatchStmt.getHandler().add(jumpToHandler);
|
||||
}
|
||||
List<Statement> blockPart = block.body.subList(bookmark.offset, block.body.size());
|
||||
tryCatchStmt.getProtectedBody().addAll(blockPart);
|
||||
blockPart.clear();
|
||||
if (!tryCatchStmt.getProtectedBody().isEmpty()) {
|
||||
blockPart.add(tryCatchStmt);
|
||||
}
|
||||
block.tryCatches.remove(bookmark);
|
||||
}
|
||||
|
||||
tryCatchBookmarks.subList(start, tryCatchBookmarks.size()).clear();
|
||||
|
@ -402,16 +377,31 @@ public class Decompiler {
|
|||
|
||||
private void createNewBookmarks(List<TryCatchBlock> tryCatchBlocks) {
|
||||
// Add new bookmarks
|
||||
Block previousRoot = null;
|
||||
for (int i = tryCatchBookmarks.size(); i < tryCatchBlocks.size(); ++i) {
|
||||
TryCatchBlock tryCatch = tryCatchBlocks.get(tryCatchBlocks.size() - 1 - i);
|
||||
TryCatchBookmark bookmark = new TryCatchBookmark();
|
||||
bookmark.block = stack.peek();
|
||||
bookmark.offset = bookmark.block.body.size();
|
||||
|
||||
Block block = stack.peek();
|
||||
int offset = block.body.size();
|
||||
|
||||
if (offset == 0 && block != previousRoot) {
|
||||
while (block.parent != previousRoot
|
||||
&& block.parent.start == block.start
|
||||
&& block.end < indexer.indexOf(tryCatch.getHandler().getIndex())
|
||||
&& block.parent.body.size() == 1) {
|
||||
block = block.parent;
|
||||
}
|
||||
}
|
||||
previousRoot = block;
|
||||
|
||||
bookmark.block = block;
|
||||
bookmark.offset = offset;
|
||||
bookmark.exceptionHandler = tryCatch.getHandler().getIndex();
|
||||
bookmark.exceptionType = tryCatch.getExceptionType();
|
||||
bookmark.exceptionVariable = tryCatch.getHandler().getExceptionVariable() != null
|
||||
? tryCatch.getHandler().getExceptionVariable().getIndex() : null;
|
||||
bookmark.block.tryCatches.add(bookmark);
|
||||
block.tryCatches.add(bookmark);
|
||||
tryCatchBookmarks.add(bookmark);
|
||||
}
|
||||
}
|
||||
|
@ -421,26 +411,16 @@ public class Decompiler {
|
|||
while (currentNode != null && currentNode.getStart() == start) {
|
||||
Block block;
|
||||
IdentifiedStatement statement;
|
||||
boolean loop = false;
|
||||
if (loopSuccessors[start] == currentNode.getEnd() || isSingleBlockLoop(start)) {
|
||||
WhileStatement whileStatement = new WhileStatement();
|
||||
statement = whileStatement;
|
||||
block = new Block(statement, whileStatement.getBody(), start, currentNode.getEnd());
|
||||
loop = true;
|
||||
} else {
|
||||
BlockStatement blockStatement = new BlockStatement();
|
||||
statement = blockStatement;
|
||||
block = new Block(statement, blockStatement.getBody(), start, currentNode.getEnd());
|
||||
}
|
||||
result.add(block);
|
||||
int mappedIndex = indexer.nodeAt(currentNode.getEnd());
|
||||
if (mappedIndex >= 0 && (blockMap[mappedIndex] == null
|
||||
|| !(blockMap[mappedIndex].statement instanceof WhileStatement))) {
|
||||
block.installTo(mappedIndex, blockMap);
|
||||
}
|
||||
if (loop) {
|
||||
block.installTo(indexer.nodeAt(start), blockMap);
|
||||
}
|
||||
parentNode = currentNode;
|
||||
currentNode = currentNode.getFirstChild();
|
||||
}
|
||||
|
@ -477,17 +457,6 @@ public class Decompiler {
|
|||
|
||||
// For each node find head of loop this node belongs to.
|
||||
//
|
||||
int[] loops = new int[sz];
|
||||
Arrays.fill(loops, -1);
|
||||
for (int head = 0; head < sz; ++head) {
|
||||
int end = loopSuccessors[head];
|
||||
if (end > sz) {
|
||||
continue;
|
||||
}
|
||||
for (int node = head + 1; node < end; ++node) {
|
||||
loops[node] = head;
|
||||
}
|
||||
}
|
||||
|
||||
List<RangeTree.Range> ranges = new ArrayList<>();
|
||||
for (int node = 0; node < sz; ++node) {
|
||||
|
@ -509,6 +478,5 @@ public class Decompiler {
|
|||
}
|
||||
codeTree = new RangeTree(sz + 1, ranges);
|
||||
this.loopSuccessors = loopSuccessors;
|
||||
this.loops = loops;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.teavm.ast.SwitchStatement;
|
|||
import org.teavm.ast.ThrowStatement;
|
||||
import org.teavm.ast.UnaryOperation;
|
||||
import org.teavm.ast.UnwrapArrayExpr;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.common.GraphIndexer;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
|
@ -95,9 +96,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
private int lastSwitchId;
|
||||
final List<Statement> statements = new ArrayList<>();
|
||||
GraphIndexer indexer;
|
||||
BasicBlock nextBlock;
|
||||
BasicBlock currentBlock;
|
||||
Decompiler.Block[] blockMap;
|
||||
Decompiler.Block currentBlock;
|
||||
Program program;
|
||||
ClassHolderSource classSource;
|
||||
private TextLocation currentLocation;
|
||||
|
@ -536,26 +535,32 @@ class StatementGenerator implements InstructionVisitor {
|
|||
throw new IllegalArgumentException(type.toString());
|
||||
}
|
||||
|
||||
Statement generateJumpStatement(BasicBlock target) {
|
||||
if (nextBlock == target && blockMap[target.getIndex()] == null) {
|
||||
Statement generateJumpStatement(Decompiler.Block sourceBlock, BasicBlock target) {
|
||||
Decompiler.Block targetBlock = getTargetBlock(sourceBlock, target);
|
||||
if (targetBlock == null) {
|
||||
int targetIndex = indexer.indexOf(target.getIndex());
|
||||
if (targetIndex >= sourceBlock.end) {
|
||||
throw new IllegalStateException("Could not find block for basic block $" + target.getIndex());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Decompiler.Block block = blockMap[target.getIndex()];
|
||||
if (block == null) {
|
||||
throw new IllegalStateException("Could not find block for basic block $" + target.getIndex());
|
||||
}
|
||||
if (target.getIndex() == indexer.nodeAt(block.end)) {
|
||||
if (target.getIndex() == indexer.nodeAt(targetBlock.end)) {
|
||||
BreakStatement breakStmt = new BreakStatement();
|
||||
breakStmt.setLocation(currentLocation);
|
||||
breakStmt.setTarget(block.statement);
|
||||
breakStmt.setTarget(targetBlock.statement);
|
||||
return breakStmt;
|
||||
} else {
|
||||
ContinueStatement contStmt = new ContinueStatement();
|
||||
contStmt.setLocation(currentLocation);
|
||||
contStmt.setTarget(block.statement);
|
||||
contStmt.setTarget(targetBlock.statement);
|
||||
return contStmt;
|
||||
}
|
||||
}
|
||||
|
||||
Statement generateJumpStatement(BasicBlock target) {
|
||||
return generateJumpStatement(currentBlock, target);
|
||||
}
|
||||
|
||||
private Statement generateJumpStatement(SwitchStatement stmt, int target) {
|
||||
Statement body = generateJumpStatement(program.basicBlockAt(target));
|
||||
if (body == null) {
|
||||
|
@ -566,6 +571,22 @@ class StatementGenerator implements InstructionVisitor {
|
|||
return body;
|
||||
}
|
||||
|
||||
private Decompiler.Block getTargetBlock(Decompiler.Block source, BasicBlock target) {
|
||||
Decompiler.Block block = source;
|
||||
int targetIndex = indexer.indexOf(target.getIndex());
|
||||
Decompiler.Block candidate = null;
|
||||
while (block != null && (block.start >= targetIndex || block.end <= targetIndex)) {
|
||||
if (block.statement instanceof WhileStatement && block.start == targetIndex) {
|
||||
return block;
|
||||
}
|
||||
if (block.end == targetIndex) {
|
||||
candidate = block;
|
||||
}
|
||||
block = block.parent;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private void branch(Expr condition, BasicBlock consequentBlock, BasicBlock alternativeBlock) {
|
||||
Statement consequent = generateJumpStatement(consequentBlock);
|
||||
Statement alternative = generateJumpStatement(alternativeBlock);
|
||||
|
|
|
@ -20,27 +20,22 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.AbstractStatementVisitor;
|
||||
import org.teavm.ast.BlockStatement;
|
||||
import org.teavm.ast.BreakStatement;
|
||||
import org.teavm.ast.ConditionalStatement;
|
||||
import org.teavm.ast.ContinueStatement;
|
||||
import org.teavm.ast.GotoPartStatement;
|
||||
import org.teavm.ast.IdentifiedStatement;
|
||||
import org.teavm.ast.InitClassStatement;
|
||||
import org.teavm.ast.MonitorEnterStatement;
|
||||
import org.teavm.ast.MonitorExitStatement;
|
||||
import org.teavm.ast.ReturnStatement;
|
||||
import org.teavm.ast.SequentialStatement;
|
||||
import org.teavm.ast.Statement;
|
||||
import org.teavm.ast.StatementVisitor;
|
||||
import org.teavm.ast.SwitchClause;
|
||||
import org.teavm.ast.SwitchStatement;
|
||||
import org.teavm.ast.ThrowStatement;
|
||||
import org.teavm.ast.TryCatchStatement;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
|
||||
class BreakEliminator implements StatementVisitor {
|
||||
class BreakEliminator extends AbstractStatementVisitor {
|
||||
private Map<BlockStatement, List<Statement>> blockSuccessors = new LinkedHashMap<>();
|
||||
private Set<IdentifiedStatement> outerStatements = new LinkedHashSet<>();
|
||||
private List<Statement> currentSequence;
|
||||
|
@ -66,10 +61,6 @@ class BreakEliminator implements StatementVisitor {
|
|||
currentSequence = oldSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SequentialStatement statement) {
|
||||
if (currentSequence == null) {
|
||||
|
@ -145,10 +136,6 @@ class BreakEliminator implements StatementVisitor {
|
|||
currentSequence.subList(currentIndex + 1, currentSequence.size()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InitClassStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(TryCatchStatement statement) {
|
||||
Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors;
|
||||
|
@ -161,18 +148,6 @@ class BreakEliminator implements StatementVisitor {
|
|||
processSequence(statement.getHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(GotoPartStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorEnterStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitStatement statement) {
|
||||
}
|
||||
|
||||
private boolean escapes(List<Statement> statements) {
|
||||
return new EscapingStatementFinder(usageCounter).check(statements);
|
||||
}
|
||||
|
|
|
@ -157,6 +157,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private boolean incremental;
|
||||
private boolean lineNumbersGenerated;
|
||||
private SimpleStringPool stringPool;
|
||||
private boolean longjmpUsed = true;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
|
@ -170,6 +172,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
this.lineNumbersGenerated = lineNumbersGenerated;
|
||||
}
|
||||
|
||||
public void setLongjmpUsed(boolean longjmpUsed) {
|
||||
this.longjmpUsed = longjmpUsed;
|
||||
}
|
||||
|
||||
public void setAstCache(MethodNodeCache astCache) {
|
||||
this.astCache = astCache;
|
||||
}
|
||||
|
@ -195,7 +201,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
|
||||
nullCheckInsertion = new NullCheckInsertion(characteristics);
|
||||
nullCheckTransformation = new NullCheckTransformation();
|
||||
|
||||
|
@ -302,12 +308,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
public void afterOptimizations(Program program, MethodReader method) {
|
||||
classInitializerEliminator.apply(program);
|
||||
classInitializerTransformer.transform(program);
|
||||
nullCheckTransformation.apply(program, method.getResultType());
|
||||
if (!longjmpUsed) {
|
||||
nullCheckTransformation.apply(program, method.getResultType());
|
||||
}
|
||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
|
||||
.apply(program, method.getReference());
|
||||
ShadowStackTransformer shadowStackTransformer = !incremental
|
||||
? this.shadowStackTransformer
|
||||
: new ShadowStackTransformer(characteristics);
|
||||
: new ShadowStackTransformer(characteristics, !longjmpUsed);
|
||||
shadowStackTransformer.apply(program, method);
|
||||
}
|
||||
|
||||
|
@ -350,7 +358,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
stringPool = new SimpleStringPool();
|
||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget, incremental);
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget, incremental, longjmpUsed);
|
||||
|
||||
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false);
|
||||
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false);
|
||||
|
@ -358,13 +366,19 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
runtimeHeaderWriter.println("#pragma once");
|
||||
if (incremental) {
|
||||
runtimeHeaderWriter.println("#define TEAVM_INCREMENTAL true");
|
||||
runtimeHeaderWriter.println("#define TEAVM_INCREMENTAL 1");
|
||||
}
|
||||
if (longjmpUsed) {
|
||||
runtimeHeaderWriter.println("#define TEAVM_USE_SETJMP 1");
|
||||
}
|
||||
emitResource(runtimeHeaderWriter, "runtime.h");
|
||||
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
|
||||
controller.getCacheStatus());
|
||||
classGenerator.setAstCache(astCache);
|
||||
if (context.isLongjmp() && !context.isIncremental()) {
|
||||
classGenerator.setCallSites(callSites);
|
||||
}
|
||||
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
|
||||
controller.getUnprocessedClassSource(), controller.getClassLoader(), controller.getServices(),
|
||||
controller.getProperties());
|
||||
|
@ -515,14 +529,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)");
|
||||
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites")
|
||||
.generate(CallSiteDescriptor.extract(context.getClassSource(), classNames));
|
||||
List<? extends CallSiteDescriptor> callSites = context.isLongjmp()
|
||||
? this.callSites
|
||||
: CallSiteDescriptor.extract(context.getClassSource(), classNames);
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites);
|
||||
}
|
||||
|
||||
private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) {
|
||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (((" + callSiteName
|
||||
+ "*) ((void**) frame)[3]) + id)");
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) ((" + callSiteName
|
||||
+ "*) ((TeaVM_StackFrame*) (frame))->callSites + id)");
|
||||
}
|
||||
|
||||
private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException {
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* Copyright 2019 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.backend.c.analyze;
|
||||
|
||||
import com.carrotsearch.hppc.IntArrayList;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntIntHashMap;
|
||||
import com.carrotsearch.hppc.IntIntMap;
|
||||
import com.carrotsearch.hppc.IntObjectHashMap;
|
||||
import com.carrotsearch.hppc.IntObjectMap;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.IntStack;
|
||||
import com.carrotsearch.hppc.LongHashSet;
|
||||
import com.carrotsearch.hppc.LongSet;
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.BlockStatement;
|
||||
import org.teavm.ast.BreakStatement;
|
||||
import org.teavm.ast.ConditionalStatement;
|
||||
import org.teavm.ast.ContinueStatement;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.IdentifiedStatement;
|
||||
import org.teavm.ast.RecursiveVisitor;
|
||||
import org.teavm.ast.ReturnStatement;
|
||||
import org.teavm.ast.Statement;
|
||||
import org.teavm.ast.SwitchClause;
|
||||
import org.teavm.ast.SwitchStatement;
|
||||
import org.teavm.ast.ThrowStatement;
|
||||
import org.teavm.ast.TryCatchStatement;
|
||||
import org.teavm.ast.VariableExpr;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphBuilder;
|
||||
|
||||
public class AstDefinitionUsageAnalysis {
|
||||
private GraphBuilder graphBuilder = new GraphBuilder();
|
||||
private GraphBuilder exceptionGraphBuilder = new GraphBuilder();
|
||||
private Graph cfg;
|
||||
private Graph exceptionGraph;
|
||||
private List<Node> nodes = new ArrayList<>();
|
||||
private int lastNode = -1;
|
||||
private IdentifiedStatement defaultBreakTarget;
|
||||
private IdentifiedStatement defaultContinueTarget;
|
||||
private ObjectIntMap<IdentifiedStatement> breakTargets = new ObjectIntHashMap<>();
|
||||
private ObjectIntMap<IdentifiedStatement> continueTargets = new ObjectIntHashMap<>();
|
||||
private List<Definition> definitions = new ArrayList<>();
|
||||
private List<? extends Definition> readonlyDefinitions = Collections.unmodifiableList(definitions);
|
||||
private ObjectIntMap<Definition> definitionIds = new ObjectIntHashMap<>();
|
||||
private List<VariableExpr> usages = new ArrayList<>();
|
||||
private IntStack stack = new IntStack();
|
||||
private IntStack exceptionHandlerStack = new IntStack();
|
||||
|
||||
public List<? extends Definition> getDefinitions() {
|
||||
return readonlyDefinitions;
|
||||
}
|
||||
|
||||
public void analyze(Statement statement) {
|
||||
prepare(statement);
|
||||
propagate();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private void prepare(Statement statement) {
|
||||
lastNode = createNode();
|
||||
statement.acceptVisitor(visitor);
|
||||
cfg = graphBuilder.build();
|
||||
exceptionGraph = exceptionGraphBuilder.build();
|
||||
graphBuilder = null;
|
||||
exceptionGraphBuilder = null;
|
||||
breakTargets = null;
|
||||
continueTargets = null;
|
||||
exceptionHandlerStack = null;
|
||||
}
|
||||
|
||||
private void connect(int from, int to) {
|
||||
if (from >= 0 && to >= 0) {
|
||||
graphBuilder.addEdge(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
private int createNode() {
|
||||
int id = nodes.size();
|
||||
nodes.add(new Node());
|
||||
for (IntCursor cursor : exceptionHandlerStack) {
|
||||
exceptionGraphBuilder.addEdge(id, cursor.value);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private void propagate() {
|
||||
while (!stack.isEmpty()) {
|
||||
int exceptionHandlerId = stack.pop();
|
||||
int nodeId = stack.pop();
|
||||
int usageId = stack.pop();
|
||||
int variableId = usages.get(usageId).getIndex();
|
||||
|
||||
Node node = nodes.get(nodeId);
|
||||
if (exceptionHandlerId < 0) {
|
||||
if (!node.usages.add(usageId)) {
|
||||
continue;
|
||||
}
|
||||
int definitionId = node.definitions.getOrDefault(variableId, -1);
|
||||
if (definitionId >= 0) {
|
||||
Definition definition = definitions.get(definitionId);
|
||||
definition.usages.add(usages.get(usageId));
|
||||
continue;
|
||||
}
|
||||
if (variableId == node.exceptionVariable) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!node.handlerUsages.add(pack(usageId, exceptionHandlerId))) {
|
||||
continue;
|
||||
}
|
||||
IntArrayList definitionIds = node.handlerDefinitions.get(variableId);
|
||||
if (definitionIds != null) {
|
||||
TryCatchStatement handlerStatement = nodes.get(exceptionHandlerId).catchStatement;
|
||||
Expr usage = usages.get(usageId);
|
||||
for (IntCursor cursor : definitionIds) {
|
||||
Definition definition = definitions.get(cursor.value);
|
||||
definition.usages.add(usages.get(usageId));
|
||||
Set<Expr> handlerUsages = definition.exceptionHandlingUsages.get(handlerStatement);
|
||||
if (handlerUsages == null) {
|
||||
handlerUsages = new LinkedHashSet<>();
|
||||
definition.exceptionHandlingUsages.put(handlerStatement, handlerUsages);
|
||||
}
|
||||
handlerUsages.add(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeId < cfg.size()) {
|
||||
for (int predecessorId : cfg.incomingEdges(nodeId)) {
|
||||
if (!nodes.get(predecessorId).usages.contains(usageId)) {
|
||||
stack.push(usageId);
|
||||
stack.push(predecessorId);
|
||||
stack.push(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeId < exceptionGraph.size()) {
|
||||
for (int predecessorId : exceptionGraph.incomingEdges(nodeId)) {
|
||||
if (!nodes.get(predecessorId).handlerUsages.contains(usageId)) {
|
||||
stack.push(usageId);
|
||||
stack.push(predecessorId);
|
||||
stack.push(nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
cfg = null;
|
||||
stack = null;
|
||||
}
|
||||
|
||||
private RecursiveVisitor visitor = new RecursiveVisitor() {
|
||||
@Override
|
||||
public void visit(ConditionalStatement statement) {
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
int forkNode = createNode();
|
||||
int joinNode = createNode();
|
||||
|
||||
connect(lastNode, forkNode);
|
||||
lastNode = forkNode;
|
||||
visit(statement.getConsequent());
|
||||
connect(lastNode, joinNode);
|
||||
|
||||
lastNode = forkNode;
|
||||
visit(statement.getAlternative());
|
||||
connect(lastNode, joinNode);
|
||||
|
||||
lastNode = joinNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SwitchStatement statement) {
|
||||
IdentifiedStatement oldDefaultBreakTarget = defaultBreakTarget;
|
||||
defaultBreakTarget = statement;
|
||||
|
||||
statement.getValue().acceptVisitor(this);
|
||||
int forkNode = createNode();
|
||||
int joinNode = createNode();
|
||||
|
||||
connect(lastNode, forkNode);
|
||||
for (SwitchClause clause : statement.getClauses()) {
|
||||
lastNode = forkNode;
|
||||
visit(clause.getBody());
|
||||
connect(lastNode, joinNode);
|
||||
}
|
||||
|
||||
lastNode = forkNode;
|
||||
visit(statement.getDefaultClause());
|
||||
connect(lastNode, joinNode);
|
||||
|
||||
lastNode = joinNode;
|
||||
defaultBreakTarget = oldDefaultBreakTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WhileStatement statement) {
|
||||
IdentifiedStatement oldDefaultBreakTarget = defaultBreakTarget;
|
||||
IdentifiedStatement oldDefaultContinueTarget = defaultContinueTarget;
|
||||
defaultBreakTarget = statement;
|
||||
defaultContinueTarget = statement;
|
||||
|
||||
int continueNode = createNode();
|
||||
int forkNode = createNode();
|
||||
int breakNode = createNode();
|
||||
breakTargets.put(statement, breakNode);
|
||||
continueTargets.put(statement, continueNode);
|
||||
|
||||
connect(lastNode, continueNode);
|
||||
lastNode = continueNode;
|
||||
if (statement.getCondition() != null) {
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
}
|
||||
|
||||
connect(lastNode, forkNode);
|
||||
connect(forkNode, breakNode);
|
||||
lastNode = forkNode;
|
||||
visit(statement.getBody());
|
||||
connect(lastNode, continueNode);
|
||||
lastNode = breakNode;
|
||||
|
||||
breakTargets.remove(statement);
|
||||
continueTargets.remove(statement);
|
||||
defaultBreakTarget = oldDefaultBreakTarget;
|
||||
defaultContinueTarget = oldDefaultContinueTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BlockStatement statement) {
|
||||
int breakNode = createNode();
|
||||
breakTargets.put(statement, breakNode);
|
||||
visit(statement.getBody());
|
||||
connect(lastNode, breakNode);
|
||||
lastNode = breakNode;
|
||||
breakTargets.remove(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(TryCatchStatement statement) {
|
||||
int handlerNode = createNode();
|
||||
Node handlerNodeData = nodes.get(handlerNode);
|
||||
handlerNodeData.catchStatement = statement;
|
||||
if (statement.getExceptionVariable() != null) {
|
||||
handlerNodeData.exceptionVariable = statement.getExceptionVariable();
|
||||
}
|
||||
exceptionHandlerStack.push(handlerNode);
|
||||
visit(statement.getProtectedBody());
|
||||
exceptionHandlerStack.pop();
|
||||
|
||||
int node = lastNode;
|
||||
lastNode = handlerNode;
|
||||
visit(statement.getHandler());
|
||||
connect(lastNode, node);
|
||||
|
||||
lastNode = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ReturnStatement statement) {
|
||||
super.visit(statement);
|
||||
lastNode = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ThrowStatement statement) {
|
||||
super.visit(statement);
|
||||
lastNode = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BreakStatement statement) {
|
||||
IdentifiedStatement target = statement.getTarget();
|
||||
if (target == null) {
|
||||
target = defaultBreakTarget;
|
||||
}
|
||||
connect(lastNode, breakTargets.getOrDefault(target, -1));
|
||||
lastNode = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ContinueStatement statement) {
|
||||
IdentifiedStatement target = statement.getTarget();
|
||||
if (target == null) {
|
||||
target = defaultContinueTarget;
|
||||
}
|
||||
connect(lastNode, continueTargets.getOrDefault(target, -1));
|
||||
lastNode = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
if (!processAssignment(statement)) {
|
||||
super.visit(statement);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processAssignment(AssignmentStatement statement) {
|
||||
if (!(statement.getLeftValue() instanceof VariableExpr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int leftIndex = ((VariableExpr) statement.getLeftValue()).getIndex();
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
Definition definition = new Definition(statement, definitions.size(), leftIndex);
|
||||
definitions.add(definition);
|
||||
definitionIds.put(definition, definition.id);
|
||||
if (lastNode >= 0) {
|
||||
Node node = nodes.get(lastNode);
|
||||
node.definitions.put(definition.variableIndex, definition.id);
|
||||
|
||||
IntArrayList handlerDefinitions = node.handlerDefinitions.get(leftIndex);
|
||||
if (handlerDefinitions == null) {
|
||||
handlerDefinitions = new IntArrayList();
|
||||
node.handlerDefinitions.put(leftIndex, handlerDefinitions);
|
||||
}
|
||||
handlerDefinitions.add(definition.id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(VariableExpr expr) {
|
||||
if (lastNode < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node node = nodes.get(lastNode);
|
||||
int definitionId = node.definitions.getOrDefault(expr.getIndex(), -1);
|
||||
if (definitionId >= 0) {
|
||||
Definition definition = definitions.get(definitionId);
|
||||
definition.usages.add(expr);
|
||||
} else if (node.exceptionVariable != expr.getIndex()) {
|
||||
int id = usages.size();
|
||||
usages.add(expr);
|
||||
node.usages.add(id);
|
||||
stack.push(id);
|
||||
stack.push(lastNode);
|
||||
stack.push(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static class Node {
|
||||
IntIntMap definitions = new IntIntHashMap();
|
||||
IntObjectMap<IntArrayList> handlerDefinitions = new IntObjectHashMap<>();
|
||||
IntSet usages = new IntHashSet();
|
||||
LongSet handlerUsages = new LongHashSet();
|
||||
TryCatchStatement catchStatement;
|
||||
int exceptionVariable = -1;
|
||||
}
|
||||
|
||||
private static long pack(int a, int b) {
|
||||
return ((long) a << 32) | b;
|
||||
}
|
||||
|
||||
public static class Definition {
|
||||
private AssignmentStatement statement;
|
||||
private int id;
|
||||
private int variableIndex;
|
||||
final Set<Expr> usages = new LinkedHashSet<>();
|
||||
private Set<? extends Expr> readonlyUsages = Collections.unmodifiableSet(usages);
|
||||
final Map<TryCatchStatement, Set<Expr>> exceptionHandlingUsages = new LinkedHashMap<>();
|
||||
private Map<? extends TryCatchStatement, Set<? extends Expr>> readonlyExceptionHandlingUsages =
|
||||
Collections.unmodifiableMap(exceptionHandlingUsages);
|
||||
|
||||
Definition(AssignmentStatement statement, int id, int variableIndex) {
|
||||
this.statement = statement;
|
||||
this.id = id;
|
||||
this.variableIndex = variableIndex;
|
||||
}
|
||||
|
||||
public AssignmentStatement getStatement() {
|
||||
return statement;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getVariableIndex() {
|
||||
return variableIndex;
|
||||
}
|
||||
|
||||
public Set<? extends Expr> getUsages() {
|
||||
return readonlyUsages;
|
||||
}
|
||||
|
||||
public Map<? extends TryCatchStatement, Set<? extends Expr>> getExceptionHandlingUsages() {
|
||||
return readonlyExceptionHandlingUsages;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2019 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.backend.c.analyze;
|
||||
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.RecursiveVisitor;
|
||||
import org.teavm.ast.Statement;
|
||||
import org.teavm.ast.TryCatchStatement;
|
||||
import org.teavm.ast.VariableExpr;
|
||||
|
||||
public class VolatileDefinitionFinder {
|
||||
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
private Map<AssignmentStatement, StackElement> defHandlers = new HashMap<>();
|
||||
private Set<AssignmentStatement> definitionsToBackup = new HashSet<>();
|
||||
private Map<TryCatchStatement, IntSet> usagesToRestoreByHandler = new HashMap<>();
|
||||
|
||||
public void findVolatileDefinitions(Statement statement) {
|
||||
AstDefinitionUsageAnalysis defuse = new AstDefinitionUsageAnalysis();
|
||||
defuse.analyze(statement);
|
||||
statement.acceptVisitor(handlerAnalyzer);
|
||||
|
||||
for (AstDefinitionUsageAnalysis.Definition definition : defuse.getDefinitions()) {
|
||||
StackElement stack = defHandlers.get(definition.getStatement());
|
||||
if (stack == null || definition.getExceptionHandlingUsages().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (stack != null) {
|
||||
if (definition.getExceptionHandlingUsages().get(stack.statement) != null) {
|
||||
IntSet usagesToRestore = usagesToRestoreByHandler.get(stack.statement);
|
||||
if (usagesToRestore == null) {
|
||||
usagesToRestore = new IntHashSet();
|
||||
usagesToRestoreByHandler.put(stack.statement, usagesToRestore);
|
||||
}
|
||||
usagesToRestore.add(definition.getVariableIndex());
|
||||
definitionsToBackup.add(definition.getStatement());
|
||||
}
|
||||
stack = stack.next;
|
||||
}
|
||||
}
|
||||
|
||||
defHandlers = null;
|
||||
}
|
||||
|
||||
public boolean shouldBackup(AssignmentStatement statement) {
|
||||
return definitionsToBackup.contains(statement);
|
||||
}
|
||||
|
||||
public int[] variablesToRestore(TryCatchStatement tryCatch) {
|
||||
IntSet result = usagesToRestoreByHandler.get(tryCatch);
|
||||
if (result == null) {
|
||||
return EMPTY_INT_ARRAY;
|
||||
}
|
||||
int[] array = result.toArray();
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
static class StackElement {
|
||||
final TryCatchStatement statement;
|
||||
final StackElement next;
|
||||
|
||||
StackElement(TryCatchStatement statement, StackElement next) {
|
||||
this.statement = statement;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
|
||||
private RecursiveVisitor handlerAnalyzer = new RecursiveVisitor() {
|
||||
StackElement surroundingTryCatches;
|
||||
StackElement handlingTryCatches;
|
||||
|
||||
@Override
|
||||
public void visit(TryCatchStatement statement) {
|
||||
surroundingTryCatches = new StackElement(statement, surroundingTryCatches);
|
||||
visit(statement.getProtectedBody());
|
||||
surroundingTryCatches = surroundingTryCatches.next;
|
||||
|
||||
visit(statement.getHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
super.visit(statement);
|
||||
if (statement.getLeftValue() instanceof VariableExpr && surroundingTryCatches != null) {
|
||||
defHandlers.put(statement, surroundingTryCatches);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.c.generate;
|
|||
import java.io.IOException;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -93,10 +94,12 @@ public class ClassGenerator {
|
|||
private CodeWriter codeWriter;
|
||||
private CodeWriter initWriter;
|
||||
private CodeWriter headerWriter;
|
||||
private CodeWriter callSitesWriter;
|
||||
private IncludeManager includes;
|
||||
private IncludeManager headerIncludes;
|
||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
|
||||
private List<CallSiteDescriptor> callSites;
|
||||
|
||||
public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler,
|
||||
CacheStatus cacheStatus) {
|
||||
|
@ -110,6 +113,10 @@ public class ClassGenerator {
|
|||
this.astCache = astCache;
|
||||
}
|
||||
|
||||
public void setCallSites(List<CallSiteDescriptor> callSites) {
|
||||
this.callSites = callSites;
|
||||
}
|
||||
|
||||
public void prepare(ListableClassHolderSource classes) {
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
|
@ -196,7 +203,7 @@ public class ClassGenerator {
|
|||
}
|
||||
|
||||
private void generateCallSites(List<? extends CallSiteDescriptor> callSites, String callSitesName) {
|
||||
CallSiteGenerator generator = new CallSiteGenerator(context, codeWriter, includes, callSitesName);
|
||||
CallSiteGenerator generator = new CallSiteGenerator(context, callSitesWriter, includes, callSitesName);
|
||||
generator.setStatic(true);
|
||||
generator.generate(callSites);
|
||||
}
|
||||
|
@ -224,6 +231,9 @@ public class ClassGenerator {
|
|||
headerIncludes.includePath("runtime.h");
|
||||
|
||||
codeGenerator = new CodeGenerator(context, codeWriter, includes);
|
||||
if (context.isLongjmp() && !context.isIncremental()) {
|
||||
codeGenerator.setCallSites(callSites);
|
||||
}
|
||||
|
||||
String sysInitializerName = context.getNames().forClassSystemInitializer(type);
|
||||
headerWriter.println("extern void " + sysInitializerName + "();");
|
||||
|
@ -279,17 +289,7 @@ public class ClassGenerator {
|
|||
}
|
||||
|
||||
if (context.isIncremental()) {
|
||||
String callSitesName;
|
||||
List<? extends CallSiteDescriptor> callSites = CallSiteDescriptor.extract(method.getProgram());
|
||||
if (!callSites.isEmpty()) {
|
||||
callSitesName = "callsites_" + context.getNames().forMethod(method.getReference());
|
||||
includes.includeClass(CallSite.class.getName());
|
||||
generateCallSites(callSites, callSitesName);
|
||||
} else {
|
||||
callSitesName = "NULL";
|
||||
}
|
||||
codeWriter.println("#define TEAVM_ALLOC_STACK(size) TEAVM_ALLOC_STACK_DEF(size, "
|
||||
+ callSitesName + ")");
|
||||
callSitesWriter = codeWriter.fragment();
|
||||
}
|
||||
|
||||
generateMethodForwardDeclaration(method);
|
||||
|
@ -303,9 +303,19 @@ public class ClassGenerator {
|
|||
methodNode = entry.method;
|
||||
}
|
||||
|
||||
List<CallSiteDescriptor> callSites = null;
|
||||
if (context.isLongjmp()) {
|
||||
if (context.isIncremental()) {
|
||||
callSites = new ArrayList<>();
|
||||
codeGenerator.setCallSites(callSites);
|
||||
}
|
||||
}
|
||||
|
||||
codeGenerator.generateMethod(methodNode);
|
||||
|
||||
if (context.isIncremental()) {
|
||||
generateCallSites(method.getReference(),
|
||||
context.isLongjmp() ? callSites : CallSiteDescriptor.extract(method.getProgram()));
|
||||
codeWriter.println("#undef TEAVM_ALLOC_STACK");
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +328,19 @@ public class ClassGenerator {
|
|||
headerWriter.println(";");
|
||||
}
|
||||
|
||||
private void generateCallSites(MethodReference method, List<? extends CallSiteDescriptor> callSites) {
|
||||
String callSitesName;
|
||||
if (!callSites.isEmpty()) {
|
||||
callSitesName = "callsites_" + context.getNames().forMethod(method);
|
||||
includes.includeClass(CallSite.class.getName());
|
||||
generateCallSites(callSites, callSitesName);
|
||||
} else {
|
||||
callSitesName = "NULL";
|
||||
}
|
||||
callSitesWriter.println("#define TEAVM_ALLOC_STACK(size) TEAVM_ALLOC_STACK_DEF(size, "
|
||||
+ callSitesName + ")");
|
||||
}
|
||||
|
||||
private void generateInitializer(ClassHolder cls) {
|
||||
if (!needsInitializer(cls)) {
|
||||
return;
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import static org.teavm.model.lowlevel.ExceptionHandlingShadowStackContributor.isManagedMethodCall;
|
||||
import com.carrotsearch.hppc.IntContainer;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
|
@ -24,10 +30,13 @@ import java.nio.LongBuffer;
|
|||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ArrayType;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.BinaryExpr;
|
||||
|
@ -41,6 +50,7 @@ import org.teavm.ast.ContinueStatement;
|
|||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.ExprVisitor;
|
||||
import org.teavm.ast.GotoPartStatement;
|
||||
import org.teavm.ast.IdentifiedStatement;
|
||||
import org.teavm.ast.InitClassStatement;
|
||||
import org.teavm.ast.InstanceOfExpr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
|
@ -65,6 +75,7 @@ import org.teavm.ast.UnaryExpr;
|
|||
import org.teavm.ast.UnwrapArrayExpr;
|
||||
import org.teavm.ast.VariableExpr;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.backend.c.analyze.VolatileDefinitionFinder;
|
||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
||||
import org.teavm.backend.c.util.InteropUtil;
|
||||
|
@ -83,9 +94,11 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.VirtualTable;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.Fiber;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
@ -107,19 +120,31 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
Object.class, void.class);
|
||||
private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, "monitorExitSync",
|
||||
Object.class, void.class);
|
||||
private static final MethodReference CATCH_EXCEPTION = new MethodReference(ExceptionHandling.class,
|
||||
"catchException", Throwable.class);
|
||||
|
||||
private static final Map<String, String> BUFFER_TYPES = new HashMap<>();
|
||||
|
||||
private GenerationContext context;
|
||||
private NameProvider names;
|
||||
private CodeWriter writer;
|
||||
private VolatileDefinitionFinder volatileDefinitions;
|
||||
private int[] temporaryVariableLevel = new int[5];
|
||||
private IntSet spilledVariables = new IntHashSet();
|
||||
private int[] maxTemporaryVariableLevel = new int[5];
|
||||
private MethodReference callingMethod;
|
||||
private IncludeManager includes;
|
||||
private boolean end;
|
||||
private boolean async;
|
||||
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
|
||||
private List<CallSiteDescriptor> callSites;
|
||||
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
|
||||
private boolean managed;
|
||||
private IdentifiedStatement defaultBreakTarget;
|
||||
private IdentifiedStatement defaultContinueTarget;
|
||||
private ObjectIntMap<IdentifiedStatement> labelMap = new ObjectIntHashMap<>();
|
||||
private Set<IdentifiedStatement> usedAsBreakTarget = new HashSet<>();
|
||||
private Set<IdentifiedStatement> usedAsContinueTarget = new HashSet<>();
|
||||
|
||||
static {
|
||||
BUFFER_TYPES.put(ByteBuffer.class.getName(), "int8_t");
|
||||
|
@ -131,11 +156,14 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
BUFFER_TYPES.put(DoubleBuffer.class.getName(), "double");
|
||||
}
|
||||
|
||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer, IncludeManager includes) {
|
||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
List<CallSiteDescriptor> callSites, VolatileDefinitionFinder volatileDefinitions) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.names = context.getNames();
|
||||
this.includes = includes;
|
||||
this.callSites = callSites;
|
||||
this.volatileDefinitions = volatileDefinitions;
|
||||
}
|
||||
|
||||
public void setAsync(boolean async) {
|
||||
|
@ -146,8 +174,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
return maxTemporaryVariableLevel;
|
||||
}
|
||||
|
||||
public IntContainer getSpilledVariables() {
|
||||
return spilledVariables;
|
||||
}
|
||||
|
||||
public void setCallingMethod(MethodReference callingMethod) {
|
||||
this.callingMethod = callingMethod;
|
||||
this.managed = context.getCharacteristics().isManaged(callingMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -308,7 +341,9 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
writer.print(")");
|
||||
break;
|
||||
case NULL_CHECK:
|
||||
writer.print("teavm_nullCheck(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case INT_TO_BYTE:
|
||||
writer.print("TEAVM_TO_BYTE(");
|
||||
|
@ -405,6 +440,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean needsCallSiteId() {
|
||||
return context.isLongjmp() && managed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
ClassReader cls = context.getClassSource().get(expr.getMethod().getClassName());
|
||||
|
@ -416,15 +455,30 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
boolean needParenthesis = false;
|
||||
|
||||
Intrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
||||
if (intrinsic != null) {
|
||||
pushLocation(expr.getLocation());
|
||||
if (needsCallSiteId() && isManagedMethodCall(context.getCharacteristics(), expr.getMethod())) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
intrinsic.apply(intrinsicContext, expr);
|
||||
popLocation(expr.getLocation());
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pushLocation(expr.getLocation());
|
||||
|
||||
if (needsCallSiteId() && context.getCharacteristics().isManaged(expr.getMethod())) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
switch (expr.getType()) {
|
||||
case CONSTRUCTOR:
|
||||
generateCallToConstructor(expr.getMethod(), expr.getArguments());
|
||||
|
@ -441,9 +495,34 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
popLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
private void withCallSite() {
|
||||
LocationStackEntry locationEntry = locationStack.peek();
|
||||
TextLocation location = locationEntry != null ? locationEntry.location : null;
|
||||
String fileName = location != null ? location.getFileName() : null;
|
||||
if (fileName != null) {
|
||||
fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
|
||||
}
|
||||
CallSiteLocation callSiteLocation = new CallSiteLocation(
|
||||
fileName,
|
||||
callingMethod.getClassName(),
|
||||
callingMethod.getName(),
|
||||
location != null ? location.getLine() : 0);
|
||||
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), callSiteLocation);
|
||||
List<ExceptionHandlerDescriptor> reverseHandlers = new ArrayList<>(handlers);
|
||||
Collections.reverse(reverseHandlers);
|
||||
callSite.getHandlers().addAll(reverseHandlers);
|
||||
callSites.add(callSite);
|
||||
|
||||
writer.print("TEAVM_WITH_CALL_SITE_ID(").print(String.valueOf(callSite.getId())).print(", ");
|
||||
}
|
||||
|
||||
private void generateCallToConstructor(MethodReference reference, List<? extends Expr> arguments) {
|
||||
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
||||
writer.print("(" + receiver + " = ");
|
||||
|
@ -751,9 +830,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
@Override
|
||||
public void visit(NewExpr expr) {
|
||||
pushLocation(expr.getLocation());
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
includes.includeClass(expr.getConstructedClass());
|
||||
includes.includeClass(ALLOC_METHOD.getClassName());
|
||||
allocObject(expr.getConstructedClass());
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
popLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
|
@ -766,6 +853,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
@Override
|
||||
public void visit(NewArrayExpr expr) {
|
||||
pushLocation(expr.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
ValueType type = ValueType.arrayOf(expr.getType());
|
||||
writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(type)).print(", ");
|
||||
|
@ -773,11 +867,24 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
includes.includeType(type);
|
||||
expr.getLength().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
popLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NewMultiArrayExpr expr) {
|
||||
pushLocation(expr.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
writer.print(names.forMethod(ALLOC_MULTI_ARRAY_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(expr.getType())).print(", ");
|
||||
includes.includeClass(ALLOC_ARRAY_METHOD.getClassName());
|
||||
|
@ -791,6 +898,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
|
||||
writer.print("}, ").print(String.valueOf(expr.getDimensions().size())).print(")");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
popLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -813,11 +926,24 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pushLocation(expr.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
writer.print("teavm_checkcast(");
|
||||
expr.getValue().acceptVisitor(this);
|
||||
includes.includeType(expr.getTarget());
|
||||
writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
popLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
|
@ -848,6 +974,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
|
||||
if (statement.getLeftValue() != null) {
|
||||
if (statement.getLeftValue() instanceof QualificationExpr) {
|
||||
QualificationExpr qualification = (QualificationExpr) statement.getLeftValue();
|
||||
|
@ -871,8 +998,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
statement.getRightValue().acceptVisitor(this);
|
||||
writer.println(";");
|
||||
|
||||
if (statement.isAsync()) {
|
||||
emitSuspendChecker();
|
||||
if (volatileDefinitions.shouldBackup(statement)) {
|
||||
VariableExpr lhs = (VariableExpr) statement.getLeftValue();
|
||||
spilledVariables.add(lhs.getIndex());
|
||||
writer.println("teavm_spill_" + lhs.getIndex() + " = " + getVariableName(lhs.getIndex()) + ";");
|
||||
}
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
|
@ -929,6 +1058,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(SwitchStatement statement) {
|
||||
IdentifiedStatement oldDefaultBreakTarget = defaultBreakTarget;
|
||||
defaultBreakTarget = statement;
|
||||
|
||||
int statementId = labelMap.size() + 1;
|
||||
labelMap.put(statement, statementId);
|
||||
|
||||
pushLocation(statement.getValue().getLocation());
|
||||
writer.print("switch (");
|
||||
statement.getValue().acceptVisitor(this);
|
||||
|
@ -958,13 +1093,23 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
|
||||
writer.outdent().println("}");
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent();
|
||||
if (usedAsBreakTarget.contains(statement)) {
|
||||
writer.outdent().println("teavm_label_" + statementId + ":;").indent();
|
||||
}
|
||||
|
||||
defaultBreakTarget = oldDefaultBreakTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WhileStatement statement) {
|
||||
IdentifiedStatement oldDefaultBreakTarget = defaultBreakTarget;
|
||||
IdentifiedStatement oldDefaultContinueTarget = defaultContinueTarget;
|
||||
defaultBreakTarget = statement;
|
||||
defaultContinueTarget = statement;
|
||||
|
||||
int statementId = labelMap.size() + 1;
|
||||
labelMap.put(statement, statementId);
|
||||
|
||||
writer.print("while (");
|
||||
if (statement.getCondition() != null) {
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
|
@ -980,44 +1125,54 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
end = oldEnd;
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("teavm_cnt_" + statement.getId() + ":;").indent();
|
||||
if (usedAsContinueTarget.contains(statement)) {
|
||||
writer.outdent().println("teavm_cnt_" + statementId + ":;").indent();
|
||||
}
|
||||
writer.outdent().println("}");
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent();
|
||||
if (usedAsBreakTarget.contains(statement)) {
|
||||
writer.outdent().println("teavm_label_" + statementId + ":;").indent();
|
||||
}
|
||||
|
||||
defaultContinueTarget = oldDefaultContinueTarget;
|
||||
defaultBreakTarget = oldDefaultBreakTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BlockStatement statement) {
|
||||
int statementId = labelMap.size() + 1;
|
||||
labelMap.put(statement, statementId);
|
||||
|
||||
visitMany(statement.getBody());
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent();
|
||||
if (usedAsBreakTarget.contains(statement)) {
|
||||
writer.outdent().println("teavm_label_" + statementId + ":;").indent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BreakStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
if (statement.getTarget() == null || statement.getTarget().getId() == null) {
|
||||
writer.println("break;");
|
||||
} else {
|
||||
writer.println("goto teavm_label_" + statement.getTarget().getId() + ";");
|
||||
IdentifiedStatement target = statement.getTarget();
|
||||
if (target == null) {
|
||||
target = defaultBreakTarget;
|
||||
}
|
||||
int id = labelMap.get(target);
|
||||
writer.println("goto teavm_label_" + id + ";");
|
||||
usedAsBreakTarget.add(target);
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ContinueStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
if (statement.getTarget() == null || statement.getTarget().getId() == null) {
|
||||
writer.println("continue;");
|
||||
} else {
|
||||
writer.println("goto teavm_cnt_" + statement.getTarget().getId() + ";");
|
||||
IdentifiedStatement target = statement.getTarget();
|
||||
if (target == null) {
|
||||
target = defaultContinueTarget;
|
||||
}
|
||||
int id = labelMap.get(target);
|
||||
writer.println("goto teavm_cnt_" + id + ";");
|
||||
usedAsContinueTarget.add(target);
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
|
@ -1036,23 +1191,99 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
@Override
|
||||
public void visit(ThrowStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
includes.includeClass(THROW_EXCEPTION_METHOD.getClassName());
|
||||
writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("(");
|
||||
statement.getException().acceptVisitor(this);
|
||||
writer.println(");");
|
||||
writer.print(")");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
writer.println(";");
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InitClassStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
includes.includeClass(statement.getClassName());
|
||||
writer.println(names.forClassInitializer(statement.getClassName()) + "();");
|
||||
writer.print(names.forClassInitializer(statement.getClassName()) + "()");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
writer.println(";");
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(TryCatchStatement statement) {
|
||||
List<TryCatchStatement> tryCatchStatements = new ArrayList<>();
|
||||
List<int[]> restoredVariablesByHandler = new ArrayList<>();
|
||||
while (true) {
|
||||
if (statement.getProtectedBody().size() != 1) {
|
||||
break;
|
||||
}
|
||||
Statement next = statement.getProtectedBody().get(0);
|
||||
if (!(next instanceof TryCatchStatement)) {
|
||||
break;
|
||||
}
|
||||
tryCatchStatements.add(statement);
|
||||
restoredVariablesByHandler.add(volatileDefinitions.variablesToRestore(statement));
|
||||
statement = (TryCatchStatement) next;
|
||||
}
|
||||
tryCatchStatements.add(statement);
|
||||
restoredVariablesByHandler.add(volatileDefinitions.variablesToRestore(statement));
|
||||
|
||||
int firstId = handlers.size();
|
||||
for (int i = 0; i < tryCatchStatements.size(); ++i) {
|
||||
TryCatchStatement tryCatch = tryCatchStatements.get(i);
|
||||
handlers.add(new ExceptionHandlerDescriptor(firstId + i + 1, tryCatch.getExceptionType()));
|
||||
}
|
||||
|
||||
writer.println("TEAVM_TRY").indent();
|
||||
visitMany(statement.getProtectedBody());
|
||||
writer.outdent().println("TEAVM_CATCH").indent();
|
||||
|
||||
for (int i = tryCatchStatements.size() - 1; i >= 0; --i) {
|
||||
TryCatchStatement tryCatch = tryCatchStatements.get(i);
|
||||
int[] variablesToRestore = restoredVariablesByHandler.get(i);
|
||||
writer.println("// CATCH " + (tryCatch.getExceptionType() != null ? tryCatch.getExceptionType() : "any"));
|
||||
writer.println("case " + (i + 1 + firstId) + ": {").indent();
|
||||
|
||||
for (int variableIndex : variablesToRestore) {
|
||||
writer.println(getVariableName(variableIndex) + " = teavm_spill_" + variableIndex + ";");
|
||||
}
|
||||
|
||||
if (tryCatch.getExceptionVariable() != null) {
|
||||
writer.print(getVariableName(tryCatch.getExceptionVariable())).print(" = ");
|
||||
writer.print(names.forMethod(CATCH_EXCEPTION)).println("();");
|
||||
}
|
||||
visitMany(tryCatch.getHandler());
|
||||
|
||||
writer.println("break;");
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
handlers.subList(firstId, handlers.size()).clear();
|
||||
|
||||
writer.outdent().println("TEAVM_END_TRY");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1062,26 +1293,47 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
@Override
|
||||
public void visit(MonitorEnterStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
includes.includeClass("java.lang.Object");
|
||||
writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("(");
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
writer.println(");");
|
||||
writer.print(")");
|
||||
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
writer.println(";");
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitStatement statement) {
|
||||
pushLocation(statement.getLocation());
|
||||
|
||||
boolean needParenthesis = false;
|
||||
if (needsCallSiteId()) {
|
||||
needParenthesis = true;
|
||||
withCallSite();
|
||||
}
|
||||
|
||||
includes.includeClass("java.lang.Object");
|
||||
writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("(");
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
writer.println(");");
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
writer.print(")");
|
||||
|
||||
public void emitSuspendChecker() {
|
||||
String suspendingName = names.forMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class));
|
||||
writer.println("if (" + suspendingName + "(fiber)) goto teavm_exit_loop;");
|
||||
if (needParenthesis) {
|
||||
writer.print(")");
|
||||
}
|
||||
writer.println(";");
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
||||
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
||||
|
|
|
@ -15,12 +15,16 @@
|
|||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import com.carrotsearch.hppc.IntContainer;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.MethodNode;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.backend.c.analyze.VolatileDefinitionFinder;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
|
||||
public class CodeGenerator {
|
||||
private GenerationContext context;
|
||||
|
@ -28,6 +32,7 @@ public class CodeGenerator {
|
|||
private CodeWriter localsWriter;
|
||||
private NameProvider names;
|
||||
private IncludeManager includes;
|
||||
private List<CallSiteDescriptor> callSites;
|
||||
|
||||
public CodeGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes) {
|
||||
this.context = context;
|
||||
|
@ -36,6 +41,10 @@ public class CodeGenerator {
|
|||
this.includes = includes;
|
||||
}
|
||||
|
||||
public void setCallSites(List<CallSiteDescriptor> callSites) {
|
||||
this.callSites = callSites;
|
||||
}
|
||||
|
||||
public void generateMethod(RegularMethodNode methodNode) {
|
||||
generateMethodSignature(writer, methodNode.getReference(),
|
||||
methodNode.getModifiers().contains(ElementModifier.STATIC), true);
|
||||
|
@ -44,13 +53,16 @@ public class CodeGenerator {
|
|||
|
||||
localsWriter = writer.fragment();
|
||||
CodeGenerationVisitor visitor = generateMethodBody(methodNode);
|
||||
generateLocals(methodNode, visitor.getTemporaries());
|
||||
generateLocals(methodNode, visitor.getTemporaries(), visitor.getSpilledVariables());
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
private CodeGenerationVisitor generateMethodBody(RegularMethodNode methodNode) {
|
||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer, includes);
|
||||
VolatileDefinitionFinder volatileDefinitions = new VolatileDefinitionFinder();
|
||||
volatileDefinitions.findVolatileDefinitions(methodNode.getBody());
|
||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer, includes, callSites,
|
||||
volatileDefinitions);
|
||||
visitor.setAsync(context.isAsync(methodNode.getReference()));
|
||||
visitor.setCallingMethod(methodNode.getReference());
|
||||
methodNode.getBody().acceptVisitor(visitor);
|
||||
|
@ -94,7 +106,7 @@ public class CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
private void generateLocals(MethodNode methodNode, int[] temporaryCount) {
|
||||
private void generateLocals(MethodNode methodNode, int[] temporaryCount, IntContainer spilledVariables) {
|
||||
int start = methodNode.getReference().parameterCount() + 1;
|
||||
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
||||
VariableNode variableNode = methodNode.getVariables().get(i);
|
||||
|
@ -103,6 +115,10 @@ public class CodeGenerator {
|
|||
}
|
||||
localsWriter.printType(variableNode.getType()).print(" teavm_local_").print(String.valueOf(i))
|
||||
.println(";");
|
||||
if (spilledVariables.contains(i)) {
|
||||
localsWriter.print("volatile ").printType(variableNode.getType()).print(" teavm_spill_")
|
||||
.print(String.valueOf(i)).println(";");
|
||||
}
|
||||
}
|
||||
|
||||
for (CVariableType type : CVariableType.values()) {
|
||||
|
|
|
@ -44,11 +44,13 @@ public class GenerationContext {
|
|||
private Predicate<MethodReference> asyncMethods;
|
||||
private BuildTarget buildTarget;
|
||||
private boolean incremental;
|
||||
private boolean longjmp;
|
||||
|
||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
||||
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental) {
|
||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental,
|
||||
boolean longjmp) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
this.dependencies = dependencies;
|
||||
|
@ -61,6 +63,7 @@ public class GenerationContext {
|
|||
this.asyncMethods = asyncMethods;
|
||||
this.buildTarget = buildTarget;
|
||||
this.incremental = incremental;
|
||||
this.longjmp = longjmp;
|
||||
}
|
||||
|
||||
public void addIntrinsic(Intrinsic intrinsic) {
|
||||
|
@ -124,4 +127,8 @@ public class GenerationContext {
|
|||
public boolean isIncremental() {
|
||||
return incremental;
|
||||
}
|
||||
|
||||
public boolean isLongjmp() {
|
||||
return longjmp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
|
||||
switch (method.getName()) {
|
||||
case "findCallSiteById":
|
||||
case "isJumpSupported":
|
||||
case "jumpToFrame":
|
||||
case "abort":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -45,6 +48,23 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
|
||||
case "isJumpSupported":
|
||||
context.writer().print("TEAVM_JUMP_SUPPORTED");
|
||||
break;
|
||||
|
||||
case "jumpToFrame":
|
||||
context.writer().print("TEAVM_JUMP_TO_FRAME(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
|
||||
case "abort":
|
||||
context.includes().addInclude("<stdlib.h>");
|
||||
context.writer().print("abort();");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,9 +73,9 @@ public class ShadowStackIntrinsic implements Intrinsic {
|
|||
context.writer().print("teavm_stackTop");
|
||||
return;
|
||||
case "getNextStackFrame":
|
||||
context.writer().print("((void**) ");
|
||||
context.writer().print("TEAVM_GET_NEXT_FRAME(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[0]");
|
||||
context.writer().print(")");
|
||||
return;
|
||||
case "getStackRootCount":
|
||||
context.writer().print("TEAVM_GC_ROOTS_COUNT(");
|
||||
|
@ -89,9 +89,9 @@ public class ShadowStackIntrinsic implements Intrinsic {
|
|||
return;
|
||||
}
|
||||
case "getCallSiteId":
|
||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||
context.writer().print("TEAVM_GET_CALL_SITE_ID(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[1])");
|
||||
context.writer().print(")");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1447,7 +1447,8 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
.softNewLine();
|
||||
boolean first = true;
|
||||
boolean defaultHandlerOccurred = false;
|
||||
for (TryCatchStatement catchClause : sequence) {
|
||||
for (int i = sequence.size() - 1; i >= 0; --i) {
|
||||
TryCatchStatement catchClause = sequence.get(i);
|
||||
if (!first) {
|
||||
writer.ws().append("else");
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
controller.getUnprocessedClassSource());
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository);
|
||||
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
|||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.runtime.CallSite;
|
||||
|
@ -50,6 +51,9 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
|||
}
|
||||
switch (methodReference.getName()) {
|
||||
case "findCallSiteById":
|
||||
case "isJumpSupported":
|
||||
case "jumpToFrame":
|
||||
case "abort":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -64,15 +68,29 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
|||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
WasmInt32Constant constant = new WasmInt32Constant(0);
|
||||
constant.setLocation(invocation.getLocation());
|
||||
constants.add(constant);
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "findCallSiteById": {
|
||||
WasmInt32Constant constant = new WasmInt32Constant(0);
|
||||
constant.setLocation(invocation.getLocation());
|
||||
constants.add(constant);
|
||||
|
||||
int callSiteSize = classGenerator.getClassSize(CallSite.class.getName());
|
||||
WasmExpression id = manager.generate(invocation.getArguments().get(0));
|
||||
WasmExpression offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL,
|
||||
id, new WasmInt32Constant(callSiteSize));
|
||||
int callSiteSize = classGenerator.getClassSize(CallSite.class.getName());
|
||||
WasmExpression id = manager.generate(invocation.getArguments().get(0));
|
||||
WasmExpression offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL,
|
||||
id, new WasmInt32Constant(callSiteSize));
|
||||
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, constant, offset);
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, constant, offset);
|
||||
}
|
||||
|
||||
case "isJumpSupported":
|
||||
return new WasmInt32Constant(0);
|
||||
|
||||
case "jumpToFrame":
|
||||
case "abort":
|
||||
return new WasmUnreachable();
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown method: " + invocation.getMethod());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,13 @@ public class RangeTree {
|
|||
});
|
||||
Deque<NodeImpl> stack = new ArrayDeque<>();
|
||||
stack.push(root);
|
||||
|
||||
Range lastRange = null;
|
||||
for (Range range : rangeList) {
|
||||
if (lastRange != null && lastRange.left == range.left && lastRange.right == range.right) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NodeImpl current = new NodeImpl();
|
||||
current.start = range.left;
|
||||
current.end = range.right;
|
||||
|
@ -114,6 +120,7 @@ public class RangeTree {
|
|||
ancestor.start = current.start;
|
||||
}
|
||||
stack.push(current);
|
||||
lastRange = range;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -272,22 +272,29 @@ public class ExceptionHandlingShadowStackContributor {
|
|||
}
|
||||
|
||||
private boolean isCallInstruction(Instruction insn) {
|
||||
return isCallInstruction(characteristics, insn);
|
||||
}
|
||||
|
||||
public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) {
|
||||
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
||||
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction) {
|
||||
return true;
|
||||
} else if (insn instanceof InvokeInstruction) {
|
||||
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
||||
if (characteristics.isManaged(method)) {
|
||||
return true;
|
||||
}
|
||||
return method.getClassName().equals(ExceptionHandling.class.getName())
|
||||
&& method.getName().startsWith("throw");
|
||||
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) {
|
||||
if (characteristics.isManaged(method)) {
|
||||
return true;
|
||||
}
|
||||
return method.getClassName().equals(ExceptionHandling.class.getName())
|
||||
&& method.getName().startsWith("throw");
|
||||
}
|
||||
|
||||
private boolean isSpecialCallInstruction(Instruction insn) {
|
||||
if (!(insn instanceof InvokeInstruction)) {
|
||||
return false;
|
||||
|
|
|
@ -24,22 +24,25 @@ import org.teavm.model.MethodReader;
|
|||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
import org.teavm.model.util.BasicBlockMapper;
|
||||
import org.teavm.runtime.ShadowStack;
|
||||
|
||||
public class ShadowStackTransformer {
|
||||
private Characteristics characteristics;
|
||||
private GCShadowStackContributor gcContributor;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
private boolean exceptionHandling;
|
||||
|
||||
public ShadowStackTransformer(Characteristics characteristics) {
|
||||
public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) {
|
||||
gcContributor = new GCShadowStackContributor(characteristics);
|
||||
this.characteristics = characteristics;
|
||||
this.exceptionHandling = exceptionHandling;
|
||||
}
|
||||
|
||||
public void apply(Program program, MethodReader method) {
|
||||
|
@ -48,11 +51,27 @@ public class ShadowStackTransformer {
|
|||
}
|
||||
|
||||
int shadowStackSize = gcContributor.contribute(program, method);
|
||||
int callSiteStartIndex = callSites.size();
|
||||
boolean exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program).contribute();
|
||||
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size());
|
||||
CallSiteDescriptor.save(programCallSites, program.getAnnotations());
|
||||
boolean exceptions;
|
||||
if (exceptionHandling) {
|
||||
List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program).contribute();
|
||||
CallSiteDescriptor.save(callSites, program.getAnnotations());
|
||||
} else {
|
||||
exceptions = false;
|
||||
outer: for (BasicBlock block : program.getBasicBlocks()) {
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
exceptions = true;
|
||||
break;
|
||||
}
|
||||
for (Instruction insn : block) {
|
||||
if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) {
|
||||
exceptions = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shadowStackSize > 0 || exceptions) {
|
||||
addStackAllocation(program, shadowStackSize);
|
||||
|
@ -62,6 +81,10 @@ public class ShadowStackTransformer {
|
|||
|
||||
private void addStackAllocation(Program program, int maxDepth) {
|
||||
BasicBlock block = program.basicBlockAt(0);
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
splitFirstBlock(program);
|
||||
}
|
||||
|
||||
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||
Variable sizeVariable = program.createVariable();
|
||||
|
||||
|
@ -79,6 +102,26 @@ public class ShadowStackTransformer {
|
|||
block.addFirstAll(instructionsToAdd);
|
||||
}
|
||||
|
||||
private void splitFirstBlock(Program program) {
|
||||
BasicBlock block = program.basicBlockAt(0);
|
||||
BasicBlock split = program.createBasicBlock();
|
||||
while (block.getFirstInstruction() != null) {
|
||||
Instruction instruction = block.getFirstInstruction();
|
||||
instruction.delete();
|
||||
split.add(instruction);
|
||||
}
|
||||
JumpInstruction jump = new JumpInstruction();
|
||||
jump.setLocation(split.getFirstInstruction().getLocation());
|
||||
jump.setTarget(split);
|
||||
block.add(jump);
|
||||
|
||||
List<TryCatchBlock> tryCatchBlocks = new ArrayList<>(block.getTryCatchBlocks());
|
||||
block.getTryCatchBlocks().clear();
|
||||
split.getTryCatchBlocks().addAll(tryCatchBlocks);
|
||||
|
||||
new BasicBlockMapper((BasicBlock b) -> b == block ? split : b).transform(program);
|
||||
}
|
||||
|
||||
private void addStackRelease(Program program, int maxDepth) {
|
||||
List<BasicBlock> blocks = new ArrayList<>();
|
||||
boolean hasResult = false;
|
||||
|
|
|
@ -17,7 +17,9 @@ package org.teavm.model.util;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.common.DisjointSet;
|
||||
|
@ -151,17 +153,103 @@ public class RegisterAllocator {
|
|||
}
|
||||
|
||||
private void insertPhiArgumentsCopies(Program program) {
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
List<List<Incoming>> catchIncomingsByVariable = new ArrayList<>(
|
||||
Collections.nCopies(program.variableCount(), null));
|
||||
|
||||
int sz = program.basicBlockCount();
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
Map<BasicBlock, BasicBlock> blockMap = new HashMap<>();
|
||||
for (Phi phi : program.basicBlockAt(i).getPhis()) {
|
||||
List<Incoming> incomingsToRepeat = new ArrayList<>();
|
||||
for (Phi phi : block.getPhis()) {
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
insertCopy(incoming, blockMap);
|
||||
boolean fromTry = incoming.getSource().getTryCatchBlocks().stream()
|
||||
.anyMatch(tryCatch -> tryCatch.getHandler() == incoming.getPhi().getBasicBlock());
|
||||
if (fromTry) {
|
||||
int valueIndex = incoming.getValue().getIndex();
|
||||
List<Incoming> catchIncomings = catchIncomingsByVariable.get(valueIndex);
|
||||
if (catchIncomings == null) {
|
||||
catchIncomings = new ArrayList<>(1);
|
||||
catchIncomingsByVariable.set(valueIndex, catchIncomings);
|
||||
}
|
||||
catchIncomings.add(incoming);
|
||||
} else {
|
||||
insertCopy(incoming, blockMap);
|
||||
incomingsToRepeat.add(incoming);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Phi phi : program.basicBlockAt(i).getPhis()) {
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
insertCopy(incoming, blockMap);
|
||||
|
||||
for (Incoming incoming : incomingsToRepeat) {
|
||||
insertCopy(incoming, blockMap);
|
||||
}
|
||||
}
|
||||
|
||||
DefinitionExtractor definitionExtractor = new DefinitionExtractor();
|
||||
List<Instruction> nextInstructions = new ArrayList<>();
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
instruction.acceptVisitor(definitionExtractor);
|
||||
Variable[] definedVariables = definitionExtractor.getDefinedVariables();
|
||||
for (Variable definedVariable : definedVariables) {
|
||||
if (definedVariable.getIndex() >= catchIncomingsByVariable.size()) {
|
||||
continue;
|
||||
}
|
||||
List<Incoming> catchIncomings = catchIncomingsByVariable.get(definedVariable.getIndex());
|
||||
if (catchIncomings == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Incoming incoming = null;
|
||||
for (Iterator<Incoming> iter = catchIncomings.iterator(); iter.hasNext();) {
|
||||
if (iter.next().getValue() == definedVariable) {
|
||||
iter.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (incoming == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable copy = program.createVariable();
|
||||
copy.setLabel(incoming.getPhi().getReceiver().getLabel());
|
||||
copy.setDebugName(incoming.getPhi().getReceiver().getDebugName());
|
||||
|
||||
AssignInstruction copyInstruction = new AssignInstruction();
|
||||
copyInstruction.setReceiver(copy);
|
||||
copyInstruction.setAssignee(incoming.getValue());
|
||||
copyInstruction.setLocation(instruction.getLocation());
|
||||
|
||||
incoming.setValue(copy);
|
||||
|
||||
nextInstructions.add(copyInstruction);
|
||||
}
|
||||
|
||||
if (!nextInstructions.isEmpty()) {
|
||||
instruction.insertNextAll(nextInstructions);
|
||||
nextInstructions.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (List<Incoming> remainingIncomings : catchIncomingsByVariable) {
|
||||
if (remainingIncomings == null) {
|
||||
continue;
|
||||
}
|
||||
for (Incoming incoming : remainingIncomings) {
|
||||
BasicBlock block = incoming.getSource();
|
||||
|
||||
Variable copy = program.createVariable();
|
||||
copy.setLabel(incoming.getPhi().getReceiver().getLabel());
|
||||
copy.setDebugName(incoming.getPhi().getReceiver().getDebugName());
|
||||
incoming.setValue(copy);
|
||||
|
||||
AssignInstruction copyInstruction = new AssignInstruction();
|
||||
copyInstruction.setReceiver(copy);
|
||||
copyInstruction.setAssignee(incoming.getValue());
|
||||
copyInstruction.setLocation(block.getFirstInstruction().getLocation());
|
||||
|
||||
block.addFirst(copyInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,6 @@ import org.teavm.model.instructions.StringConstantInstruction;
|
|||
import org.teavm.model.instructions.SwitchInstruction;
|
||||
import org.teavm.model.instructions.SwitchTableEntry;
|
||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.TransitionExtractor;
|
||||
|
||||
public class ProgramParser {
|
||||
|
@ -185,8 +184,6 @@ public class ProgramParser {
|
|||
while (program.variableCount() <= signatureVars) {
|
||||
program.createVariable();
|
||||
}
|
||||
program.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(
|
||||
program.basicBlockAt(1), program));
|
||||
return program;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ public final class Allocator {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static RuntimeArray allocateMultiArray(RuntimeClass tag, Address dimensions, int dimensionCount) {
|
||||
int size = dimensions.getInt();
|
||||
RuntimeArray array = allocateArray(tag, dimensions.getInt()).toStructure();
|
||||
|
|
|
@ -55,7 +55,7 @@ public final class EventQueue {
|
|||
}
|
||||
|
||||
public static void kill(int id) {
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (data[i].id == id) {
|
||||
remove(i);
|
||||
break;
|
||||
|
|
|
@ -25,15 +25,26 @@ public final class ExceptionHandling {
|
|||
private ExceptionHandling() {
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static native CallSite findCallSiteById(int id, Address frame);
|
||||
|
||||
@Unmanaged
|
||||
public static native boolean isJumpSupported();
|
||||
|
||||
@Unmanaged
|
||||
public static native void jumpToFrame(Address frame, int id);
|
||||
|
||||
@Unmanaged
|
||||
public static native void abort();
|
||||
|
||||
@Unmanaged
|
||||
public static void printStack() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
MethodLocation methodLocation = location.method;
|
||||
MethodLocation methodLocation = location != null ? location.method : null;
|
||||
|
||||
Console.printString(" at ");
|
||||
if (methodLocation.className == null || methodLocation.methodName == null) {
|
||||
|
@ -56,7 +67,7 @@ public final class ExceptionHandling {
|
|||
|
||||
private static Throwable thrownException;
|
||||
|
||||
@Export(name = "sys_catchException")
|
||||
@Export(name = "teavm_catchException")
|
||||
@Unmanaged
|
||||
public static Throwable catchException() {
|
||||
Throwable exception = thrownException;
|
||||
|
@ -72,6 +83,7 @@ public final class ExceptionHandling {
|
|||
RuntimeClass exceptionClass = RuntimeClass.getClass(exceptionPtr);
|
||||
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
int handlerId = 0;
|
||||
stackLoop: while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
|
@ -79,6 +91,7 @@ public final class ExceptionHandling {
|
|||
|
||||
while (handler != null) {
|
||||
if (handler.exceptionClass == null || handler.exceptionClass.isSupertypeOf.apply(exceptionClass)) {
|
||||
handlerId = handler.id;
|
||||
ShadowStack.setExceptionHandlerId(stackFrame, handler.id);
|
||||
break stackLoop;
|
||||
}
|
||||
|
@ -88,6 +101,13 @@ public final class ExceptionHandling {
|
|||
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId - 1);
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
|
||||
if (stackFrame == null) {
|
||||
printStack();
|
||||
abort();
|
||||
} else if (isJumpSupported()) {
|
||||
jumpToFrame(stackFrame, handlerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
|
@ -96,6 +116,7 @@ public final class ExceptionHandling {
|
|||
}
|
||||
|
||||
@Unmanaged
|
||||
@Export(name = "teavm_throwNullPointerException")
|
||||
public static void throwNullPointerException() {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
@ -119,11 +140,11 @@ public final class ExceptionHandling {
|
|||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
MethodLocation methodLocation = location.method;
|
||||
MethodLocation methodLocation = location != null ? location.method : null;
|
||||
StackTraceElement element = createElement(
|
||||
location != null && methodLocation.className != null ? methodLocation.className.value : "",
|
||||
location != null && methodLocation.methodName != null ? methodLocation.methodName.value : "",
|
||||
location != null && methodLocation.fileName != null ? methodLocation.fileName.value : null,
|
||||
methodLocation != null && methodLocation.className != null ? methodLocation.className.value : "",
|
||||
methodLocation != null && methodLocation.methodName != null ? methodLocation.methodName.value : "",
|
||||
methodLocation != null && methodLocation.fileName != null ? methodLocation.fileName.value : null,
|
||||
location != null ? location.lineNumber : -1);
|
||||
target[index++] = element;
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
|
|
|
@ -16,7 +16,11 @@
|
|||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
final class MarkQueue {
|
||||
private MarkQueue() {
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <dirent.h>
|
||||
#include <utime.h>
|
||||
#include <pwd.h>
|
||||
#include <string.h>
|
||||
|
||||
int32_t teavm_file_homeDirectory(char16_t** result) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
void* teavm_gc_heapAddress = NULL;
|
||||
|
||||
void** teavm_stackTop;
|
||||
TeaVM_StackFrame* teavm_stackTop = NULL;
|
||||
|
||||
void* teavm_gc_gcStorageAddress = NULL;
|
||||
int32_t teavm_gc_gcStorageSize = INT32_C(0);
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef TEAVM_USE_SETJMP
|
||||
#include <setjmp.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <stdalign.h>
|
||||
#include <signal.h>
|
||||
|
@ -58,6 +62,18 @@ typedef struct TeaVM_String {
|
|||
int32_t hashCode;
|
||||
} TeaVM_String;
|
||||
|
||||
typedef struct TeaVM_StackFrame {
|
||||
struct TeaVM_StackFrame* next;
|
||||
#ifdef TEAVM_INCREMENTAL
|
||||
void* callSites;
|
||||
#endif
|
||||
#ifdef TEAVM_USE_SETJMP
|
||||
jmp_buf* jmpTarget;
|
||||
#endif
|
||||
int32_t size;
|
||||
int32_t callSiteId;
|
||||
} TeaVM_StackFrame;
|
||||
|
||||
extern void* teavm_gc_heapAddress;
|
||||
extern char *teavm_beforeClasses;
|
||||
|
||||
|
@ -87,10 +103,10 @@ static inline int32_t teavm_compare_i64(int64_t a, int64_t b) {
|
|||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
}
|
||||
static inline int32_t teavm_compare_float(float a, float b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : a == b ? INT32_C(0) : INT32_C(1);
|
||||
}
|
||||
static inline int32_t teavm_compare_double(double a, double b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : a == b ? INT32_C(0) : INT32_C(1);
|
||||
}
|
||||
|
||||
#define TEAVM_ALIGN(addr, alignment) ((void*) (((uintptr_t) (addr) + ((alignment) - 1)) / (alignment) * (alignment)))
|
||||
|
@ -110,36 +126,39 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
|
|||
|
||||
#ifdef TEAVM_INCREMENTAL
|
||||
|
||||
#define TEAVM_ALLOC_STACK_DEF(size, callSites) \
|
||||
void* teavm__shadowStack__[(size) + 4]; \
|
||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||
teavm__shadowStack__[2] = (void*) size; \
|
||||
teavm__shadowStack__[3] = (void*) (callSites); \
|
||||
teavm_stackTop = teavm__shadowStack__
|
||||
#define TEAVM_ALLOC_STACK_DEF(sz, cs) \
|
||||
struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
|
||||
teavm_shadowStack.header.next = teavm_stackTop; \
|
||||
teavm_shadowStack.header.callSites = (cs); \
|
||||
teavm_shadowStack.header.size = (sz); \
|
||||
teavm_stackTop = &teavm_shadowStack.header
|
||||
|
||||
#define TEAVM_STACK_HEADER_ADD_SIZE 1
|
||||
|
||||
#else
|
||||
|
||||
#define TEAVM_ALLOC_STACK(size) \
|
||||
void* teavm__shadowStack__[(size) + 3]; \
|
||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||
teavm__shadowStack__[2] = (void*) size; \
|
||||
teavm_stackTop = teavm__shadowStack__
|
||||
#define TEAVM_ALLOC_STACK(sz) \
|
||||
struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
|
||||
teavm_shadowStack.header.next = teavm_stackTop; \
|
||||
teavm_shadowStack.header.size = (sz); \
|
||||
teavm_stackTop = &teavm_shadowStack.header
|
||||
|
||||
#define TEAVM_STACK_HEADER_ADD_SIZE 0
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
|
||||
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = ptr
|
||||
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = NULL
|
||||
#define TEAVM_GC_ROOTS_COUNT(ptr) ((int32_t) (intptr_t) ((void**) (ptr))[2])
|
||||
#define TEAVM_GET_GC_ROOTS(ptr) (((void**) (ptr)) + 3 + TEAVM_STACK_HEADER_ADD_SIZE)
|
||||
#define TEAVM_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id))
|
||||
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1]))
|
||||
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id))
|
||||
#define TEAVM_RELEASE_STACK (teavm_stackTop = teavm_shadowStack.header.next)
|
||||
#define TEAVM_GC_ROOT(index, ptr) teavm_shadowStack.data[index] = ptr
|
||||
#define TEAVM_GC_ROOT_RELEASE(index) teavm_shadowStack.data[index] = NULL
|
||||
#define TEAVM_GC_ROOTS_COUNT(ptr) (((TeaVM_StackFrame*) (ptr))->size);
|
||||
#define TEAVM_GET_GC_ROOTS(ptr) &((struct { TeaVM_StackFrame header; void* data[1]; }*) (ptr))->data;
|
||||
#define TEAVM_CALL_SITE(id) (teavm_shadowStack.header.callSiteId = (id))
|
||||
#define TEAVM_WITH_CALL_SITE_ID(id, expr) (TEAVM_CALL_SITE(id), (expr))
|
||||
#define TEAVM_EXCEPTION_HANDLER (teavm_shadowStack.header.callSiteId)
|
||||
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((TeaVM_StackFrame*) (frame))->callSiteId = (id))
|
||||
#define TEAVM_GET_NEXT_FRAME(frame) (((TeaVM_StackFrame*) (frame))->next)
|
||||
#define TEAVM_GET_CALL_SITE_ID(frame) (((TeaVM_StackFrame*) (frame))->callSiteId)
|
||||
|
||||
#define TEAVM_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
|
||||
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset)
|
||||
|
@ -160,7 +179,7 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
|
|||
.hashCode = INT32_C(hash) \
|
||||
}
|
||||
|
||||
extern void** teavm_stackTop;
|
||||
extern TeaVM_StackFrame* teavm_stackTop;
|
||||
|
||||
extern void* teavm_gc_gcStorageAddress;
|
||||
extern int32_t teavm_gc_gcStorageSize;
|
||||
|
@ -359,4 +378,49 @@ extern int32_t teavm_file_canonicalize(char16_t*, int32_t, char16_t**);
|
|||
extern int64_t teavm_unixTimeOffset;
|
||||
#endif
|
||||
|
||||
extern void teavm_logchar(int32_t);
|
||||
extern void teavm_logchar(int32_t);
|
||||
|
||||
#ifdef TEAVM_USE_SETJMP
|
||||
#define TEAVM_JUMP_SUPPORTED 1
|
||||
#define TEAVM_TRY \
|
||||
do { \
|
||||
jmp_buf teavm_tryBuffer; \
|
||||
jmp_buf* teavm_oldTryBuffer = teavm_shadowStack.header.jmpTarget; \
|
||||
teavm_shadowStack.header.jmpTarget = &teavm_tryBuffer; \
|
||||
int teavm_exceptionHandler = setjmp(teavm_tryBuffer); \
|
||||
switch (teavm_exceptionHandler) { \
|
||||
case 0: {
|
||||
#define TEAVM_CATCH \
|
||||
break; \
|
||||
} \
|
||||
default: { \
|
||||
longjmp(*teavm_oldTryBuffer, teavm_exceptionHandler); \
|
||||
break; \
|
||||
}
|
||||
#define TEAVM_END_TRY \
|
||||
} \
|
||||
teavm_shadowStack.header.jmpTarget = teavm_oldTryBuffer; \
|
||||
} while (0);
|
||||
|
||||
#define TEAVM_JUMP_TO_FRAME(frame, id) \
|
||||
teavm_stackTop = (TeaVM_StackFrame*) (frame); \
|
||||
longjmp(*teavm_stackTop->jmpTarget, id)
|
||||
extern void teavm_throwNullPointerException();
|
||||
inline static void* teavm_nullCheck(void* o) {
|
||||
if (o == NULL) {
|
||||
teavm_throwNullPointerException();
|
||||
#ifdef __GNUC__
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
__assume(0);
|
||||
#endif
|
||||
}
|
||||
return o;
|
||||
}
|
||||
#else
|
||||
#define TEAVM_JUMP_SUPPORTED 0
|
||||
#define TEAVM_JUMP_TO_FRAME(frame, id)
|
||||
#endif
|
||||
|
||||
extern void* teavm_catchException();
|
|
@ -32,6 +32,7 @@ public interface PlatformClassMetadata extends JSObject {
|
|||
PlatformClass getSuperclass();
|
||||
|
||||
@JSProperty
|
||||
@Unmanaged
|
||||
String getName();
|
||||
|
||||
@JSProperty
|
||||
|
|
4
samples/benchmark/.gitignore
vendored
4
samples/benchmark/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
/CMakeFiles
|
||||
/CMakeCache.txt
|
||||
/Makefile
|
||||
/cmake_install.cmake
|
|
@ -1,2 +1,3 @@
|
|||
export LC_ALL=C
|
||||
SOURCE_DIR=$(pwd)
|
||||
gcc -g -O0 -lrt -lm all.c -o run_test
|
|
@ -335,18 +335,21 @@ public class ByteBufferTest {
|
|||
byte[] receiver = new byte[4];
|
||||
try {
|
||||
buffer.get(receiver, 0, 5);
|
||||
fail("Error expected");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
assertThat(receiver, is(new byte[4]));
|
||||
assertThat(buffer.position(), is(0));
|
||||
}
|
||||
try {
|
||||
buffer.get(receiver, -1, 3);
|
||||
fail("Error expected");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
assertThat(receiver, is(new byte[4]));
|
||||
assertThat(buffer.position(), is(0));
|
||||
}
|
||||
try {
|
||||
buffer.get(receiver, 6, 3);
|
||||
fail("Error expected");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
assertThat(receiver, is(new byte[4]));
|
||||
assertThat(buffer.position(), is(0));
|
||||
|
|
|
@ -20,6 +20,8 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -116,19 +118,19 @@ public class VMTest {
|
|||
@Test
|
||||
public void catchFinally() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<String> a = Arrays.asList("a", "b");
|
||||
if (a.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (Integer.parseInt("invalid") > 0) {
|
||||
sb.append("err1;");
|
||||
} else {
|
||||
sb.append("err2;");
|
||||
for (String b : a) {
|
||||
if (b.length() < 3) {
|
||||
sb.append(b);
|
||||
}
|
||||
}
|
||||
sb.append("err3");
|
||||
} catch (NumberFormatException e) {
|
||||
sb.append("catch;");
|
||||
} finally {
|
||||
sb.append("finally;");
|
||||
}
|
||||
assertEquals("catch;finally;", sb.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -195,7 +197,7 @@ public class VMTest {
|
|||
n += foo() * 5;
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(RuntimeException.class, e.getClass());
|
||||
assertEquals(n, 22);
|
||||
assertEquals(22, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ public class IncrementalCBuilder {
|
|||
private String mainClass;
|
||||
private String[] classPath;
|
||||
private int minHeapSize = 32;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean lineNumbersGenerated;
|
||||
private String targetPath;
|
||||
private String externalTool;
|
||||
|
@ -131,6 +132,10 @@ public class IncrementalCBuilder {
|
|||
this.mainFunctionName = mainFunctionName;
|
||||
}
|
||||
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
public void addProgressHandler(ProgressHandler handler) {
|
||||
synchronized (progressHandlers) {
|
||||
progressHandlers.add(handler);
|
||||
|
@ -327,6 +332,7 @@ public class IncrementalCBuilder {
|
|||
cTarget.setIncremental(true);
|
||||
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
cTarget.setLineNumbersGenerated(lineNumbersGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||
vm.setCacheStatus(classSource);
|
||||
vm.addVirtualMethods(m -> true);
|
||||
|
|
|
@ -62,6 +62,10 @@ public class TeaVMCBuilderRunner {
|
|||
.hasArg()
|
||||
.withDescription("Minimum heap size in bytes")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("no-longjmp")
|
||||
.withDescription("Don't use setjmp/longjmp functions to emulate exception handling")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("entry-point")
|
||||
.withArgName("name")
|
||||
|
@ -117,6 +121,9 @@ public class TeaVMCBuilderRunner {
|
|||
if (commandLine.hasOption('e')) {
|
||||
builder.setMainFunctionName(commandLine.getOptionValue('e'));
|
||||
}
|
||||
if (commandLine.hasOption("no-longjmp")) {
|
||||
builder.setLongjmpSupported(false);
|
||||
}
|
||||
|
||||
String[] args = commandLine.getArgs();
|
||||
if (args.length != 1) {
|
||||
|
|
|
@ -141,6 +141,10 @@ public final class TeaVMRunner {
|
|||
.withDescription("Maximum number of names kept in top-level scope ("
|
||||
+ "other will be put in a separate object. 10000 by default.")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("no-longjmp")
|
||||
.withDescription("Don't use setjmp/longjmp functions to emulate exceptions (C target)")
|
||||
.create());
|
||||
}
|
||||
|
||||
private TeaVMRunner(CommandLine commandLine) {
|
||||
|
@ -177,6 +181,7 @@ public final class TeaVMRunner {
|
|||
parseIncrementalOptions();
|
||||
parseJavaScriptOptions();
|
||||
parseWasmOptions();
|
||||
parseCOptions();
|
||||
parseHeap();
|
||||
|
||||
if (commandLine.hasOption("e")) {
|
||||
|
@ -313,6 +318,12 @@ public final class TeaVMRunner {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseCOptions() {
|
||||
if (commandLine.hasOption("no-longjmp")) {
|
||||
tool.setLongjmpSupported(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeap() {
|
||||
if (commandLine.hasOption("min-heap")) {
|
||||
int size;
|
||||
|
|
|
@ -103,7 +103,7 @@ public class TeaVMTool {
|
|||
private Set<File> generatedFiles = new HashSet<>();
|
||||
private int minHeapSize = 32 * (1 << 20);
|
||||
private ReferenceCache referenceCache;
|
||||
private String mainFunctionName;
|
||||
private boolean longjmpSupported = true;
|
||||
|
||||
public File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
|
@ -249,8 +249,8 @@ public class TeaVMTool {
|
|||
this.wasmVersion = wasmVersion;
|
||||
}
|
||||
|
||||
public void setMainFunctionName(String mainFunctionName) {
|
||||
this.mainFunctionName = mainFunctionName;
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
public void setProgressListener(TeaVMProgressListener progressListener) {
|
||||
|
@ -327,6 +327,7 @@ public class TeaVMTool {
|
|||
cTarget = new CTarget();
|
||||
cTarget.setMinHeapSize(minHeapSize);
|
||||
cTarget.setLineNumbersGenerated(debugInformationGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
return cTarget;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,5 +74,7 @@ public interface BuildStrategy {
|
|||
|
||||
void setHeapSize(int heapSize);
|
||||
|
||||
void setLongjmpSupported(boolean value);
|
||||
|
||||
BuildResult build() throws BuildException;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
private int heapSize = 32;
|
||||
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
||||
private boolean longjmpSupported = true;
|
||||
private TeaVMProgressListener progressListener;
|
||||
private Properties properties = new Properties();
|
||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||
|
@ -196,6 +197,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
this.heapSize = heapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildResult build() throws BuildException {
|
||||
TeaVMTool tool = new TeaVMTool();
|
||||
|
@ -222,6 +228,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null);
|
||||
tool.setWasmVersion(wasmVersion);
|
||||
tool.setMinHeapSize(heapSize);
|
||||
tool.setLongjmpSupported(longjmpSupported);
|
||||
|
||||
tool.getProperties().putAll(properties);
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
|||
request = new RemoteBuildRequest();
|
||||
request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
|
||||
request.wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
request.longjmpSupported = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -174,6 +175,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
|||
request.heapSize = heapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongjmpSupported(boolean value) {
|
||||
request.longjmpSupported = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildResult build() throws BuildException {
|
||||
RemoteBuildResponse response;
|
||||
|
|
|
@ -159,6 +159,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
|
|||
tool.setMaxTopLevelNames(request.maxTopLevelNames);
|
||||
tool.setWasmVersion(request.wasmVersion);
|
||||
tool.setMinHeapSize(request.heapSize);
|
||||
tool.setLongjmpSupported(request.longjmpSupported);
|
||||
|
||||
for (String sourceDirectory : request.sourceDirectories) {
|
||||
tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory)));
|
||||
|
|
|
@ -46,4 +46,5 @@ public class RemoteBuildRequest implements Serializable {
|
|||
public boolean fastDependencyAnalysis;
|
||||
public WasmBinaryVersion wasmVersion;
|
||||
public int heapSize;
|
||||
public boolean longjmpSupported;
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
<configuration>
|
||||
<targetDirectory>${build.directory}/classes/teavm/devserver</targetDirectory>
|
||||
<targetDirectory>${project.build.directory}/classes/teavm/devserver</targetDirectory>
|
||||
<targetFileName>deobfuscator.js</targetFileName>
|
||||
<minifying>true</minifying>
|
||||
<optimizationLevel>ADVANCED</optimizationLevel>
|
||||
|
|
|
@ -146,6 +146,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
@Parameter(property = "teavm.processMemory", defaultValue = "512")
|
||||
private int processMemory;
|
||||
|
||||
@Parameter(property = "teavm.longjmpSupported", defaultValue = "true")
|
||||
private boolean longjmpSupported;
|
||||
|
||||
private void setupBuilder(BuildStrategy builder) throws MojoExecutionException {
|
||||
builder.setLog(new MavenTeaVMToolLog(getLog()));
|
||||
try {
|
||||
|
@ -277,6 +280,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
builder.setCacheDirectory(cacheDirectory.getAbsolutePath());
|
||||
builder.setTargetType(targetType);
|
||||
builder.setWasmVersion(wasmVersion);
|
||||
builder.setLongjmpSupported(longjmpSupported);
|
||||
BuildResult result;
|
||||
result = builder.build();
|
||||
TeaVMProblemRenderer.describeProblems(result.getCallGraph(), result.getProblems(), toolLog);
|
||||
|
|
Loading…
Reference in New Issue
Block a user