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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,9 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
switch (method.getName()) {
case "findCallSiteById":
case "isJumpSupported":
case "jumpToFrame":
case "abort":
return true;
default:
return false;
@ -45,6 +48,23 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
context.emit(invocation.getArguments().get(1));
context.writer().print(")");
break;
case "isJumpSupported":
context.writer().print("TEAVM_JUMP_SUPPORTED");
break;
case "jumpToFrame":
context.writer().print("TEAVM_JUMP_TO_FRAME(");
context.emit(invocation.getArguments().get(0));
context.writer().print(", ");
context.emit(invocation.getArguments().get(1));
context.writer().print(")");
break;
case "abort":
context.includes().addInclude("<stdlib.h>");
context.writer().print("abort();");
break;
}
}
}

View File

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

View File

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

View File

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

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.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.MethodReference;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.runtime.CallSite;
@ -50,6 +51,9 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
}
switch (methodReference.getName()) {
case "findCallSiteById":
case "isJumpSupported":
case "jumpToFrame":
case "abort":
return true;
}
return false;
@ -64,15 +68,29 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
WasmInt32Constant constant = new WasmInt32Constant(0);
constant.setLocation(invocation.getLocation());
constants.add(constant);
switch (invocation.getMethod().getName()) {
case "findCallSiteById": {
WasmInt32Constant constant = new WasmInt32Constant(0);
constant.setLocation(invocation.getLocation());
constants.add(constant);
int callSiteSize = classGenerator.getClassSize(CallSite.class.getName());
WasmExpression id = manager.generate(invocation.getArguments().get(0));
WasmExpression offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL,
id, new WasmInt32Constant(callSiteSize));
int callSiteSize = classGenerator.getClassSize(CallSite.class.getName());
WasmExpression id = manager.generate(invocation.getArguments().get(0));
WasmExpression offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL,
id, new WasmInt32Constant(callSiteSize));
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, constant, offset);
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, constant, offset);
}
case "isJumpSupported":
return new WasmInt32Constant(0);
case "jumpToFrame":
case "abort":
return new WasmUnreachable();
default:
throw new IllegalArgumentException("Unknown method: " + invocation.getMethod());
}
}
}

View File

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

View File

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

View File

