C: add option to support exceptions via setjmp/longjmp

This commit is contained in:
Alexey Andreev 2019-07-01 00:26:10 +03:00
parent 114ad986e4
commit 90e00f7eb4
44 changed files with 1405 additions and 280 deletions

View File

@ -25,7 +25,6 @@ import org.teavm.classlib.java.lang.TObject;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeArray;
@ -69,7 +68,6 @@ public final class TArray extends TObject {
private static native TObject newInstanceImpl(PlatformClass componentType, int length); private static native TObject newInstanceImpl(PlatformClass componentType, int length);
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Unmanaged
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) { private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
return Allocator.allocateArray(cls.arrayType, length).toStructure(); return Allocator.allocateArray(cls.arrayType, length).toStructure();
} }

View File

@ -61,9 +61,7 @@ public class Decompiler {
private Graph graph; private Graph graph;
private LoopGraph loopGraph; private LoopGraph loopGraph;
private GraphIndexer indexer; private GraphIndexer indexer;
private int[] loops;
private int[] loopSuccessors; private int[] loopSuccessors;
private Block[] blockMap;
private boolean[] exceptionHandlers; private boolean[] exceptionHandlers;
private int lastBlockId; private int lastBlockId;
private RangeTree codeTree; private RangeTree codeTree;
@ -83,7 +81,6 @@ public class Decompiler {
static class Block { static class Block {
Block parent; Block parent;
int parentOffset;
final IdentifiedStatement statement; final IdentifiedStatement statement;
final List<Statement> body; final List<Statement> body;
final int end; final int end;
@ -100,24 +97,6 @@ public class Decompiler {
this.start = start; this.start = start;
this.end = end; 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 { static class TryCatchBookmark {
@ -207,16 +186,14 @@ public class Decompiler {
graph = indexer.getGraph(); graph = indexer.getGraph();
loopGraph = new LoopGraph(this.graph); loopGraph = new LoopGraph(this.graph);
unflatCode(); unflatCode();
blockMap = new Block[program.basicBlockCount() * 2 + 1];
stack = new ArrayDeque<>(); stack = new ArrayDeque<>();
this.program = program; this.program = program;
BlockStatement rootStmt = new BlockStatement(); BlockStatement rootStmt = new BlockStatement();
rootStmt.setId("root"); 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(); StatementGenerator generator = new StatementGenerator();
generator.classSource = classSource; generator.classSource = classSource;
generator.program = program; generator.program = program;
generator.blockMap = blockMap;
generator.indexer = indexer; generator.indexer = indexer;
parentNode = codeTree.getRoot(); parentNode = codeTree.getRoot();
currentNode = parentNode.getFirstChild(); currentNode = parentNode.getFirstChild();
@ -224,18 +201,7 @@ public class Decompiler {
fillExceptionHandlers(program); fillExceptionHandlers(program);
for (int i = 0; i < this.graph.size(); ++i) { for (int i = 0; i < this.graph.size(); ++i) {
int node = i < indexer.size() ? indexer.nodeAt(i) : -1; int node = i < indexer.size() ? indexer.nodeAt(i) : -1;
int next = i + 1; BasicBlock basicBlock = node >= 0 ? program.basicBlockAt(node) : null;
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;
}
Block block = stack.peek(); Block block = stack.peek();
while (parentNode.getEnd() == i) { while (parentNode.getEnd() == i) {
@ -245,16 +211,18 @@ public class Decompiler {
for (Block newBlock : createBlocks(i)) { for (Block newBlock : createBlocks(i)) {
block.body.add(newBlock.statement); block.body.add(newBlock.statement);
newBlock.parent = block; newBlock.parent = block;
newBlock.parentOffset = block.body.size();
stack.push(newBlock); stack.push(newBlock);
block = 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(); generator.statements.clear();
TextLocation lastLocation = null; TextLocation lastLocation = null;
for (Instruction insn : generator.currentBlock) { for (Instruction insn : basicBlock) {
if (insn.getLocation() != null && lastLocation != insn.getLocation()) { if (insn.getLocation() != null && lastLocation != insn.getLocation()) {
lastLocation = insn.getLocation(); lastLocation = insn.getLocation();
} }
@ -276,20 +244,17 @@ public class Decompiler {
Block oldBlock = block; Block oldBlock = block;
stack.pop(); stack.pop();
block = stack.peek(); 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); TryCatchBookmark bookmark = oldBlock.tryCatches.get(j);
TryCatchStatement tryCatchStmt = new TryCatchStatement(); TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(bookmark.exceptionType); tryCatchStmt.setExceptionType(bookmark.exceptionType);
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
tryCatchStmt.getHandler().add(generator.generateJumpStatement( Statement handlerStatement = generator.generateJumpStatement(block,
program.basicBlockAt(bookmark.exceptionHandler))); program.basicBlockAt(bookmark.exceptionHandler));
if (handlerStatement != null) {
tryCatchStmt.getHandler().add(handlerStatement);
}
List<Statement> blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size()); List<Statement> blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size());
tryCatchStmt.getProtectedBody().addAll(blockPart); tryCatchStmt.getProtectedBody().addAll(blockPart);
blockPart.clear(); blockPart.clear();
@ -301,11 +266,16 @@ public class Decompiler {
tryCatchBookmarks.subList(tryCatchBookmarks.size() - oldBlock.tryCatches.size(), tryCatchBookmarks.subList(tryCatchBookmarks.size() - oldBlock.tryCatches.size(),
tryCatchBookmarks.size()).clear(); tryCatchBookmarks.size()).clear();
oldBlock.tryCatches.clear(); oldBlock.tryCatches.clear();
oldBlock.removeFrom(blockMap);
} }
if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) { if (i < this.graph.size() - 1) {
closeExpiredBookmarks(generator, generator.nextBlock.getTryCatchBlocks()); 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); removedBookmarks.add(bookmark);
} }
if (!removedBookmarks.isEmpty()) {
Collections.reverse(removedBookmarks); Collections.reverse(removedBookmarks);
for (TryCatchBookmark bookmark : removedBookmarks) {
Block block = stack.peek(); 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(); TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(bookmark.exceptionType); tryCatchStmt.setExceptionType(bookmark.exceptionType);
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
tryCatchStmt.getHandler().add(generator.generateJumpStatement( Statement handlerStatement = generator.generateJumpStatement(block,
program.basicBlockAt(bookmark.exceptionHandler))); program.basicBlockAt(bookmark.exceptionHandler));
List<Statement> body = block.body.subList(0, block.body.size() - 1); 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); tryCatchStmt.getProtectedBody().addAll(body);
if (!body.isEmpty()) {
body.clear(); body.clear();
body.add(tryCatchStmt); body.add(tryCatchStmt);
} }
}
block.tryCatches.removeAll(removedBookmarks);
block = block.parent; 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(); tryCatchBookmarks.subList(start, tryCatchBookmarks.size()).clear();
@ -402,16 +377,31 @@ public class Decompiler {
private void createNewBookmarks(List<TryCatchBlock> tryCatchBlocks) { private void createNewBookmarks(List<TryCatchBlock> tryCatchBlocks) {
// Add new bookmarks // Add new bookmarks
Block previousRoot = null;
for (int i = tryCatchBookmarks.size(); i < tryCatchBlocks.size(); ++i) { for (int i = tryCatchBookmarks.size(); i < tryCatchBlocks.size(); ++i) {
TryCatchBlock tryCatch = tryCatchBlocks.get(tryCatchBlocks.size() - 1 - i); TryCatchBlock tryCatch = tryCatchBlocks.get(tryCatchBlocks.size() - 1 - i);
TryCatchBookmark bookmark = new TryCatchBookmark(); 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.exceptionHandler = tryCatch.getHandler().getIndex();
bookmark.exceptionType = tryCatch.getExceptionType(); bookmark.exceptionType = tryCatch.getExceptionType();
bookmark.exceptionVariable = tryCatch.getHandler().getExceptionVariable() != null bookmark.exceptionVariable = tryCatch.getHandler().getExceptionVariable() != null
? tryCatch.getHandler().getExceptionVariable().getIndex() : null; ? tryCatch.getHandler().getExceptionVariable().getIndex() : null;
bookmark.block.tryCatches.add(bookmark); block.tryCatches.add(bookmark);
tryCatchBookmarks.add(bookmark); tryCatchBookmarks.add(bookmark);
} }
} }
@ -421,26 +411,16 @@ public class Decompiler {
while (currentNode != null && currentNode.getStart() == start) { while (currentNode != null && currentNode.getStart() == start) {
Block block; Block block;
IdentifiedStatement statement; IdentifiedStatement statement;
boolean loop = false;
if (loopSuccessors[start] == currentNode.getEnd() || isSingleBlockLoop(start)) { if (loopSuccessors[start] == currentNode.getEnd() || isSingleBlockLoop(start)) {
WhileStatement whileStatement = new WhileStatement(); WhileStatement whileStatement = new WhileStatement();
statement = whileStatement; statement = whileStatement;
block = new Block(statement, whileStatement.getBody(), start, currentNode.getEnd()); block = new Block(statement, whileStatement.getBody(), start, currentNode.getEnd());
loop = true;
} else { } else {
BlockStatement blockStatement = new BlockStatement(); BlockStatement blockStatement = new BlockStatement();
statement = blockStatement; statement = blockStatement;
block = new Block(statement, blockStatement.getBody(), start, currentNode.getEnd()); block = new Block(statement, blockStatement.getBody(), start, currentNode.getEnd());
} }
result.add(block); 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; parentNode = currentNode;
currentNode = currentNode.getFirstChild(); currentNode = currentNode.getFirstChild();
} }
@ -477,17 +457,6 @@ public class Decompiler {
// For each node find head of loop this node belongs to. // 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<>(); List<RangeTree.Range> ranges = new ArrayList<>();
for (int node = 0; node < sz; ++node) { for (int node = 0; node < sz; ++node) {
@ -509,6 +478,5 @@ public class Decompiler {
} }
codeTree = new RangeTree(sz + 1, ranges); codeTree = new RangeTree(sz + 1, ranges);
this.loopSuccessors = loopSuccessors; this.loopSuccessors = loopSuccessors;
this.loops = loops;
} }
} }

View File

@ -40,6 +40,7 @@ import org.teavm.ast.SwitchStatement;
import org.teavm.ast.ThrowStatement; import org.teavm.ast.ThrowStatement;
import org.teavm.ast.UnaryOperation; import org.teavm.ast.UnaryOperation;
import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.WhileStatement;
import org.teavm.common.GraphIndexer; import org.teavm.common.GraphIndexer;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
@ -95,9 +96,7 @@ class StatementGenerator implements InstructionVisitor {
private int lastSwitchId; private int lastSwitchId;
final List<Statement> statements = new ArrayList<>(); final List<Statement> statements = new ArrayList<>();
GraphIndexer indexer; GraphIndexer indexer;
BasicBlock nextBlock; Decompiler.Block currentBlock;
BasicBlock currentBlock;
Decompiler.Block[] blockMap;
Program program; Program program;
ClassHolderSource classSource; ClassHolderSource classSource;
private TextLocation currentLocation; private TextLocation currentLocation;
@ -536,26 +535,32 @@ class StatementGenerator implements InstructionVisitor {
throw new IllegalArgumentException(type.toString()); throw new IllegalArgumentException(type.toString());
} }
Statement generateJumpStatement(BasicBlock target) { Statement generateJumpStatement(Decompiler.Block sourceBlock, BasicBlock target) {
if (nextBlock == target && blockMap[target.getIndex()] == null) { Decompiler.Block targetBlock = getTargetBlock(sourceBlock, target);
return null; if (targetBlock == null) {
} int targetIndex = indexer.indexOf(target.getIndex());
Decompiler.Block block = blockMap[target.getIndex()]; if (targetIndex >= sourceBlock.end) {
if (block == null) {
throw new IllegalStateException("Could not find block for basic block $" + target.getIndex()); throw new IllegalStateException("Could not find block for basic block $" + target.getIndex());
} }
if (target.getIndex() == indexer.nodeAt(block.end)) { return null;
}
if (target.getIndex() == indexer.nodeAt(targetBlock.end)) {
BreakStatement breakStmt = new BreakStatement(); BreakStatement breakStmt = new BreakStatement();
breakStmt.setLocation(currentLocation); breakStmt.setLocation(currentLocation);
breakStmt.setTarget(block.statement); breakStmt.setTarget(targetBlock.statement);
return breakStmt; return breakStmt;
} else { } else {
ContinueStatement contStmt = new ContinueStatement(); ContinueStatement contStmt = new ContinueStatement();
contStmt.setLocation(currentLocation); contStmt.setLocation(currentLocation);
contStmt.setTarget(block.statement); contStmt.setTarget(targetBlock.statement);
return contStmt; return contStmt;
} }
} }
Statement generateJumpStatement(BasicBlock target) {
return generateJumpStatement(currentBlock, target);
}
private Statement generateJumpStatement(SwitchStatement stmt, int target) { private Statement generateJumpStatement(SwitchStatement stmt, int target) {
Statement body = generateJumpStatement(program.basicBlockAt(target)); Statement body = generateJumpStatement(program.basicBlockAt(target));
if (body == null) { if (body == null) {
@ -566,6 +571,22 @@ class StatementGenerator implements InstructionVisitor {
return body; 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) { private void branch(Expr condition, BasicBlock consequentBlock, BasicBlock alternativeBlock) {
Statement consequent = generateJumpStatement(consequentBlock); Statement consequent = generateJumpStatement(consequentBlock);
Statement alternative = generateJumpStatement(alternativeBlock); Statement alternative = generateJumpStatement(alternativeBlock);

View File

@ -20,27 +20,22 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AbstractStatementVisitor;
import org.teavm.ast.BlockStatement; import org.teavm.ast.BlockStatement;
import org.teavm.ast.BreakStatement; import org.teavm.ast.BreakStatement;
import org.teavm.ast.ConditionalStatement; import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ContinueStatement; import org.teavm.ast.ContinueStatement;
import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement; 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.ReturnStatement;
import org.teavm.ast.SequentialStatement; import org.teavm.ast.SequentialStatement;
import org.teavm.ast.Statement; import org.teavm.ast.Statement;
import org.teavm.ast.StatementVisitor;
import org.teavm.ast.SwitchClause; import org.teavm.ast.SwitchClause;
import org.teavm.ast.SwitchStatement; import org.teavm.ast.SwitchStatement;
import org.teavm.ast.ThrowStatement; import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement; import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
class BreakEliminator implements StatementVisitor { class BreakEliminator extends AbstractStatementVisitor {
private Map<BlockStatement, List<Statement>> blockSuccessors = new LinkedHashMap<>(); private Map<BlockStatement, List<Statement>> blockSuccessors = new LinkedHashMap<>();
private Set<IdentifiedStatement> outerStatements = new LinkedHashSet<>(); private Set<IdentifiedStatement> outerStatements = new LinkedHashSet<>();
private List<Statement> currentSequence; private List<Statement> currentSequence;
@ -66,10 +61,6 @@ class BreakEliminator implements StatementVisitor {
currentSequence = oldSequence; currentSequence = oldSequence;
} }
@Override
public void visit(AssignmentStatement statement) {
}
@Override @Override
public void visit(SequentialStatement statement) { public void visit(SequentialStatement statement) {
if (currentSequence == null) { if (currentSequence == null) {
@ -145,10 +136,6 @@ class BreakEliminator implements StatementVisitor {
currentSequence.subList(currentIndex + 1, currentSequence.size()).clear(); currentSequence.subList(currentIndex + 1, currentSequence.size()).clear();
} }
@Override
public void visit(InitClassStatement statement) {
}
@Override @Override
public void visit(TryCatchStatement statement) { public void visit(TryCatchStatement statement) {
Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors; Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors;
@ -161,18 +148,6 @@ class BreakEliminator implements StatementVisitor {
processSequence(statement.getHandler()); 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) { private boolean escapes(List<Statement> statements) {
return new EscapingStatementFinder(usageCounter).check(statements); return new EscapingStatementFinder(usageCounter).check(statements);
} }

View File

@ -157,6 +157,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private boolean incremental; private boolean incremental;
private boolean lineNumbersGenerated; private boolean lineNumbersGenerated;
private SimpleStringPool stringPool; private SimpleStringPool stringPool;
private boolean longjmpUsed = true;
private List<CallSiteDescriptor> callSites = new ArrayList<>();
public void setMinHeapSize(int minHeapSize) { public void setMinHeapSize(int minHeapSize) {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
@ -170,6 +172,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
this.lineNumbersGenerated = lineNumbersGenerated; this.lineNumbersGenerated = lineNumbersGenerated;
} }
public void setLongjmpUsed(boolean longjmpUsed) {
this.longjmpUsed = longjmpUsed;
}
public void setAstCache(MethodNodeCache astCache) { public void setAstCache(MethodNodeCache astCache) {
this.astCache = astCache; this.astCache = astCache;
} }
@ -195,7 +201,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
characteristics = new Characteristics(controller.getUnprocessedClassSource()); characteristics = new Characteristics(controller.getUnprocessedClassSource());
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer(); classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(characteristics); shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
nullCheckInsertion = new NullCheckInsertion(characteristics); nullCheckInsertion = new NullCheckInsertion(characteristics);
nullCheckTransformation = new NullCheckTransformation(); nullCheckTransformation = new NullCheckTransformation();
@ -302,12 +308,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
public void afterOptimizations(Program program, MethodReader method) { public void afterOptimizations(Program program, MethodReader method) {
classInitializerEliminator.apply(program); classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program); classInitializerTransformer.transform(program);
if (!longjmpUsed) {
nullCheckTransformation.apply(program, method.getResultType()); nullCheckTransformation.apply(program, method.getResultType());
}
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads) new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
.apply(program, method.getReference()); .apply(program, method.getReference());
ShadowStackTransformer shadowStackTransformer = !incremental ShadowStackTransformer shadowStackTransformer = !incremental
? this.shadowStackTransformer ? this.shadowStackTransformer
: new ShadowStackTransformer(characteristics); : new ShadowStackTransformer(characteristics, !longjmpUsed);
shadowStackTransformer.apply(program, method); shadowStackTransformer.apply(program, method);
} }
@ -350,7 +358,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
stringPool = new SimpleStringPool(); stringPool = new SimpleStringPool();
GenerationContext context = new GenerationContext(vtableProvider, characteristics, GenerationContext context = new GenerationContext(vtableProvider, characteristics,
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes, 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 runtimeWriter = new BufferedCodeWriter(false);
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false); BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false);
@ -358,13 +366,19 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
runtimeHeaderWriter.println("#pragma once"); runtimeHeaderWriter.println("#pragma once");
if (incremental) { 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"); emitResource(runtimeHeaderWriter, "runtime.h");
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler, ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
controller.getCacheStatus()); controller.getCacheStatus());
classGenerator.setAstCache(astCache); classGenerator.setAstCache(astCache);
if (context.isLongjmp() && !context.isIncremental()) {
classGenerator.setCallSites(callSites);
}
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl( IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
controller.getUnprocessedClassSource(), controller.getClassLoader(), controller.getServices(), controller.getUnprocessedClassSource(), controller.getClassLoader(), controller.getServices(),
controller.getProperties()); controller.getProperties());
@ -515,14 +529,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
headerWriter.println("extern " + callSiteName + " teavm_callSites[];"); headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)"); headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)");
new CallSiteGenerator(context, writer, includes, "teavm_callSites") List<? extends CallSiteDescriptor> callSites = context.isLongjmp()
.generate(CallSiteDescriptor.extract(context.getClassSource(), classNames)); ? this.callSites
: CallSiteDescriptor.extract(context.getClassSource(), classNames);
new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites);
} }
private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) { private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) {
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE); String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (((" + callSiteName headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) ((" + callSiteName
+ "*) ((void**) frame)[3]) + id)"); + "*) ((TeaVM_StackFrame*) (frame))->callSites + id)");
} }
private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException { private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException {

View File

@ -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;
}
}
}

View File

@ -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);
}
}
};
}

View File

@ -18,6 +18,7 @@ package org.teavm.backend.c.generate;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -93,10 +94,12 @@ public class ClassGenerator {
private CodeWriter codeWriter; private CodeWriter codeWriter;
private CodeWriter initWriter; private CodeWriter initWriter;
private CodeWriter headerWriter; private CodeWriter headerWriter;
private CodeWriter callSitesWriter;
private IncludeManager includes; private IncludeManager includes;
private IncludeManager headerIncludes; private IncludeManager headerIncludes;
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE; private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor(); private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
private List<CallSiteDescriptor> callSites;
public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler, public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler,
CacheStatus cacheStatus) { CacheStatus cacheStatus) {
@ -110,6 +113,10 @@ public class ClassGenerator {
this.astCache = astCache; this.astCache = astCache;
} }
public void setCallSites(List<CallSiteDescriptor> callSites) {
this.callSites = callSites;
}
public void prepare(ListableClassHolderSource classes) { public void prepare(ListableClassHolderSource classes) {
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className); ClassHolder cls = classes.get(className);
@ -196,7 +203,7 @@ public class ClassGenerator {
} }
private void generateCallSites(List<? extends CallSiteDescriptor> callSites, String callSitesName) { 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.setStatic(true);
generator.generate(callSites); generator.generate(callSites);
} }
@ -224,6 +231,9 @@ public class ClassGenerator {
headerIncludes.includePath("runtime.h"); headerIncludes.includePath("runtime.h");
codeGenerator = new CodeGenerator(context, codeWriter, includes); codeGenerator = new CodeGenerator(context, codeWriter, includes);
if (context.isLongjmp() && !context.isIncremental()) {
codeGenerator.setCallSites(callSites);
}
String sysInitializerName = context.getNames().forClassSystemInitializer(type); String sysInitializerName = context.getNames().forClassSystemInitializer(type);
headerWriter.println("extern void " + sysInitializerName + "();"); headerWriter.println("extern void " + sysInitializerName + "();");
@ -279,17 +289,7 @@ public class ClassGenerator {
} }
if (context.isIncremental()) { if (context.isIncremental()) {
String callSitesName; callSitesWriter = codeWriter.fragment();
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 + ")");
} }
generateMethodForwardDeclaration(method); generateMethodForwardDeclaration(method);
@ -303,9 +303,19 @@ public class ClassGenerator {
methodNode = entry.method; methodNode = entry.method;
} }
List<CallSiteDescriptor> callSites = null;
if (context.isLongjmp()) {
if (context.isIncremental()) {
callSites = new ArrayList<>();
codeGenerator.setCallSites(callSites);
}
}
codeGenerator.generateMethod(methodNode); codeGenerator.generateMethod(methodNode);
if (context.isIncremental()) { if (context.isIncremental()) {
generateCallSites(method.getReference(),
context.isLongjmp() ? callSites : CallSiteDescriptor.extract(method.getProgram()));
codeWriter.println("#undef TEAVM_ALLOC_STACK"); codeWriter.println("#undef TEAVM_ALLOC_STACK");
} }
} }
@ -318,6 +328,19 @@ public class ClassGenerator {
headerWriter.println(";"); 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) { private void generateInitializer(ClassHolder cls) {
if (!needsInitializer(cls)) { if (!needsInitializer(cls)) {
return; return;

View File

@ -15,6 +15,12 @@
*/ */
package org.teavm.backend.c.generate; 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.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.DoubleBuffer; import java.nio.DoubleBuffer;
@ -24,10 +30,13 @@ import java.nio.LongBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.teavm.ast.ArrayType; import org.teavm.ast.ArrayType;
import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AssignmentStatement;
import org.teavm.ast.BinaryExpr; import org.teavm.ast.BinaryExpr;
@ -41,6 +50,7 @@ import org.teavm.ast.ContinueStatement;
import org.teavm.ast.Expr; import org.teavm.ast.Expr;
import org.teavm.ast.ExprVisitor; import org.teavm.ast.ExprVisitor;
import org.teavm.ast.GotoPartStatement; import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.InitClassStatement; import org.teavm.ast.InitClassStatement;
import org.teavm.ast.InstanceOfExpr; import org.teavm.ast.InstanceOfExpr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
@ -65,6 +75,7 @@ import org.teavm.ast.UnaryExpr;
import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; 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.Intrinsic;
import org.teavm.backend.c.intrinsic.IntrinsicContext; import org.teavm.backend.c.intrinsic.IntrinsicContext;
import org.teavm.backend.c.util.InteropUtil; 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.TextLocation;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTable; 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.Allocator;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
import org.teavm.runtime.Fiber;
import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject; import org.teavm.runtime.RuntimeObject;
@ -107,19 +120,31 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
Object.class, void.class); Object.class, void.class);
private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, "monitorExitSync", private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, "monitorExitSync",
Object.class, void.class); 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 static final Map<String, String> BUFFER_TYPES = new HashMap<>();
private GenerationContext context; private GenerationContext context;
private NameProvider names; private NameProvider names;
private CodeWriter writer; private CodeWriter writer;
private VolatileDefinitionFinder volatileDefinitions;
private int[] temporaryVariableLevel = new int[5]; private int[] temporaryVariableLevel = new int[5];
private IntSet spilledVariables = new IntHashSet();
private int[] maxTemporaryVariableLevel = new int[5]; private int[] maxTemporaryVariableLevel = new int[5];
private MethodReference callingMethod; private MethodReference callingMethod;
private IncludeManager includes; private IncludeManager includes;
private boolean end; private boolean end;
private boolean async; private boolean async;
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>(); 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 { static {
BUFFER_TYPES.put(ByteBuffer.class.getName(), "int8_t"); 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"); 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.context = context;
this.writer = writer; this.writer = writer;
this.names = context.getNames(); this.names = context.getNames();
this.includes = includes; this.includes = includes;
this.callSites = callSites;
this.volatileDefinitions = volatileDefinitions;
} }
public void setAsync(boolean async) { public void setAsync(boolean async) {
@ -146,8 +174,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
return maxTemporaryVariableLevel; return maxTemporaryVariableLevel;
} }
public IntContainer getSpilledVariables() {
return spilledVariables;
}
public void setCallingMethod(MethodReference callingMethod) { public void setCallingMethod(MethodReference callingMethod) {
this.callingMethod = callingMethod; this.callingMethod = callingMethod;
this.managed = context.getCharacteristics().isManaged(callingMethod);
} }
@Override @Override
@ -308,7 +341,9 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print(")"); writer.print(")");
break; break;
case NULL_CHECK: case NULL_CHECK:
writer.print("teavm_nullCheck(");
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
writer.print(")");
break; break;
case INT_TO_BYTE: case INT_TO_BYTE:
writer.print("TEAVM_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 @Override
public void visit(InvocationExpr expr) { public void visit(InvocationExpr expr) {
ClassReader cls = context.getClassSource().get(expr.getMethod().getClassName()); 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()); Intrinsic intrinsic = context.getIntrinsic(expr.getMethod());
if (intrinsic != null) { if (intrinsic != null) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
if (needsCallSiteId() && isManagedMethodCall(context.getCharacteristics(), expr.getMethod())) {
needParenthesis = true;
withCallSite();
}
intrinsic.apply(intrinsicContext, expr); intrinsic.apply(intrinsicContext, expr);
popLocation(expr.getLocation()); popLocation(expr.getLocation());
if (needParenthesis) {
writer.print(")");
}
return; return;
} }
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
if (needsCallSiteId() && context.getCharacteristics().isManaged(expr.getMethod())) {
needParenthesis = true;
withCallSite();
}
switch (expr.getType()) { switch (expr.getType()) {
case CONSTRUCTOR: case CONSTRUCTOR:
generateCallToConstructor(expr.getMethod(), expr.getArguments()); generateCallToConstructor(expr.getMethod(), expr.getArguments());
@ -441,9 +495,34 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
} }
if (needParenthesis) {
writer.print(")");
}
popLocation(expr.getLocation()); 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) { private void generateCallToConstructor(MethodReference reference, List<? extends Expr> arguments) {
String receiver = allocTemporaryVariable(CVariableType.PTR); String receiver = allocTemporaryVariable(CVariableType.PTR);
writer.print("(" + receiver + " = "); writer.print("(" + receiver + " = ");
@ -751,9 +830,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(NewExpr expr) { public void visit(NewExpr expr) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
includes.includeClass(expr.getConstructedClass()); includes.includeClass(expr.getConstructedClass());
includes.includeClass(ALLOC_METHOD.getClassName()); includes.includeClass(ALLOC_METHOD.getClassName());
allocObject(expr.getConstructedClass()); allocObject(expr.getConstructedClass());
if (needParenthesis) {
writer.print(")");
}
popLocation(expr.getLocation()); popLocation(expr.getLocation());
} }
@ -766,6 +853,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(NewArrayExpr expr) { public void visit(NewArrayExpr expr) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
ValueType type = ValueType.arrayOf(expr.getType()); ValueType type = ValueType.arrayOf(expr.getType());
writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&") writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
.print(names.forClassInstance(type)).print(", "); .print(names.forClassInstance(type)).print(", ");
@ -773,11 +867,24 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
includes.includeType(type); includes.includeType(type);
expr.getLength().acceptVisitor(this); expr.getLength().acceptVisitor(this);
writer.print(")"); writer.print(")");
if (needParenthesis) {
writer.print(")");
}
popLocation(expr.getLocation()); popLocation(expr.getLocation());
} }
@Override @Override
public void visit(NewMultiArrayExpr expr) { 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("(&") writer.print(names.forMethod(ALLOC_MULTI_ARRAY_METHOD)).print("(&")
.print(names.forClassInstance(expr.getType())).print(", "); .print(names.forClassInstance(expr.getType())).print(", ");
includes.includeClass(ALLOC_ARRAY_METHOD.getClassName()); 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(")"); writer.print("}, ").print(String.valueOf(expr.getDimensions().size())).print(")");
if (needParenthesis) {
writer.print(")");
}
popLocation(expr.getLocation());
} }
@Override @Override
@ -813,11 +926,24 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
return; return;
} }
} }
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
writer.print("teavm_checkcast("); writer.print("teavm_checkcast(");
expr.getValue().acceptVisitor(this); expr.getValue().acceptVisitor(this);
includes.includeType(expr.getTarget()); includes.includeType(expr.getTarget());
writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")"); writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")");
if (needParenthesis) {
writer.print(")");
}
popLocation(expr.getLocation()); popLocation(expr.getLocation());
} }
@ -848,6 +974,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(AssignmentStatement statement) { public void visit(AssignmentStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
if (statement.getLeftValue() != null) { if (statement.getLeftValue() != null) {
if (statement.getLeftValue() instanceof QualificationExpr) { if (statement.getLeftValue() instanceof QualificationExpr) {
QualificationExpr qualification = (QualificationExpr) statement.getLeftValue(); QualificationExpr qualification = (QualificationExpr) statement.getLeftValue();
@ -871,8 +998,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
statement.getRightValue().acceptVisitor(this); statement.getRightValue().acceptVisitor(this);
writer.println(";"); writer.println(";");
if (statement.isAsync()) { if (volatileDefinitions.shouldBackup(statement)) {
emitSuspendChecker(); VariableExpr lhs = (VariableExpr) statement.getLeftValue();
spilledVariables.add(lhs.getIndex());
writer.println("teavm_spill_" + lhs.getIndex() + " = " + getVariableName(lhs.getIndex()) + ";");
} }
popLocation(statement.getLocation()); popLocation(statement.getLocation());
@ -929,6 +1058,12 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(SwitchStatement statement) { public void visit(SwitchStatement statement) {
IdentifiedStatement oldDefaultBreakTarget = defaultBreakTarget;
defaultBreakTarget = statement;
int statementId = labelMap.size() + 1;
labelMap.put(statement, statementId);
pushLocation(statement.getValue().getLocation()); pushLocation(statement.getValue().getLocation());
writer.print("switch ("); writer.print("switch (");
statement.getValue().acceptVisitor(this); statement.getValue().acceptVisitor(this);
@ -958,13 +1093,23 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.outdent().println("}"); writer.outdent().println("}");
if (statement.getId() != null) { if (usedAsBreakTarget.contains(statement)) {
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent(); writer.outdent().println("teavm_label_" + statementId + ":;").indent();
} }
defaultBreakTarget = oldDefaultBreakTarget;
} }
@Override @Override
public void visit(WhileStatement statement) { 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 ("); writer.print("while (");
if (statement.getCondition() != null) { if (statement.getCondition() != null) {
statement.getCondition().acceptVisitor(this); statement.getCondition().acceptVisitor(this);
@ -980,44 +1125,54 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
end = oldEnd; end = oldEnd;
if (statement.getId() != null) { if (usedAsContinueTarget.contains(statement)) {
writer.outdent().println("teavm_cnt_" + statement.getId() + ":;").indent(); writer.outdent().println("teavm_cnt_" + statementId + ":;").indent();
} }
writer.outdent().println("}"); writer.outdent().println("}");
if (statement.getId() != null) { if (usedAsBreakTarget.contains(statement)) {
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent(); writer.outdent().println("teavm_label_" + statementId + ":;").indent();
} }
defaultContinueTarget = oldDefaultContinueTarget;
defaultBreakTarget = oldDefaultBreakTarget;
} }
@Override @Override
public void visit(BlockStatement statement) { public void visit(BlockStatement statement) {
int statementId = labelMap.size() + 1;
labelMap.put(statement, statementId);
visitMany(statement.getBody()); visitMany(statement.getBody());
if (statement.getId() != null) { if (usedAsBreakTarget.contains(statement)) {
writer.outdent().println("teavm_label_" + statement.getId() + ":;").indent(); writer.outdent().println("teavm_label_" + statementId + ":;").indent();
} }
} }
@Override @Override
public void visit(BreakStatement statement) { public void visit(BreakStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
if (statement.getTarget() == null || statement.getTarget().getId() == null) { IdentifiedStatement target = statement.getTarget();
writer.println("break;"); if (target == null) {
} else { target = defaultBreakTarget;
writer.println("goto teavm_label_" + statement.getTarget().getId() + ";");
} }
int id = labelMap.get(target);
writer.println("goto teavm_label_" + id + ";");
usedAsBreakTarget.add(target);
popLocation(statement.getLocation()); popLocation(statement.getLocation());
} }
@Override @Override
public void visit(ContinueStatement statement) { public void visit(ContinueStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
if (statement.getTarget() == null || statement.getTarget().getId() == null) { IdentifiedStatement target = statement.getTarget();
writer.println("continue;"); if (target == null) {
} else { target = defaultContinueTarget;
writer.println("goto teavm_cnt_" + statement.getTarget().getId() + ";");
} }
int id = labelMap.get(target);
writer.println("goto teavm_cnt_" + id + ";");
usedAsContinueTarget.add(target);
popLocation(statement.getLocation()); popLocation(statement.getLocation());
} }
@ -1036,23 +1191,99 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(ThrowStatement statement) { public void visit(ThrowStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
includes.includeClass(THROW_EXCEPTION_METHOD.getClassName()); includes.includeClass(THROW_EXCEPTION_METHOD.getClassName());
writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("("); writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("(");
statement.getException().acceptVisitor(this); statement.getException().acceptVisitor(this);
writer.println(");"); writer.print(")");
if (needParenthesis) {
writer.print(")");
}
writer.println(";");
popLocation(statement.getLocation()); popLocation(statement.getLocation());
} }
@Override @Override
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
includes.includeClass(statement.getClassName()); 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()); popLocation(statement.getLocation());
} }
@Override @Override
public void visit(TryCatchStatement statement) { 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 @Override
@ -1062,26 +1293,47 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(MonitorEnterStatement statement) { public void visit(MonitorEnterStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
includes.includeClass("java.lang.Object"); includes.includeClass("java.lang.Object");
writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("("); writer.print(names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)).print("(");
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
writer.println(");"); writer.print(")");
if (needParenthesis) {
writer.print(")");
}
writer.println(";");
popLocation(statement.getLocation()); popLocation(statement.getLocation());
} }
@Override @Override
public void visit(MonitorExitStatement statement) { public void visit(MonitorExitStatement statement) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
includes.includeClass("java.lang.Object"); includes.includeClass("java.lang.Object");
writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("("); writer.print(names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)).print("(");
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
writer.println(");"); writer.print(")");
popLocation(statement.getLocation());
}
public void emitSuspendChecker() { if (needParenthesis) {
String suspendingName = names.forMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class)); writer.print(")");
writer.println("if (" + suspendingName + "(fiber)) goto teavm_exit_loop;"); }
writer.println(";");
popLocation(statement.getLocation());
} }
private IntrinsicContext intrinsicContext = new IntrinsicContext() { private IntrinsicContext intrinsicContext = new IntrinsicContext() {

View File

@ -15,12 +15,16 @@
*/ */
package org.teavm.backend.c.generate; package org.teavm.backend.c.generate;
import com.carrotsearch.hppc.IntContainer;
import java.util.List;
import org.teavm.ast.MethodNode; import org.teavm.ast.MethodNode;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode; import org.teavm.ast.VariableNode;
import org.teavm.backend.c.analyze.VolatileDefinitionFinder;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.lowlevel.CallSiteDescriptor;
public class CodeGenerator { public class CodeGenerator {
private GenerationContext context; private GenerationContext context;
@ -28,6 +32,7 @@ public class CodeGenerator {
private CodeWriter localsWriter; private CodeWriter localsWriter;
private NameProvider names; private NameProvider names;
private IncludeManager includes; private IncludeManager includes;
private List<CallSiteDescriptor> callSites;
public CodeGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes) { public CodeGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes) {
this.context = context; this.context = context;
@ -36,6 +41,10 @@ public class CodeGenerator {
this.includes = includes; this.includes = includes;
} }
public void setCallSites(List<CallSiteDescriptor> callSites) {
this.callSites = callSites;
}
public void generateMethod(RegularMethodNode methodNode) { public void generateMethod(RegularMethodNode methodNode) {
generateMethodSignature(writer, methodNode.getReference(), generateMethodSignature(writer, methodNode.getReference(),
methodNode.getModifiers().contains(ElementModifier.STATIC), true); methodNode.getModifiers().contains(ElementModifier.STATIC), true);
@ -44,13 +53,16 @@ public class CodeGenerator {
localsWriter = writer.fragment(); localsWriter = writer.fragment();
CodeGenerationVisitor visitor = generateMethodBody(methodNode); CodeGenerationVisitor visitor = generateMethodBody(methodNode);
generateLocals(methodNode, visitor.getTemporaries()); generateLocals(methodNode, visitor.getTemporaries(), visitor.getSpilledVariables());
writer.outdent().println("}"); writer.outdent().println("}");
} }
private CodeGenerationVisitor generateMethodBody(RegularMethodNode methodNode) { 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.setAsync(context.isAsync(methodNode.getReference()));
visitor.setCallingMethod(methodNode.getReference()); visitor.setCallingMethod(methodNode.getReference());
methodNode.getBody().acceptVisitor(visitor); 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; int start = methodNode.getReference().parameterCount() + 1;
for (int i = start; i < methodNode.getVariables().size(); ++i) { for (int i = start; i < methodNode.getVariables().size(); ++i) {
VariableNode variableNode = methodNode.getVariables().get(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)) localsWriter.printType(variableNode.getType()).print(" teavm_local_").print(String.valueOf(i))
.println(";"); .println(";");
if (spilledVariables.contains(i)) {
localsWriter.print("volatile ").printType(variableNode.getType()).print(" teavm_spill_")
.print(String.valueOf(i)).println(";");
}
} }
for (CVariableType type : CVariableType.values()) { for (CVariableType type : CVariableType.values()) {

View File

@ -44,11 +44,13 @@ public class GenerationContext {
private Predicate<MethodReference> asyncMethods; private Predicate<MethodReference> asyncMethods;
private BuildTarget buildTarget; private BuildTarget buildTarget;
private boolean incremental; private boolean incremental;
private boolean longjmp;
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics, public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics, DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators, 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.virtualTableProvider = virtualTableProvider;
this.characteristics = characteristics; this.characteristics = characteristics;
this.dependencies = dependencies; this.dependencies = dependencies;
@ -61,6 +63,7 @@ public class GenerationContext {
this.asyncMethods = asyncMethods; this.asyncMethods = asyncMethods;
this.buildTarget = buildTarget; this.buildTarget = buildTarget;
this.incremental = incremental; this.incremental = incremental;
this.longjmp = longjmp;
} }
public void addIntrinsic(Intrinsic intrinsic) { public void addIntrinsic(Intrinsic intrinsic) {
@ -124,4 +127,8 @@ public class GenerationContext {
public boolean isIncremental() { public boolean isIncremental() {
return incremental; return incremental;
} }
public boolean isLongjmp() {
return longjmp;
}
} }

View File

@ -28,6 +28,9 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
switch (method.getName()) { switch (method.getName()) {
case "findCallSiteById": case "findCallSiteById":
case "isJumpSupported":
case "jumpToFrame":
case "abort":
return true; return true;
default: default:
return false; return false;
@ -45,6 +48,23 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
context.emit(invocation.getArguments().get(1)); context.emit(invocation.getArguments().get(1));
context.writer().print(")"); context.writer().print(")");
break; 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;
} }
} }
} }

View File

@ -73,9 +73,9 @@ public class ShadowStackIntrinsic implements Intrinsic {
context.writer().print("teavm_stackTop"); context.writer().print("teavm_stackTop");
return; return;
case "getNextStackFrame": case "getNextStackFrame":
context.writer().print("((void**) "); context.writer().print("TEAVM_GET_NEXT_FRAME(");
context.emit(invocation.getArguments().get(0)); context.emit(invocation.getArguments().get(0));
context.writer().print(")[0]"); context.writer().print(")");
return; return;
case "getStackRootCount": case "getStackRootCount":
context.writer().print("TEAVM_GC_ROOTS_COUNT("); context.writer().print("TEAVM_GC_ROOTS_COUNT(");
@ -89,9 +89,9 @@ public class ShadowStackIntrinsic implements Intrinsic {
return; return;
} }
case "getCallSiteId": 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.emit(invocation.getArguments().get(0));
context.writer().print(")[1])"); context.writer().print(")");
return; return;
} }

View File

@ -1447,7 +1447,8 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
.softNewLine(); .softNewLine();
boolean first = true; boolean first = true;
boolean defaultHandlerOccurred = false; boolean defaultHandlerOccurred = false;
for (TryCatchStatement catchClause : sequence) { for (int i = sequence.size() - 1; i >= 0; --i) {
TryCatchStatement catchClause = sequence.get(i);
if (!first) { if (!first) {
writer.ws().append("else"); writer.ws().append("else");
} }

View File

@ -165,7 +165,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
controller.getUnprocessedClassSource()); controller.getUnprocessedClassSource());
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer(); classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository); shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository, true);
} }
@Override @Override

