mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
C: support array bound checking
This commit is contained in:
parent
6f50eefaf9
commit
94a96f571b
|
@ -106,12 +106,13 @@ import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
import org.teavm.model.lowlevel.CheckInstructionTransformation;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
import org.teavm.model.lowlevel.ExportDependencyListener;
|
import org.teavm.model.lowlevel.ExportDependencyListener;
|
||||||
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
|
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
|
||||||
import org.teavm.model.lowlevel.NullCheckTransformation;
|
|
||||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
|
import org.teavm.model.transformation.BoundCheckInsertion;
|
||||||
import org.teavm.model.transformation.ClassPatch;
|
import org.teavm.model.transformation.ClassPatch;
|
||||||
import org.teavm.model.transformation.NullCheckInsertion;
|
import org.teavm.model.transformation.NullCheckInsertion;
|
||||||
import org.teavm.model.util.AsyncMethodFinder;
|
import org.teavm.model.util.AsyncMethodFinder;
|
||||||
|
@ -150,7 +151,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
private ClassInitializerTransformer classInitializerTransformer;
|
private ClassInitializerTransformer classInitializerTransformer;
|
||||||
private ShadowStackTransformer shadowStackTransformer;
|
private ShadowStackTransformer shadowStackTransformer;
|
||||||
private NullCheckInsertion nullCheckInsertion;
|
private NullCheckInsertion nullCheckInsertion;
|
||||||
private NullCheckTransformation nullCheckTransformation;
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
|
private CheckInstructionTransformation checkTransformation;
|
||||||
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
||||||
private int minHeapSize = 4 * 1024 * 1024;
|
private int minHeapSize = 4 * 1024 * 1024;
|
||||||
private int maxHeapSize = 128 * 1024 * 1024;
|
private int maxHeapSize = 128 * 1024 * 1024;
|
||||||
|
@ -222,7 +224,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
|
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
|
||||||
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
|
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
|
||||||
nullCheckTransformation = new NullCheckTransformation();
|
checkTransformation = new CheckInstructionTransformation();
|
||||||
|
|
||||||
controller.addVirtualMethods(VIRTUAL_METHODS::contains);
|
controller.addVirtualMethods(VIRTUAL_METHODS::contains);
|
||||||
}
|
}
|
||||||
|
@ -267,6 +269,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
void.class)).use();
|
void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||||
void.class)).use();
|
void.class)).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class,
|
||||||
|
"throwArrayIndexOutOfBoundsException", void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class))
|
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class))
|
||||||
.propagate(0, NullPointerException.class.getName())
|
.propagate(0, NullPointerException.class.getName())
|
||||||
.use();
|
.use();
|
||||||
|
@ -324,6 +328,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
@Override
|
@Override
|
||||||
public void beforeOptimizations(Program program, MethodReader method) {
|
public void beforeOptimizations(Program program, MethodReader method) {
|
||||||
nullCheckInsertion.transformProgram(program, method.getReference());
|
nullCheckInsertion.transformProgram(program, method.getReference());
|
||||||
|
boundCheckInsertion.transformProgram(program, method.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -331,7 +336,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
classInitializerEliminator.apply(program);
|
classInitializerEliminator.apply(program);
|
||||||
classInitializerTransformer.transform(program);
|
classInitializerTransformer.transform(program);
|
||||||
if (!longjmpUsed) {
|
if (!longjmpUsed) {
|
||||||
nullCheckTransformation.apply(program, method.getResultType());
|
checkTransformation.apply(program, method.getResultType());
|
||||||
}
|
}
|
||||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
|
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
|
||||||
.apply(program, method.getReference());
|
.apply(program, method.getReference());
|
||||||
|
@ -394,8 +399,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
if (incremental) {
|
if (incremental) {
|
||||||
configHeaderWriter.println("#define TEAVM_INCREMENTAL 1");
|
configHeaderWriter.println("#define TEAVM_INCREMENTAL 1");
|
||||||
}
|
}
|
||||||
if (longjmpUsed) {
|
if (!longjmpUsed) {
|
||||||
configHeaderWriter.println("#define TEAVM_USE_SETJMP 1");
|
configHeaderWriter.println("#define TEAVM_USE_SETJMP 0");
|
||||||
}
|
}
|
||||||
if (vmAssertions) {
|
if (vmAssertions) {
|
||||||
configHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");
|
configHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");
|
||||||
|
|
|
@ -1423,7 +1423,37 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BoundCheckExpr expr) {
|
public void visit(BoundCheckExpr expr) {
|
||||||
|
if (expr.getArray() == null && !expr.isLower()) {
|
||||||
|
expr.getIndex().acceptVisitor(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean needParenthesis = false;
|
||||||
|
if (needsCallSiteId()) {
|
||||||
|
needParenthesis = true;
|
||||||
|
withCallSite();
|
||||||
|
}
|
||||||
|
|
||||||
|
String functionName;
|
||||||
|
if (expr.getArray() == null) {
|
||||||
|
functionName = "teavm_checkLowerBound";
|
||||||
|
} else if (!expr.isLower()) {
|
||||||
|
functionName = "teavm_checkUpperBound";
|
||||||
|
} else {
|
||||||
|
functionName = "teavm_checkBounds";
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.print(functionName);
|
||||||
|
writer.print("(");
|
||||||
expr.getIndex().acceptVisitor(this);
|
expr.getIndex().acceptVisitor(this);
|
||||||
|
if (expr.getArray() != null) {
|
||||||
|
writer.print(", ");
|
||||||
|
visitReference(expr.getArray());
|
||||||
|
}
|
||||||
|
writer.print(")");
|
||||||
|
if (needParenthesis) {
|
||||||
|
writer.print(")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model.lowlevel;
|
||||||
|
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryOperation;
|
||||||
|
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
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.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
import org.teavm.model.util.BasicBlockSplitter;
|
||||||
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
|
||||||
|
public class CheckInstructionTransformation {
|
||||||
|
private BasicBlock returnBlock;
|
||||||
|
private BasicBlock next;
|
||||||
|
|
||||||
|
public void apply(Program program, ValueType returnType) {
|
||||||
|
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
||||||
|
|
||||||
|
returnBlock = null;
|
||||||
|
int count = program.basicBlockCount();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
next = program.basicBlockAt(i);
|
||||||
|
BasicBlock block;
|
||||||
|
while (next != null) {
|
||||||
|
block = next;
|
||||||
|
next = null;
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (instruction instanceof NullCheckInstruction) {
|
||||||
|
replaceNullCheck(splitter, program, (NullCheckInstruction) instruction);
|
||||||
|
} else if (instruction instanceof BoundCheckInstruction) {
|
||||||
|
replaceBoundCheck(splitter, program, (BoundCheckInstruction) instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnBlock != null) {
|
||||||
|
ExitInstruction fakeExit = new ExitInstruction();
|
||||||
|
if (returnType != ValueType.VOID) {
|
||||||
|
Variable fakeReturnVar = program.createVariable();
|
||||||
|
createFakeReturnValue(returnBlock, fakeReturnVar, returnType);
|
||||||
|
fakeExit.setValueToReturn(fakeReturnVar);
|
||||||
|
}
|
||||||
|
returnBlock.add(fakeExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
splitter.fixProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceNullCheck(BasicBlockSplitter splitter, Program program, NullCheckInstruction nullCheck) {
|
||||||
|
BasicBlock block = nullCheck.getBasicBlock();
|
||||||
|
BasicBlock continueBlock = splitter.split(block, nullCheck);
|
||||||
|
BasicBlock throwBlock = program.createBasicBlock();
|
||||||
|
|
||||||
|
InvokeInstruction throwNPE = new InvokeInstruction();
|
||||||
|
throwNPE.setType(InvocationType.SPECIAL);
|
||||||
|
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||||
|
void.class));
|
||||||
|
throwNPE.setLocation(nullCheck.getLocation());
|
||||||
|
throwBlock.add(throwNPE);
|
||||||
|
|
||||||
|
jumpToReturn(program, nullCheck, throwBlock);
|
||||||
|
|
||||||
|
BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL);
|
||||||
|
jumpIfNull.setOperand(nullCheck.getValue());
|
||||||
|
jumpIfNull.setConsequent(throwBlock);
|
||||||
|
jumpIfNull.setAlternative(continueBlock);
|
||||||
|
jumpIfNull.setLocation(nullCheck.getLocation());
|
||||||
|
nullCheck.replace(jumpIfNull);
|
||||||
|
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setAssignee(nullCheck.getValue());
|
||||||
|
assign.setReceiver(nullCheck.getReceiver());
|
||||||
|
assign.setLocation(nullCheck.getLocation());
|
||||||
|
continueBlock.addFirst(assign);
|
||||||
|
|
||||||
|
next = continueBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceBoundCheck(BasicBlockSplitter splitter, Program program, BoundCheckInstruction boundCheck) {
|
||||||
|
BasicBlock block = boundCheck.getBasicBlock();
|
||||||
|
BasicBlock continueBlock = splitter.split(block, boundCheck);
|
||||||
|
BasicBlock throwBlock = program.createBasicBlock();
|
||||||
|
|
||||||
|
BasicBlock ifPositiveBlock = continueBlock;
|
||||||
|
if (boundCheck.isLower() && boundCheck.getArray() != null) {
|
||||||
|
ifPositiveBlock = program.createBasicBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeInstruction throwAIOOBE = new InvokeInstruction();
|
||||||
|
throwAIOOBE.setType(InvocationType.SPECIAL);
|
||||||
|
throwAIOOBE.setMethod(new MethodReference(ExceptionHandling.class, "throwArrayIndexOutOfBoundsException",
|
||||||
|
void.class));
|
||||||
|
throwAIOOBE.setLocation(boundCheck.getLocation());
|
||||||
|
throwBlock.add(throwAIOOBE);
|
||||||
|
|
||||||
|
jumpToReturn(program, boundCheck, throwBlock);
|
||||||
|
|
||||||
|
if (boundCheck.isLower()) {
|
||||||
|
BranchingInstruction jumpIfLess = new BranchingInstruction(BranchingCondition.LESS);
|
||||||
|
jumpIfLess.setOperand(boundCheck.getIndex());
|
||||||
|
jumpIfLess.setConsequent(throwBlock);
|
||||||
|
jumpIfLess.setAlternative(ifPositiveBlock);
|
||||||
|
jumpIfLess.setLocation(boundCheck.getLocation());
|
||||||
|
boundCheck.replace(jumpIfLess);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundCheck.getArray() != null) {
|
||||||
|
ArrayLengthInstruction arrayLength = new ArrayLengthInstruction();
|
||||||
|
arrayLength.setArray(boundCheck.getArray());
|
||||||
|
arrayLength.setReceiver(program.createVariable());
|
||||||
|
arrayLength.setLocation(boundCheck.getLocation());
|
||||||
|
|
||||||
|
BinaryInstruction compare = new BinaryInstruction(BinaryOperation.COMPARE, NumericOperandType.INT);
|
||||||
|
compare.setFirstOperand(boundCheck.getIndex());
|
||||||
|
compare.setSecondOperand(arrayLength.getReceiver());
|
||||||
|
compare.setReceiver(program.createVariable());
|
||||||
|
compare.setLocation(boundCheck.getLocation());
|
||||||
|
|
||||||
|
BranchingInstruction jumpIfGreater = new BranchingInstruction(BranchingCondition.GREATER_OR_EQUAL);
|
||||||
|
jumpIfGreater.setOperand(compare.getReceiver());
|
||||||
|
jumpIfGreater.setConsequent(throwBlock);
|
||||||
|
jumpIfGreater.setAlternative(continueBlock);
|
||||||
|
jumpIfGreater.setLocation(boundCheck.getLocation());
|
||||||
|
|
||||||
|
if (boundCheck.isLower()) {
|
||||||
|
ifPositiveBlock.add(jumpIfGreater);
|
||||||
|
} else {
|
||||||
|
boundCheck.replace(jumpIfGreater);
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpIfGreater.insertPrevious(arrayLength);
|
||||||
|
jumpIfGreater.insertPrevious(compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setAssignee(boundCheck.getIndex());
|
||||||
|
assign.setReceiver(boundCheck.getReceiver());
|
||||||
|
assign.setLocation(boundCheck.getLocation());
|
||||||
|
continueBlock.addFirst(assign);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void jumpToReturn(Program program, Instruction instruction, BasicBlock throwBlock) {
|
||||||
|
if (returnBlock == null) {
|
||||||
|
returnBlock = program.createBasicBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
JumpInstruction jumpToFakeReturn = new JumpInstruction();
|
||||||
|
jumpToFakeReturn.setTarget(returnBlock);
|
||||||
|
jumpToFakeReturn.setLocation(instruction.getLocation());
|
||||||
|
throwBlock.add(jumpToFakeReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case INTEGER:
|
||||||
|
case CHARACTER:
|
||||||
|
IntegerConstantInstruction intZero = new IntegerConstantInstruction();
|
||||||
|
intZero.setReceiver(variable);
|
||||||
|
block.add(intZero);
|
||||||
|
return;
|
||||||
|
case LONG:
|
||||||
|
LongConstantInstruction longZero = new LongConstantInstruction();
|
||||||
|
longZero.setReceiver(variable);
|
||||||
|
block.add(longZero);
|
||||||
|
return;
|
||||||
|
case FLOAT:
|
||||||
|
FloatConstantInstruction floatZero = new FloatConstantInstruction();
|
||||||
|
floatZero.setReceiver(variable);
|
||||||
|
block.add(floatZero);
|
||||||
|
return;
|
||||||
|
case DOUBLE:
|
||||||
|
DoubleConstantInstruction doubleZero = new DoubleConstantInstruction();
|
||||||
|
doubleZero.setReceiver(variable);
|
||||||
|
block.add(doubleZero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NullConstantInstruction nullConstant = new NullConstantInstruction();
|
||||||
|
nullConstant.setReceiver(variable);
|
||||||
|
block.add(nullConstant);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
@ -283,7 +284,7 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||||
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction
|
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction
|
||||||
|| insn instanceof NullCheckInstruction) {
|
|| insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction) {
|
||||||
return true;
|
return true;
|
||||||
} else if (insn instanceof InvokeInstruction) {
|
} else if (insn instanceof InvokeInstruction) {
|
||||||
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
|
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018 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.model.lowlevel;
|
|
||||||
|
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.Instruction;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
import org.teavm.model.Program;
|
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
import org.teavm.model.Variable;
|
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
|
||||||
import org.teavm.model.instructions.BranchingCondition;
|
|
||||||
import org.teavm.model.instructions.BranchingInstruction;
|
|
||||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
|
||||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
|
||||||
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.instructions.LongConstantInstruction;
|
|
||||||
import org.teavm.model.instructions.NullCheckInstruction;
|
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
|
||||||
import org.teavm.model.util.BasicBlockSplitter;
|
|
||||||
import org.teavm.runtime.ExceptionHandling;
|
|
||||||
|
|
||||||
public class NullCheckTransformation {
|
|
||||||
public void apply(Program program, ValueType returnType) {
|
|
||||||
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
|
||||||
|
|
||||||
BasicBlock returnBlock = null;
|
|
||||||
int count = program.basicBlockCount();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
BasicBlock next = program.basicBlockAt(i);
|
|
||||||
BasicBlock block;
|
|
||||||
while (next != null) {
|
|
||||||
block = next;
|
|
||||||
next = null;
|
|
||||||
for (Instruction instruction : block) {
|
|
||||||
if (!(instruction instanceof NullCheckInstruction)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
|
|
||||||
BasicBlock continueBlock = splitter.split(block, nullCheck);
|
|
||||||
BasicBlock throwBlock = program.createBasicBlock();
|
|
||||||
|
|
||||||
InvokeInstruction throwNPE = new InvokeInstruction();
|
|
||||||
throwNPE.setType(InvocationType.SPECIAL);
|
|
||||||
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
|
||||||
void.class));
|
|
||||||
throwNPE.setLocation(nullCheck.getLocation());
|
|
||||||
throwBlock.add(throwNPE);
|
|
||||||
|
|
||||||
if (returnBlock == null) {
|
|
||||||
returnBlock = program.createBasicBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
JumpInstruction jumpToFakeReturn = new JumpInstruction();
|
|
||||||
jumpToFakeReturn.setTarget(returnBlock);
|
|
||||||
jumpToFakeReturn.setLocation(nullCheck.getLocation());
|
|
||||||
throwBlock.add(jumpToFakeReturn);
|
|
||||||
|
|
||||||
BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL);
|
|
||||||
jumpIfNull.setOperand(nullCheck.getValue());
|
|
||||||
jumpIfNull.setConsequent(throwBlock);
|
|
||||||
jumpIfNull.setAlternative(continueBlock);
|
|
||||||
jumpIfNull.setLocation(nullCheck.getLocation());
|
|
||||||
nullCheck.replace(jumpIfNull);
|
|
||||||
|
|
||||||
AssignInstruction assign = new AssignInstruction();
|
|
||||||
assign.setAssignee(nullCheck.getValue());
|
|
||||||
assign.setReceiver(nullCheck.getReceiver());
|
|
||||||
assign.setLocation(nullCheck.getLocation());
|
|
||||||
continueBlock.addFirst(assign);
|
|
||||||
|
|
||||||
next = continueBlock;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnBlock != null) {
|
|
||||||
ExitInstruction fakeExit = new ExitInstruction();
|
|
||||||
if (returnType != ValueType.VOID) {
|
|
||||||
Variable fakeReturnVar = program.createVariable();
|
|
||||||
createFakeReturnValue(returnBlock, fakeReturnVar, returnType);
|
|
||||||
fakeExit.setValueToReturn(fakeReturnVar);
|
|
||||||
}
|
|
||||||
returnBlock.add(fakeExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
splitter.fixProgram();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {
|
|
||||||
if (type instanceof ValueType.Primitive) {
|
|
||||||
switch (((ValueType.Primitive) type).getKind()) {
|
|
||||||
case BOOLEAN:
|
|
||||||
case BYTE:
|
|
||||||
case SHORT:
|
|
||||||
case INTEGER:
|
|
||||||
case CHARACTER:
|
|
||||||
IntegerConstantInstruction intZero = new IntegerConstantInstruction();
|
|
||||||
intZero.setReceiver(variable);
|
|
||||||
block.add(intZero);
|
|
||||||
return;
|
|
||||||
case LONG:
|
|
||||||
LongConstantInstruction longZero = new LongConstantInstruction();
|
|
||||||
longZero.setReceiver(variable);
|
|
||||||
block.add(longZero);
|
|
||||||
return;
|
|
||||||
case FLOAT:
|
|
||||||
FloatConstantInstruction floatZero = new FloatConstantInstruction();
|
|
||||||
floatZero.setReceiver(variable);
|
|
||||||
block.add(floatZero);
|
|
||||||
return;
|
|
||||||
case DOUBLE:
|
|
||||||
DoubleConstantInstruction doubleZero = new DoubleConstantInstruction();
|
|
||||||
doubleZero.setReceiver(variable);
|
|
||||||
block.add(doubleZero);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NullConstantInstruction nullConstant = new NullConstantInstruction();
|
|
||||||
nullConstant.setReceiver(variable);
|
|
||||||
block.add(nullConstant);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -126,6 +126,12 @@ public final class ExceptionHandling {
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
@Export(name = "teavm_throwArrayIndexOutOfBoundsException")
|
||||||
|
public static void throwArrayIndexOutOfBoundsException() {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
public static int callStackSize() {
|
public static int callStackSize() {
|
||||||
Address stackFrame = ShadowStack.getStackTop();
|
Address stackFrame = ShadowStack.getStackTop();
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "definitions.h"
|
#include "definitions.h"
|
||||||
#include "heapdump.h"
|
#include "heapdump.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "exceptions.h"
|
|
||||||
|
|
||||||
#if TEAVM_MEMORY_TRACE
|
#if TEAVM_MEMORY_TRACE
|
||||||
#include "heaptrace.h"
|
#include "heaptrace.h"
|
||||||
|
@ -53,6 +52,10 @@ typedef struct TeaVM_String {
|
||||||
|
|
||||||
extern char* teavm_beforeClasses;
|
extern char* teavm_beforeClasses;
|
||||||
|
|
||||||
|
extern void* teavm_throwClassCastException();
|
||||||
|
extern void teavm_throwNullPointerException();
|
||||||
|
extern void teavm_throwArrayIndexOutOfBoundsException();
|
||||||
|
|
||||||
#define TEAVM_PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - teavm_beforeClasses) >> 3))
|
#define TEAVM_PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - teavm_beforeClasses) >> 3))
|
||||||
#define TEAVM_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3)))
|
#define TEAVM_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3)))
|
||||||
#define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header))
|
#define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "definitions.h"
|
#include "definitions.h"
|
||||||
|
#include "core.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
@ -7,9 +8,6 @@
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern void* teavm_throwClassCastException();
|
|
||||||
extern void teavm_throwNullPointerException();
|
|
||||||
|
|
||||||
#if TEAVM_USE_SETJMP
|
#if TEAVM_USE_SETJMP
|
||||||
#define TEAVM_JUMP_SUPPORTED 1
|
#define TEAVM_JUMP_SUPPORTED 1
|
||||||
#define TEAVM_TRY \
|
#define TEAVM_TRY \
|
||||||
|
@ -35,18 +33,7 @@ extern void teavm_throwNullPointerException();
|
||||||
#define TEAVM_JUMP_TO_FRAME(frame, id) \
|
#define TEAVM_JUMP_TO_FRAME(frame, id) \
|
||||||
teavm_stackTop = (TeaVM_StackFrame*) (frame); \
|
teavm_stackTop = (TeaVM_StackFrame*) (frame); \
|
||||||
longjmp(*teavm_stackTop->jmpTarget, id)
|
longjmp(*teavm_stackTop->jmpTarget, id)
|
||||||
inline static void* teavm_nullCheck(void* o) {
|
|
||||||
if (o == NULL) {
|
|
||||||
teavm_throwNullPointerException();
|
|
||||||
#if TEAVM_UNIX
|
|
||||||
__builtin_unreachable();
|
|
||||||
#endif
|
|
||||||
#if TEAVM_WINDOWS
|
|
||||||
__assume(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if TEAVM_UNIX
|
#if TEAVM_UNIX
|
||||||
#define TEAVM_UNREACHABLE __builtin_unreachable();
|
#define TEAVM_UNREACHABLE __builtin_unreachable();
|
||||||
|
@ -58,6 +45,38 @@ extern void teavm_throwNullPointerException();
|
||||||
#define TEAVM_UNREACHABLE return;
|
#define TEAVM_UNREACHABLE return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
inline static void* teavm_nullCheck(void* o) {
|
||||||
|
if (o == NULL) {
|
||||||
|
teavm_throwNullPointerException();
|
||||||
|
TEAVM_UNREACHABLE
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int32_t teavm_checkBounds(int32_t index, void* array) {
|
||||||
|
if (index < 0 || index >= TEAVM_ARRAY_LENGTH(array)) {
|
||||||
|
teavm_throwArrayIndexOutOfBoundsException();
|
||||||
|
TEAVM_UNREACHABLE
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int32_t teavm_checkLowerBound(int32_t index) {
|
||||||
|
if (index < 0) {
|
||||||
|
teavm_throwArrayIndexOutOfBoundsException();
|
||||||
|
TEAVM_UNREACHABLE
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int32_t teavm_checkUpperBound(int32_t index, void* array) {
|
||||||
|
if (index >= TEAVM_ARRAY_LENGTH(array)) {
|
||||||
|
teavm_throwArrayIndexOutOfBoundsException();
|
||||||
|
TEAVM_UNREACHABLE
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define TEAVM_JUMP_SUPPORTED 0
|
#define TEAVM_JUMP_SUPPORTED 0
|
||||||
#define TEAVM_JUMP_TO_FRAME(frame, id)
|
#define TEAVM_JUMP_TO_FRAME(frame, id)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "definitions.h"
|
#include "definitions.h"
|
||||||
|
|
||||||
#if TEAVM_MEMORY_TRACE
|
#if TEAVM_MEMORY_TRACE
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "definitions.h"
|
#include "definitions.h"
|
||||||
|
|
||||||
|
#if TEAVM_USE_SETJMP
|
||||||
|
#include <setjmp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TeaVM_String* value;
|
TeaVM_String* value;
|
||||||
} TeaVM_StringPtr;
|
} TeaVM_StringPtr;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user