@ -24,22 +24,25 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.util.BasicBlockMapper;
import org.teavm.runtime.ShadowStack;
public class ShadowStackTransformer {
private Characteristics characteristics;
private GCShadowStackContributor gcContributor;
private List<CallSiteDescriptor> callSites = new ArrayList<>();
private boolean exceptionHandling;
public ShadowStackTransformer(Characteristics characteristics) {
public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) {
gcContributor = new GCShadowStackContributor(characteristics);
this.characteristics = characteristics;
this.exceptionHandling = exceptionHandling;
}
public void apply(Program program, MethodReader method) {
@ -48,11 +51,27 @@ public class ShadowStackTransformer {
}
int shadowStackSize = gcContributor.contribute(program, method);
int callSiteStartIndex = callSites.size();
boolean exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
method.getReference(), program).contribute();
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size());
CallSiteDescriptor.save(programCallSites, program.getAnnotations());
boolean exceptions;
if (exceptionHandling) {
List<CallSiteDescriptor> callSites = new ArrayList<>();
exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
method.getReference(), program).contribute();
CallSiteDescriptor.save(callSites, program.getAnnotations());
} else {
exceptions = false;
outer: for (BasicBlock block : program.getBasicBlocks()) {
if (!block.getTryCatchBlocks().isEmpty()) {
exceptions = true;
break;
}
for (Instruction insn : block) {
if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) {
exceptions = true;
break outer;
}
}
}
}
if (shadowStackSize > 0 || exceptions) {
addStackAllocation(program, shadowStackSize);
@ -62,6 +81,10 @@ public class ShadowStackTransformer {
private void addStackAllocation(Program program, int maxDepth) {
BasicBlock block = program.basicBlockAt(0);
if (!block.getTryCatchBlocks().isEmpty()) {
splitFirstBlock(program);
}
List<Instruction> instructionsToAdd = new ArrayList<>();
Variable sizeVariable = program.createVariable();
@ -79,6 +102,26 @@ public class ShadowStackTransformer {
block.addFirstAll(instructionsToAdd);
}
private void splitFirstBlock(Program program) {
BasicBlock block = program.basicBlockAt(0);
BasicBlock split = program.createBasicBlock();
while (block.getFirstInstruction() != null) {
Instruction instruction = block.getFirstInstruction();
instruction.delete();
split.add(instruction);
}
JumpInstruction jump = new JumpInstruction();
jump.setLocation(split.getFirstInstruction().getLocation());
jump.setTarget(split);
block.add(jump);
List<TryCatchBlock> tryCatchBlocks = new ArrayList<>(block.getTryCatchBlocks());
block.getTryCatchBlocks().clear();
split.getTryCatchBlocks().addAll(tryCatchBlocks);
new BasicBlockMapper((BasicBlock b) -> b == block ? split : b).transform(program);
}
private void addStackRelease(Program program, int maxDepth) {
List<BasicBlock> blocks = new ArrayList<>();
boolean hasResult = false;

View File

@ -17,7 +17,9 @@ package org.teavm.model.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.teavm.common.DisjointSet;
@ -151,17 +153,103 @@ public class RegisterAllocator {
}
private void insertPhiArgumentsCopies(Program program) {
for (int i = 0; i < program.basicBlockCount(); ++i) {
List<List<Incoming>> catchIncomingsByVariable = new ArrayList<>(
Collections.nCopies(program.variableCount(), null));
int sz = program.basicBlockCount();
for (int i = 0; i < sz; ++i) {
BasicBlock block = program.basicBlockAt(i);
Map<BasicBlock, BasicBlock> blockMap = new HashMap<>();
for (Phi phi : program.basicBlockAt(i).getPhis()) {
List<Incoming> incomingsToRepeat = new ArrayList<>();
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
insertCopy(incoming, blockMap);
boolean fromTry = incoming.getSource().getTryCatchBlocks().stream()
.anyMatch(tryCatch -> tryCatch.getHandler() == incoming.getPhi().getBasicBlock());
if (fromTry) {
int valueIndex = incoming.getValue().getIndex();
List<Incoming> catchIncomings = catchIncomingsByVariable.get(valueIndex);
if (catchIncomings == null) {
catchIncomings = new ArrayList<>(1);
catchIncomingsByVariable.set(valueIndex, catchIncomings);
}
catchIncomings.add(incoming);
} else {
insertCopy(incoming, blockMap);
incomingsToRepeat.add(incoming);
}
}
}
for (Phi phi : program.basicBlockAt(i).getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
insertCopy(incoming, blockMap);
for (Incoming incoming : incomingsToRepeat) {
insertCopy(incoming, blockMap);
}
}
DefinitionExtractor definitionExtractor = new DefinitionExtractor();
List<Instruction> nextInstructions = new ArrayList<>();
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction instruction : block) {
instruction.acceptVisitor(definitionExtractor);
Variable[] definedVariables = definitionExtractor.getDefinedVariables();
for (Variable definedVariable : definedVariables) {
if (definedVariable.getIndex() >= catchIncomingsByVariable.size()) {
continue;
}
List<Incoming> catchIncomings = catchIncomingsByVariable.get(definedVariable.getIndex());
if (catchIncomings == null) {
continue;
}
Incoming incoming = null;
for (Iterator<Incoming> iter = catchIncomings.iterator(); iter.hasNext();) {
if (iter.next().getValue() == definedVariable) {
iter.remove();
break;
}
}
if (incoming == null) {
continue;
}
Variable copy = program.createVariable();
copy.setLabel(incoming.getPhi().getReceiver().getLabel());
copy.setDebugName(incoming.getPhi().getReceiver().getDebugName());
AssignInstruction copyInstruction = new AssignInstruction();
copyInstruction.setReceiver(copy);
copyInstruction.setAssignee(incoming.getValue());
copyInstruction.setLocation(instruction.getLocation());
incoming.setValue(copy);
nextInstructions.add(copyInstruction);
}
if (!nextInstructions.isEmpty()) {
instruction.insertNextAll(nextInstructions);
nextInstructions.clear();
}
}
}
for (List<Incoming> remainingIncomings : catchIncomingsByVariable) {
if (remainingIncomings == null) {
continue;
}
for (Incoming incoming : remainingIncomings) {
BasicBlock block = incoming.getSource();
Variable copy = program.createVariable();
copy.setLabel(incoming.getPhi().getReceiver().getLabel());
copy.setDebugName(incoming.getPhi().getReceiver().getDebugName());
incoming.setValue(copy);
AssignInstruction copyInstruction = new AssignInstruction();
copyInstruction.setReceiver(copy);
copyInstruction.setAssignee(incoming.getValue());
copyInstruction.setLocation(block.getFirstInstruction().getLocation());
block.addFirst(copyInstruction);
}
}
}

View File

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

View File

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

View File

@ -55,7 +55,7 @@ public final class EventQueue {
}
public static void kill(int id) {
for (int i = 0; i < data.length; ++i) {
for (int i = 0; i < size; ++i) {
if (data[i].id == id) {
remove(i);
break;

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,10 @@
#include <stddef.h>
#include <math.h>
#ifdef TEAVM_USE_SETJMP
#include <setjmp.h>
#endif
#ifdef __GNUC__
#include <stdalign.h>
#include <signal.h>
@ -58,6 +62,18 @@ typedef struct TeaVM_String {
int32_t hashCode;
} TeaVM_String;
typedef struct TeaVM_StackFrame {
struct TeaVM_StackFrame* next;
#ifdef TEAVM_INCREMENTAL
void* callSites;
#endif
#ifdef TEAVM_USE_SETJMP
jmp_buf* jmpTarget;
#endif
int32_t size;
int32_t callSiteId;
} TeaVM_StackFrame;
extern void* teavm_gc_heapAddress;
extern char *teavm_beforeClasses;
@ -87,10 +103,10 @@ static inline int32_t teavm_compare_i64(int64_t a, int64_t b) {
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
}
static inline int32_t teavm_compare_float(float a, float b) {
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : a == b ? INT32_C(0) : INT32_C(1);
}
static inline int32_t teavm_compare_double(double a, double b) {
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : a == b ? INT32_C(0) : INT32_C(1);
}
#define TEAVM_ALIGN(addr, alignment) ((void*) (((uintptr_t) (addr) + ((alignment) - 1)) / (alignment) * (alignment)))
@ -110,36 +126,39 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
#ifdef TEAVM_INCREMENTAL
#define TEAVM_ALLOC_STACK_DEF(size, callSites) \
void* teavm__shadowStack__[(size) + 4]; \
teavm__shadowStack__[0] = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \
teavm__shadowStack__[3] = (void*) (callSites); \
teavm_stackTop = teavm__shadowStack__
#define TEAVM_ALLOC_STACK_DEF(sz, cs) \
struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
teavm_shadowStack.header.next = teavm_stackTop; \
teavm_shadowStack.header.callSites = (cs); \
teavm_shadowStack.header.size = (sz); \
teavm_stackTop = &teavm_shadowStack.header
#define TEAVM_STACK_HEADER_ADD_SIZE 1
#else
#define TEAVM_ALLOC_STACK(size) \
void* teavm__shadowStack__[(size) + 3]; \
teavm__shadowStack__[0] = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \
teavm_stackTop = teavm__shadowStack__
#define TEAVM_ALLOC_STACK(sz) \
struct { TeaVM_StackFrame header; void* data[(sz)]; } teavm_shadowStack; \
teavm_shadowStack.header.next = teavm_stackTop; \
teavm_shadowStack.header.size = (sz); \
teavm_stackTop = &teavm_shadowStack.header
#define TEAVM_STACK_HEADER_ADD_SIZE 0
#endif
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = ptr
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = NULL
#define TEAVM_GC_ROOTS_COUNT(ptr) ((int32_t) (intptr_t) ((void**) (ptr))[2])
#define TEAVM_GET_GC_ROOTS(ptr) (((void**) (ptr)) + 3 + TEAVM_STACK_HEADER_ADD_SIZE)
#define TEAVM_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id))
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1]))
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id))
#define TEAVM_RELEASE_STACK (teavm_stackTop = teavm_shadowStack.header.next)
#define TEAVM_GC_ROOT(index, ptr) teavm_shadowStack.data[index] = ptr
#define TEAVM_GC_ROOT_RELEASE(index) teavm_shadowStack.data[index] = NULL
#define TEAVM_GC_ROOTS_COUNT(ptr) (((TeaVM_StackFrame*) (ptr))->size);
#define TEAVM_GET_GC_ROOTS(ptr) &((struct { TeaVM_StackFrame header; void* data[1]; }*) (ptr))->data;
#define TEAVM_CALL_SITE(id) (teavm_shadowStack.header.callSiteId = (id))
#define TEAVM_WITH_CALL_SITE_ID(id, expr) (TEAVM_CALL_SITE(id), (expr))
#define TEAVM_EXCEPTION_HANDLER (teavm_shadowStack.header.callSiteId)
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((TeaVM_StackFrame*) (frame))->callSiteId = (id))
#define TEAVM_GET_NEXT_FRAME(frame) (((TeaVM_StackFrame*) (frame))->next)
#define TEAVM_GET_CALL_SITE_ID(frame) (((TeaVM_StackFrame*) (frame))->callSiteId)
#define TEAVM_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset)
@ -160,7 +179,7 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
.hashCode = INT32_C(hash) \
}
extern void** teavm_stackTop;
extern TeaVM_StackFrame* teavm_stackTop;
extern void* teavm_gc_gcStorageAddress;
extern int32_t teavm_gc_gcStorageSize;
@ -359,4 +378,49 @@ extern int32_t teavm_file_canonicalize(char16_t*, int32_t, char16_t**);
extern int64_t teavm_unixTimeOffset;
#endif
extern void teavm_logchar(int32_t);
extern void teavm_logchar(int32_t);
#ifdef TEAVM_USE_SETJMP
#define TEAVM_JUMP_SUPPORTED 1
#define TEAVM_TRY \
do { \
jmp_buf teavm_tryBuffer; \
jmp_buf* teavm_oldTryBuffer = teavm_shadowStack.header.jmpTarget; \
teavm_shadowStack.header.jmpTarget = &teavm_tryBuffer; \
int teavm_exceptionHandler = setjmp(teavm_tryBuffer); \
switch (teavm_exceptionHandler) { \
case 0: {
#define TEAVM_CATCH \
break; \
} \
default: { \
longjmp(*teavm_oldTryBuffer, teavm_exceptionHandler); \
break; \
}
#define TEAVM_END_TRY \
} \
teavm_shadowStack.header.jmpTarget = teavm_oldTryBuffer; \
} while (0);
#define TEAVM_JUMP_TO_FRAME(frame, id) \
teavm_stackTop = (TeaVM_StackFrame*) (frame); \
longjmp(*teavm_stackTop->jmpTarget, id)
extern void teavm_throwNullPointerException();
inline static void* teavm_nullCheck(void* o) {
if (o == NULL) {
teavm_throwNullPointerException();
#ifdef __GNUC__
__builtin_unreachable();
#endif
#ifdef _MSC_VER
__assume(0);
#endif
}
return o;
}
#else
#define TEAVM_JUMP_SUPPORTED 0
#define TEAVM_JUMP_TO_FRAME(frame, id)
#endif
extern void* teavm_catchException();

View File

@ -32,6 +32,7 @@ public interface PlatformClassMetadata extends JSObject {
PlatformClass getSuperclass();
@JSProperty
@Unmanaged
String getName();
@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)
gcc -g -O0 -lrt -lm all.c -o run_test

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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