View File

@ -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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; 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.MethodReference;
import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.runtime.CallSite; import org.teavm.runtime.CallSite;
@ -50,6 +51,9 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
} }
switch (methodReference.getName()) { switch (methodReference.getName()) {
case "findCallSiteById": case "findCallSiteById":
case "isJumpSupported":
case "jumpToFrame":
case "abort":
return true; return true;
} }
return false; return false;
@ -64,6 +68,8 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) {
case "findCallSiteById": {
WasmInt32Constant constant = new WasmInt32Constant(0); WasmInt32Constant constant = new WasmInt32Constant(0);
constant.setLocation(invocation.getLocation()); constant.setLocation(invocation.getLocation());
constants.add(constant); constants.add(constant);
@ -75,4 +81,16 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
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());
}
}
} }

View File

@ -96,7 +96,13 @@ public class RangeTree {
}); });
Deque<NodeImpl> stack = new ArrayDeque<>(); Deque<NodeImpl> stack = new ArrayDeque<>();
stack.push(root); stack.push(root);
Range lastRange = null;
for (Range range : rangeList) { for (Range range : rangeList) {
if (lastRange != null && lastRange.left == range.left && lastRange.right == range.right) {
continue;
}
NodeImpl current = new NodeImpl(); NodeImpl current = new NodeImpl();
current.start = range.left; current.start = range.left;
current.end = range.right; current.end = range.right;
@ -114,6 +120,7 @@ public class RangeTree {
ancestor.start = current.start; ancestor.start = current.start;
} }
stack.push(current); stack.push(current);
lastRange = range;
} }
} }

View File

@ -272,21 +272,28 @@ public class ExceptionHandlingShadowStackContributor {
} }
private boolean isCallInstruction(Instruction insn) { private boolean isCallInstruction(Instruction insn) {
return isCallInstruction(characteristics, insn);
}
public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) {
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction || insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction) { || insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction) {
return true; return true;
} else if (insn instanceof InvokeInstruction) { } else if (insn instanceof InvokeInstruction) {
MethodReference method = ((InvokeInstruction) insn).getMethod(); return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
}
return false;
}
public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) {
if (characteristics.isManaged(method)) { if (characteristics.isManaged(method)) {
return true; return true;
} }
return method.getClassName().equals(ExceptionHandling.class.getName()) return method.getClassName().equals(ExceptionHandling.class.getName())
&& method.getName().startsWith("throw"); && method.getName().startsWith("throw");
} }
return false;
}
private boolean isSpecialCallInstruction(Instruction insn) { private boolean isSpecialCallInstruction(Instruction insn) {
if (!(insn instanceof InvokeInstruction)) { if (!(insn instanceof InvokeInstruction)) {

View File

@ -24,22 +24,25 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.Variable; import org.teavm.model.Variable;
import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.util.BasicBlockMapper;
import org.teavm.runtime.ShadowStack; import org.teavm.runtime.ShadowStack;
public class ShadowStackTransformer { public class ShadowStackTransformer {
private Characteristics characteristics; private Characteristics characteristics;
private GCShadowStackContributor gcContributor; 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); gcContributor = new GCShadowStackContributor(characteristics);
this.characteristics = characteristics; this.characteristics = characteristics;
this.exceptionHandling = exceptionHandling;
} }
public void apply(Program program, MethodReader method) { public void apply(Program program, MethodReader method) {
@ -48,11 +51,27 @@ public class ShadowStackTransformer {
} }
int shadowStackSize = gcContributor.contribute(program, method); int shadowStackSize = gcContributor.contribute(program, method);
int callSiteStartIndex = callSites.size(); boolean exceptions;
boolean exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites, if (exceptionHandling) {
List<CallSiteDescriptor> callSites = new ArrayList<>();
exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
method.getReference(), program).contribute(); method.getReference(), program).contribute();
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size()); CallSiteDescriptor.save(callSites, program.getAnnotations());
CallSiteDescriptor.save(programCallSites, 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) { if (shadowStackSize > 0 || exceptions) {
addStackAllocation(program, shadowStackSize); addStackAllocation(program, shadowStackSize);
@ -62,6 +81,10 @@ public class ShadowStackTransformer {
private void addStackAllocation(Program program, int maxDepth) { private void addStackAllocation(Program program, int maxDepth) {
BasicBlock block = program.basicBlockAt(0); BasicBlock block = program.basicBlockAt(0);
if (!block.getTryCatchBlocks().isEmpty()) {
splitFirstBlock(program);
}
List<Instruction> instructionsToAdd = new ArrayList<>(); List<Instruction> instructionsToAdd = new ArrayList<>();
Variable sizeVariable = program.createVariable(); Variable sizeVariable = program.createVariable();
@ -79,6 +102,26 @@ public class ShadowStackTransformer {
block.addFirstAll(instructionsToAdd); 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) { private void addStackRelease(Program program, int maxDepth) {
List<BasicBlock> blocks = new ArrayList<>(); List<BasicBlock> blocks = new ArrayList<>();
boolean hasResult = false; boolean hasResult = false;

View File

@ -17,7 +17,9 @@ package org.teavm.model.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.common.DisjointSet; import org.teavm.common.DisjointSet;
@ -151,17 +153,103 @@ public class RegisterAllocator {
} }
private void insertPhiArgumentsCopies(Program program) { 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<>(); 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()) { for (Incoming incoming : phi.getIncomings()) {
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 (Incoming incoming : incomingsToRepeat) {
insertCopy(incoming, blockMap); insertCopy(incoming, blockMap);
} }
} }
for (Phi phi : program.basicBlockAt(i).getPhis()) {
for (Incoming incoming : phi.getIncomings()) { DefinitionExtractor definitionExtractor = new DefinitionExtractor();
insertCopy(incoming, blockMap); 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);
} }
} }
} }

View File

@ -93,7 +93,6 @@ import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.SwitchInstruction; import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.SwitchTableEntry; import org.teavm.model.instructions.SwitchTableEntry;
import org.teavm.model.instructions.UnwrapArrayInstruction; import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.ProgramUtils;
import org.teavm.model.util.TransitionExtractor; import org.teavm.model.util.TransitionExtractor;
public class ProgramParser { public class ProgramParser {
@ -185,8 +184,6 @@ public class ProgramParser {
while (program.variableCount() <= signatureVars) { while (program.variableCount() <= signatureVars) {
program.createVariable(); program.createVariable();
} }
program.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(
program.basicBlockAt(1), program));
return program; return program;
} }

View File

@ -47,7 +47,6 @@ public final class Allocator {
return result; return result;
} }
@Unmanaged
public static RuntimeArray allocateMultiArray(RuntimeClass tag, Address dimensions, int dimensionCount) { public static RuntimeArray allocateMultiArray(RuntimeClass tag, Address dimensions, int dimensionCount) {
int size = dimensions.getInt(); int size = dimensions.getInt();
RuntimeArray array = allocateArray(tag, dimensions.getInt()).toStructure(); RuntimeArray array = allocateArray(tag, dimensions.getInt()).toStructure();

View File

@ -55,7 +55,7 @@ public final class EventQueue {
} }
public static void kill(int id) { 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) { if (data[i].id == id) {
remove(i); remove(i);
break; break;

View File

@ -25,15 +25,26 @@ public final class ExceptionHandling {
private ExceptionHandling() { private ExceptionHandling() {
} }
@Unmanaged
public static native CallSite findCallSiteById(int id, Address frame); 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() { public static void printStack() {
Address stackFrame = ShadowStack.getStackTop(); Address stackFrame = ShadowStack.getStackTop();
while (stackFrame != null) { while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame); int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location; CallSiteLocation location = callSite.location;
MethodLocation methodLocation = location.method; MethodLocation methodLocation = location != null ? location.method : null;
Console.printString(" at "); Console.printString(" at ");
if (methodLocation.className == null || methodLocation.methodName == null) { if (methodLocation.className == null || methodLocation.methodName == null) {
@ -56,7 +67,7 @@ public final class ExceptionHandling {
private static Throwable thrownException; private static Throwable thrownException;
@Export(name = "sys_catchException") @Export(name = "teavm_catchException")
@Unmanaged @Unmanaged
public static Throwable catchException() { public static Throwable catchException() {
Throwable exception = thrownException; Throwable exception = thrownException;
@ -72,6 +83,7 @@ public final class ExceptionHandling {
RuntimeClass exceptionClass = RuntimeClass.getClass(exceptionPtr); RuntimeClass exceptionClass = RuntimeClass.getClass(exceptionPtr);
Address stackFrame = ShadowStack.getStackTop(); Address stackFrame = ShadowStack.getStackTop();
int handlerId = 0;
stackLoop: while (stackFrame != null) { stackLoop: while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame); int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame);
@ -79,6 +91,7 @@ public final class ExceptionHandling {
while (handler != null) { while (handler != null) {
if (handler.exceptionClass == null || handler.exceptionClass.isSupertypeOf.apply(exceptionClass)) { if (handler.exceptionClass == null || handler.exceptionClass.isSupertypeOf.apply(exceptionClass)) {
handlerId = handler.id;
ShadowStack.setExceptionHandlerId(stackFrame, handler.id); ShadowStack.setExceptionHandlerId(stackFrame, handler.id);
break stackLoop; break stackLoop;
} }
@ -88,6 +101,13 @@ public final class ExceptionHandling {
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId - 1); ShadowStack.setExceptionHandlerId(stackFrame, callSiteId - 1);
stackFrame = ShadowStack.getNextStackFrame(stackFrame); stackFrame = ShadowStack.getNextStackFrame(stackFrame);
} }
if (stackFrame == null) {
printStack();
abort();
} else if (isJumpSupported()) {
jumpToFrame(stackFrame, handlerId);
}
} }
@Unmanaged @Unmanaged
@ -96,6 +116,7 @@ public final class ExceptionHandling {
} }
@Unmanaged @Unmanaged
@Export(name = "teavm_throwNullPointerException")
public static void throwNullPointerException() { public static void throwNullPointerException() {
throw new NullPointerException(); throw new NullPointerException();
} }
@ -119,11 +140,11 @@ public final class ExceptionHandling {
int callSiteId = ShadowStack.getCallSiteId(stackFrame); int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location; CallSiteLocation location = callSite.location;
MethodLocation methodLocation = location.method; MethodLocation methodLocation = location != null ? location.method : null;
StackTraceElement element = createElement( StackTraceElement element = createElement(
location != null && methodLocation.className != null ? methodLocation.className.value : "", methodLocation != null && methodLocation.className != null ? methodLocation.className.value : "",
location != null && methodLocation.methodName != null ? methodLocation.methodName.value : "", methodLocation != null && methodLocation.methodName != null ? methodLocation.methodName.value : "",
location != null && methodLocation.fileName != null ? methodLocation.fileName.value : null, methodLocation != null && methodLocation.fileName != null ? methodLocation.fileName.value : null,
location != null ? location.lineNumber : -1); location != null ? location.lineNumber : -1);
target[index++] = element; target[index++] = element;
stackFrame = ShadowStack.getNextStackFrame(stackFrame); stackFrame = ShadowStack.getNextStackFrame(stackFrame);

View File

@ -16,7 +16,11 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
@Unmanaged
@StaticInit
final class MarkQueue { final class MarkQueue {
private MarkQueue() { private MarkQueue() {
} }

View File

@ -11,6 +11,7 @@
#include <dirent.h> #include <dirent.h>
#include <utime.h> #include <utime.h>
#include <pwd.h> #include <pwd.h>
#include <string.h>
int32_t teavm_file_homeDirectory(char16_t** result) { int32_t teavm_file_homeDirectory(char16_t** result) {
struct passwd *pw = getpwuid(getuid()); struct passwd *pw = getpwuid(getuid());

View File

@ -24,7 +24,7 @@
void* teavm_gc_heapAddress = NULL; void* teavm_gc_heapAddress = NULL;
void** teavm_stackTop; TeaVM_StackFrame* teavm_stackTop = NULL;
void* teavm_gc_gcStorageAddress = NULL; void* teavm_gc_gcStorageAddress = NULL;
int32_t teavm_gc_gcStorageSize = INT32_C(0); int32_t teavm_gc_gcStorageSize = INT32_C(0);

View File

@ -4,6 +4,10 @@
#include <stddef.h> #include <stddef.h>
#include <math.h> #include <math.h>
#ifdef TEAVM_USE_SETJMP
#include <setjmp.h>
#endif
#ifdef __GNUC__ #ifdef __GNUC__
#include <stdalign.h> #include <stdalign.h>
#include <signal.h> #include <signal.h>
@ -58,6 +62,18 @@ typedef struct TeaVM_String {
int32_t hashCode; int32_t hashCode;
} TeaVM_String; } 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 void* teavm_gc_heapAddress;
extern char *teavm_beforeClasses; 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); 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) { 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) { 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))) #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 #ifdef TEAVM_INCREMENTAL
#define TEAVM_ALLOC_STACK_DEF(size, callSites) \ #define TEAVM_ALLOC_STACK_DEF(sz, cs) \
void* teavm__shadowStack__[(size) + 4]; \ struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
teavm__shadowStack__[0] = teavm_stackTop; \ teavm_shadowStack.header.next = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \ teavm_shadowStack.header.callSites = (cs); \
teavm__shadowStack__[3] = (void*) (callSites); \ teavm_shadowStack.header.size = (sz); \
teavm_stackTop = teavm__shadowStack__ teavm_stackTop = &teavm_shadowStack.header
#define TEAVM_STACK_HEADER_ADD_SIZE 1 #define TEAVM_STACK_HEADER_ADD_SIZE 1
#else #else
#define TEAVM_ALLOC_STACK(size) \ #define TEAVM_ALLOC_STACK(sz) \
void* teavm__shadowStack__[(size) + 3]; \ struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
teavm__shadowStack__[0] = teavm_stackTop; \ teavm_shadowStack.header.next = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \ teavm_shadowStack.header.size = (sz); \
teavm_stackTop = teavm__shadowStack__ teavm_stackTop = &teavm_shadowStack.header
#define TEAVM_STACK_HEADER_ADD_SIZE 0 #define TEAVM_STACK_HEADER_ADD_SIZE 0
#endif #endif
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0] #define TEAVM_RELEASE_STACK (teavm_stackTop = teavm_shadowStack.header.next)
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = ptr #define TEAVM_GC_ROOT(index, ptr) teavm_shadowStack.data[index] = ptr
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = NULL #define TEAVM_GC_ROOT_RELEASE(index) teavm_shadowStack.data[index] = NULL
#define TEAVM_GC_ROOTS_COUNT(ptr) ((int32_t) (intptr_t) ((void**) (ptr))[2]) #define TEAVM_GC_ROOTS_COUNT(ptr) (((TeaVM_StackFrame*) (ptr))->size);
#define TEAVM_GET_GC_ROOTS(ptr) (((void**) (ptr)) + 3 + TEAVM_STACK_HEADER_ADD_SIZE) #define TEAVM_GET_GC_ROOTS(ptr) &((struct { TeaVM_StackFrame header; void* data[1]; }*) (ptr))->data;
#define TEAVM_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id)) #define TEAVM_CALL_SITE(id) (teavm_shadowStack.header.callSiteId = (id))
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1])) #define TEAVM_WITH_CALL_SITE_ID(id, expr) (TEAVM_CALL_SITE(id), (expr))
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id)) #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_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (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) \ .hashCode = INT32_C(hash) \
} }
extern void** teavm_stackTop; extern TeaVM_StackFrame* teavm_stackTop;
extern void* teavm_gc_gcStorageAddress; extern void* teavm_gc_gcStorageAddress;
extern int32_t teavm_gc_gcStorageSize; extern int32_t teavm_gc_gcStorageSize;
@ -360,3 +379,48 @@ extern int64_t teavm_unixTimeOffset;
#endif #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();

View File

@ -32,6 +32,7 @@ public interface PlatformClassMetadata extends JSObject {
PlatformClass getSuperclass(); PlatformClass getSuperclass();
@JSProperty @JSProperty
@Unmanaged
String getName(); String getName();
@JSProperty @JSProperty

View File

@ -1,4 +0,0 @@
/CMakeFiles
/CMakeCache.txt
/Makefile
/cmake_install.cmake

View File

@ -1,2 +1,3 @@
export LC_ALL=C
SOURCE_DIR=$(pwd) SOURCE_DIR=$(pwd)
gcc -g -O0 -lrt -lm all.c -o run_test gcc -g -O0 -lrt -lm all.c -o run_test

View File

@ -335,18 +335,21 @@ public class ByteBufferTest {
byte[] receiver = new byte[4]; byte[] receiver = new byte[4];
try { try {
buffer.get(receiver, 0, 5); buffer.get(receiver, 0, 5);
fail("Error expected");
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
assertThat(receiver, is(new byte[4])); assertThat(receiver, is(new byte[4]));
assertThat(buffer.position(), is(0)); assertThat(buffer.position(), is(0));
} }
try { try {
buffer.get(receiver, -1, 3); buffer.get(receiver, -1, 3);
fail("Error expected");
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
assertThat(receiver, is(new byte[4])); assertThat(receiver, is(new byte[4]));
assertThat(buffer.position(), is(0)); assertThat(buffer.position(), is(0));
} }
try { try {
buffer.get(receiver, 6, 3); buffer.get(receiver, 6, 3);
fail("Error expected");
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
assertThat(receiver, is(new byte[4])); assertThat(receiver, is(new byte[4]));
assertThat(buffer.position(), is(0)); assertThat(buffer.position(), is(0));

View File

@ -20,6 +20,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -116,19 +118,19 @@ public class VMTest {
@Test @Test
public void catchFinally() { public void catchFinally() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
try { List<String> a = Arrays.asList("a", "b");
if (Integer.parseInt("invalid") > 0) { if (a.isEmpty()) {
sb.append("err1;"); return;
} else { }
sb.append("err2;"); try {
for (String b : a) {
if (b.length() < 3) {
sb.append(b);
}
} }
sb.append("err3");
} catch (NumberFormatException e) {
sb.append("catch;");
} finally { } finally {
sb.append("finally;"); sb.append("finally;");
} }
assertEquals("catch;finally;", sb.toString());
} }
@Test @Test
@ -195,7 +197,7 @@ public class VMTest {
n += foo() * 5; n += foo() * 5;
} catch (RuntimeException e) { } catch (RuntimeException e) {
assertEquals(RuntimeException.class, e.getClass()); assertEquals(RuntimeException.class, e.getClass());
assertEquals(n, 22); assertEquals(22, n);
} }
} }

View File

@ -63,6 +63,7 @@ public class IncrementalCBuilder {
private String mainClass; private String mainClass;
private String[] classPath; private String[] classPath;
private int minHeapSize = 32; private int minHeapSize = 32;
private boolean longjmpSupported = true;
private boolean lineNumbersGenerated; private boolean lineNumbersGenerated;
private String targetPath; private String targetPath;
private String externalTool; private String externalTool;
@ -131,6 +132,10 @@ public class IncrementalCBuilder {
this.mainFunctionName = mainFunctionName; this.mainFunctionName = mainFunctionName;
} }
public void setLongjmpSupported(boolean longjmpSupported) {
this.longjmpSupported = longjmpSupported;
}
public void addProgressHandler(ProgressHandler handler) { public void addProgressHandler(ProgressHandler handler) {
synchronized (progressHandlers) { synchronized (progressHandlers) {
progressHandlers.add(handler); progressHandlers.add(handler);
@ -327,6 +332,7 @@ public class IncrementalCBuilder {
cTarget.setIncremental(true); cTarget.setIncremental(true);
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024); cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
cTarget.setLineNumbersGenerated(lineNumbersGenerated); cTarget.setLineNumbersGenerated(lineNumbersGenerated);
cTarget.setLongjmpUsed(longjmpSupported);
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
vm.setCacheStatus(classSource); vm.setCacheStatus(classSource);
vm.addVirtualMethods(m -> true); vm.addVirtualMethods(m -> true);

View File

@ -62,6 +62,10 @@ public class TeaVMCBuilderRunner {
.hasArg() .hasArg()
.withDescription("Minimum heap size in bytes") .withDescription("Minimum heap size in bytes")
.create()); .create());
options.addOption(OptionBuilder
.withLongOpt("no-longjmp")
.withDescription("Don't use setjmp/longjmp functions to emulate exception handling")
.create());
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("entry-point") .withLongOpt("entry-point")
.withArgName("name") .withArgName("name")
@ -117,6 +121,9 @@ public class TeaVMCBuilderRunner {
if (commandLine.hasOption('e')) { if (commandLine.hasOption('e')) {
builder.setMainFunctionName(commandLine.getOptionValue('e')); builder.setMainFunctionName(commandLine.getOptionValue('e'));
} }
if (commandLine.hasOption("no-longjmp")) {
builder.setLongjmpSupported(false);
}
String[] args = commandLine.getArgs(); String[] args = commandLine.getArgs();
if (args.length != 1) { if (args.length != 1) {

View File

@ -141,6 +141,10 @@ public final class TeaVMRunner {
.withDescription("Maximum number of names kept in top-level scope (" .withDescription("Maximum number of names kept in top-level scope ("
+ "other will be put in a separate object. 10000 by default.") + "other will be put in a separate object. 10000 by default.")
.create()); .create());
options.addOption(OptionBuilder
.withLongOpt("no-longjmp")
.withDescription("Don't use setjmp/longjmp functions to emulate exceptions (C target)")
.create());
} }
private TeaVMRunner(CommandLine commandLine) { private TeaVMRunner(CommandLine commandLine) {
@ -177,6 +181,7 @@ public final class TeaVMRunner {
parseIncrementalOptions(); parseIncrementalOptions();
parseJavaScriptOptions(); parseJavaScriptOptions();
parseWasmOptions(); parseWasmOptions();
parseCOptions();
parseHeap(); parseHeap();
if (commandLine.hasOption("e")) { 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() { private void parseHeap() {
if (commandLine.hasOption("min-heap")) { if (commandLine.hasOption("min-heap")) {
int size; int size;

View File

@ -103,7 +103,7 @@ public class TeaVMTool {
private Set<File> generatedFiles = new HashSet<>(); private Set<File> generatedFiles = new HashSet<>();
private int minHeapSize = 32 * (1 << 20); private int minHeapSize = 32 * (1 << 20);
private ReferenceCache referenceCache; private ReferenceCache referenceCache;
private String mainFunctionName; private boolean longjmpSupported = true;
public File getTargetDirectory() { public File getTargetDirectory() {
return targetDirectory; return targetDirectory;
@ -249,8 +249,8 @@ public class TeaVMTool {
this.wasmVersion = wasmVersion; this.wasmVersion = wasmVersion;
} }
public void setMainFunctionName(String mainFunctionName) { public void setLongjmpSupported(boolean longjmpSupported) {
this.mainFunctionName = mainFunctionName; this.longjmpSupported = longjmpSupported;
} }
public void setProgressListener(TeaVMProgressListener progressListener) { public void setProgressListener(TeaVMProgressListener progressListener) {
@ -327,6 +327,7 @@ public class TeaVMTool {
cTarget = new CTarget(); cTarget = new CTarget();
cTarget.setMinHeapSize(minHeapSize); cTarget.setMinHeapSize(minHeapSize);
cTarget.setLineNumbersGenerated(debugInformationGenerated); cTarget.setLineNumbersGenerated(debugInformationGenerated);
cTarget.setLongjmpUsed(longjmpSupported);
return cTarget; return cTarget;
} }

View File

@ -74,5 +74,7 @@ public interface BuildStrategy {
void setHeapSize(int heapSize); void setHeapSize(int heapSize);
void setLongjmpSupported(boolean value);
BuildResult build() throws BuildException; BuildResult build() throws BuildException;
} }

View File

@ -61,6 +61,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
private int heapSize = 32; private int heapSize = 32;
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>(); private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
private boolean longjmpSupported = true;
private TeaVMProgressListener progressListener; private TeaVMProgressListener progressListener;
private Properties properties = new Properties(); private Properties properties = new Properties();
private TeaVMToolLog log = new EmptyTeaVMToolLog(); private TeaVMToolLog log = new EmptyTeaVMToolLog();
@ -196,6 +197,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
this.heapSize = heapSize; this.heapSize = heapSize;
} }
@Override
public void setLongjmpSupported(boolean longjmpSupported) {
this.longjmpSupported = longjmpSupported;
}
@Override @Override
public BuildResult build() throws BuildException { public BuildResult build() throws BuildException {
TeaVMTool tool = new TeaVMTool(); TeaVMTool tool = new TeaVMTool();
@ -222,6 +228,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null); tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null);
tool.setWasmVersion(wasmVersion); tool.setWasmVersion(wasmVersion);
tool.setMinHeapSize(heapSize); tool.setMinHeapSize(heapSize);
tool.setLongjmpSupported(longjmpSupported);
tool.getProperties().putAll(properties); tool.getProperties().putAll(properties);

View File

@ -51,6 +51,7 @@ public class RemoteBuildStrategy implements BuildStrategy {
request = new RemoteBuildRequest(); request = new RemoteBuildRequest();
request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED; request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
request.wasmVersion = WasmBinaryVersion.V_0x1; request.wasmVersion = WasmBinaryVersion.V_0x1;
request.longjmpSupported = true;
} }
@Override @Override
@ -174,6 +175,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
request.heapSize = heapSize; request.heapSize = heapSize;
} }
@Override
public void setLongjmpSupported(boolean value) {
request.longjmpSupported = value;
}
@Override @Override
public BuildResult build() throws BuildException { public BuildResult build() throws BuildException {
RemoteBuildResponse response; RemoteBuildResponse response;

View File

@ -159,6 +159,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
tool.setMaxTopLevelNames(request.maxTopLevelNames); tool.setMaxTopLevelNames(request.maxTopLevelNames);
tool.setWasmVersion(request.wasmVersion); tool.setWasmVersion(request.wasmVersion);
tool.setMinHeapSize(request.heapSize); tool.setMinHeapSize(request.heapSize);
tool.setLongjmpSupported(request.longjmpSupported);
for (String sourceDirectory : request.sourceDirectories) { for (String sourceDirectory : request.sourceDirectories) {
tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory))); tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory)));

View File

@ -46,4 +46,5 @@ public class RemoteBuildRequest implements Serializable {
public boolean fastDependencyAnalysis; public boolean fastDependencyAnalysis;
public WasmBinaryVersion wasmVersion; public WasmBinaryVersion wasmVersion;
public int heapSize; public int heapSize;
public boolean longjmpSupported;
} }

View File

@ -130,7 +130,7 @@
</goals> </goals>
<phase>process-classes</phase> <phase>process-classes</phase>
<configuration> <configuration>
<targetDirectory>${build.directory}/classes/teavm/devserver</targetDirectory> <targetDirectory>${project.build.directory}/classes/teavm/devserver</targetDirectory>
<targetFileName>deobfuscator.js</targetFileName> <targetFileName>deobfuscator.js</targetFileName>
<minifying>true</minifying> <minifying>true</minifying>
<optimizationLevel>ADVANCED</optimizationLevel> <optimizationLevel>ADVANCED</optimizationLevel>

View File

@ -146,6 +146,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.processMemory", defaultValue = "512") @Parameter(property = "teavm.processMemory", defaultValue = "512")
private int processMemory; private int processMemory;
@Parameter(property = "teavm.longjmpSupported", defaultValue = "true")
private boolean longjmpSupported;
private void setupBuilder(BuildStrategy builder) throws MojoExecutionException { private void setupBuilder(BuildStrategy builder) throws MojoExecutionException {
builder.setLog(new MavenTeaVMToolLog(getLog())); builder.setLog(new MavenTeaVMToolLog(getLog()));
try { try {
@ -277,6 +280,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setCacheDirectory(cacheDirectory.getAbsolutePath()); builder.setCacheDirectory(cacheDirectory.getAbsolutePath());
builder.setTargetType(targetType); builder.setTargetType(targetType);
builder.setWasmVersion(wasmVersion); builder.setWasmVersion(wasmVersion);
builder.setLongjmpSupported(longjmpSupported);
BuildResult result; BuildResult result;
result = builder.build(); result = builder.build();
TeaVMProblemRenderer.describeProblems(result.getCallGraph(), result.getProblems(), toolLog); TeaVMProblemRenderer.describeProblems(result.getCallGraph(), result.getProblems(), toolLog);