mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Implement array bounds checking in JavaScript
This commit is contained in:
parent
a1d7153cab
commit
bffb50f0cd
|
@ -136,7 +136,9 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
|
|||
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
|
||||
data[i] ^= 0xFFFFFFFF;
|
||||
}
|
||||
data[toDataIndex] ^= trailingOneBits(toIndex);
|
||||
if ((toIndex & 31) != 0) {
|
||||
data[toDataIndex] ^= trailingOneBits(toIndex);
|
||||
}
|
||||
}
|
||||
if (toIndex == length) {
|
||||
recalculateLength();
|
||||
|
@ -177,7 +179,9 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
|
|||
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
|
||||
data[i] = 0xFFFFFFFF;
|
||||
}
|
||||
data[toDataIndex] |= trailingOneBits(toIndex);
|
||||
if ((toIndex & 31) != 0) {
|
||||
data[toDataIndex] |= trailingOneBits(toIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +230,9 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
|
|||
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
|
||||
data[i] = 0;
|
||||
}
|
||||
data[toDataIndex] &= trailingZeroBits(toIndex);
|
||||
if ((toIndex & 31) != 0) {
|
||||
data[toDataIndex] &= trailingZeroBits(toIndex);
|
||||
}
|
||||
}
|
||||
recalculateLength();
|
||||
}
|
||||
|
@ -422,7 +428,7 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
|
|||
public void or(TBitSet set) {
|
||||
length = TMath.max(length, set.length);
|
||||
ensureCapacity((length + 31) / 32);
|
||||
int sz = TMath.min(data.length, set.length);
|
||||
int sz = TMath.min(data.length, set.data.length);
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
data[i] |= set.data[i];
|
||||
}
|
||||
|
@ -431,7 +437,7 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
|
|||
public void xor(TBitSet set) {
|
||||
length = TMath.max(length, set.length);
|
||||
ensureCapacity((length + 31) / 32);
|
||||
int sz = TMath.min(data.length, set.length);
|
||||
int sz = TMath.min(data.length, set.data.length);
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
data[i] ^= set.data[i];
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ import org.teavm.model.instructions.InvocationType;
|
|||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.RaiseInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.lowlevel.BoundCheckInsertion;
|
||||
import org.teavm.model.util.AsyncMethodFinder;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.vm.BuildTarget;
|
||||
|
@ -126,6 +127,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
|
||||
private int topLevelNameLimit = 10000;
|
||||
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
|
||||
private boolean strict;
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
|
@ -207,6 +209,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
this.topLevelNameLimit = topLevelNameLimit;
|
||||
}
|
||||
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresRegisterAllocation() {
|
||||
return true;
|
||||
|
@ -274,6 +280,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons.use();
|
||||
|
||||
if (strict) {
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
ArrayIndexOutOfBoundsException.class, "<init>", void.class));
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(
|
||||
ArrayIndexOutOfBoundsException.class.getName()));
|
||||
exceptionCons.use();
|
||||
}
|
||||
|
||||
if (stackTraceIncluded) {
|
||||
includeStackTraceMethods(dependencyAnalyzer);
|
||||
}
|
||||
|
@ -324,6 +338,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
|
||||
@Override
|
||||
public void beforeOptimizations(Program program, MethodReader method) {
|
||||
if (strict) {
|
||||
new BoundCheckInsertion().transformProgram(program, method.getReference());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.teavm.ast.AssignmentStatement;
|
|||
import org.teavm.ast.AsyncMethodNode;
|
||||
import org.teavm.ast.AsyncMethodPart;
|
||||
import org.teavm.ast.BinaryExpr;
|
||||
import org.teavm.ast.BoundCheckExpr;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.InitClassStatement;
|
||||
import org.teavm.ast.InstanceOfExpr;
|
||||
|
@ -303,4 +304,16 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
|||
consumer.consumeFunction("$rt_isInstance");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BoundCheckExpr expr) {
|
||||
super.visit(expr);
|
||||
if (expr.getArray() != null && expr.getIndex() != null) {
|
||||
consumer.consumeFunction("$rt_checkBounds");
|
||||
} else if (expr.getArray() != null) {
|
||||
consumer.consumeFunction("$rt_checkUpperBound");
|
||||
} else if (expr.isLower()) {
|
||||
consumer.consumeFunction("$rt_checkLowerBound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,7 +250,8 @@ public class Renderer implements RenderingManager {
|
|||
private void renderRuntimeAliases() throws IOException {
|
||||
String[] names = { "$rt_throw", "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray",
|
||||
"$rt_isInstance", "$rt_nativeThread", "$rt_suspending", "$rt_resuming", "$rt_invalidPointer",
|
||||
"$rt_s", "$rt_eraseClinit", "$rt_imul", "$rt_wrapException" };
|
||||
"$rt_s", "$rt_eraseClinit", "$rt_imul", "$rt_wrapException", "$rt_checkBounds",
|
||||
"$rt_checkUpperBound", "$rt_checkLowerBound" };
|
||||
boolean first = true;
|
||||
for (String name : names) {
|
||||
if (!first) {
|
||||
|
@ -413,8 +414,7 @@ public class Renderer implements RenderingManager {
|
|||
|
||||
ScopedName name = naming.getNameForClassInit(cls.getName());
|
||||
renderFunctionDeclaration(name);
|
||||
writer.append("()").ws()
|
||||
.append("{").softNewLine().indent();
|
||||
writer.append("()").ws().append("{").softNewLine().indent();
|
||||
|
||||
if (isAsync) {
|
||||
writer.append("var ").append(context.pointerName()).ws().append("=").ws()
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.mozilla.javascript.ast.AstRoot;
|
|||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
@ -46,6 +47,8 @@ public class RuntimeRenderer {
|
|||
"<init>", String.class, String.class, String.class, int.class, void.class);
|
||||
private static final MethodReference SET_STACK_TRACE_METHOD = new MethodReference(Throwable.class,
|
||||
"setStackTrace", StackTraceElement[].class, void.class);
|
||||
private static final MethodReference AIOOBE_INIT_METHOD = new MethodReference(ArrayIndexOutOfBoundsException.class,
|
||||
"<init>", void.class);
|
||||
|
||||
private final ClassReaderSource classSource;
|
||||
private final SourceWriter writer;
|
||||
|
@ -69,6 +72,7 @@ public class RuntimeRenderer {
|
|||
renderRuntimeCreateException();
|
||||
renderCreateStackTraceElement();
|
||||
renderSetStackTrace();
|
||||
renderThrowAIOOBE();
|
||||
} catch (IOException e) {
|
||||
throw new RenderingException("IO error", e);
|
||||
}
|
||||
|
@ -249,4 +253,18 @@ public class RuntimeRenderer {
|
|||
}
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderThrowAIOOBE() throws IOException {
|
||||
writer.append("function $rt_throwAIOOBE()").ws().append("{").indent().softNewLine();
|
||||
|
||||
ClassReader cls = classSource.get(AIOOBE_INIT_METHOD.getClassName());
|
||||
if (cls != null) {
|
||||
MethodReader method = cls.getMethod(AIOOBE_INIT_METHOD.getDescriptor());
|
||||
if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
writer.append("$rt_throw(").appendInit(AIOOBE_INIT_METHOD).append("());").softNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1564,7 +1564,36 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(BoundCheckExpr expr) {
|
||||
expr.getIndex().acceptVisitor(this);
|
||||
try {
|
||||
if (expr.getLocation() != null) {
|
||||
pushLocation(expr.getLocation());
|
||||
}
|
||||
|
||||
if (expr.getArray() != null && expr.isLower()) {
|
||||
writer.appendFunction("$rt_checkBounds").append("(");
|
||||
} else if (expr.getArray() != null) {
|
||||
writer.appendFunction("$rt_checkUpperBound").append("(");
|
||||
} else if (expr.isLower()) {
|
||||
writer.appendFunction("$rt_checkLowerBound").append("(");
|
||||
}
|
||||
|
||||
expr.getIndex().acceptVisitor(this);
|
||||
|
||||
if (expr.getArray() != null) {
|
||||
writer.append(",").ws();
|
||||
expr.getArray().acceptVisitor(this);
|
||||
}
|
||||
|
||||
if (expr.getArray() != null || expr.isLower()) {
|
||||
writer.append(")");
|
||||
}
|
||||
|
||||
if (expr.getLocation() != null) {
|
||||
popLocation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RenderingException("IO error occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class InjectorContextImpl implements InjectorContext {
|
||||
|
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
* Copyright 2019 konsoletyper.
|
||||
*
|
||||
* 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 com.carrotsearch.hppc.IntArrayList;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||
import java.util.Arrays;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BinaryInstruction;
|
||||
import org.teavm.model.instructions.BinaryOperation;
|
||||
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||
import org.teavm.model.instructions.GetElementInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
import org.teavm.model.instructions.NumericOperandType;
|
||||
import org.teavm.model.instructions.PutElementInstruction;
|
||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||
import org.teavm.model.util.DominatorWalker;
|
||||
import org.teavm.model.util.DominatorWalkerCallback;
|
||||
import org.teavm.model.util.PhiUpdater;
|
||||
|
||||
public class BoundCheckInsertion {
|
||||
public void transformProgram(Program program, MethodReference methodReference) {
|
||||
if (program.basicBlockCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionVisitor visitor = new InsertionVisitor(program.variableCount());
|
||||
new DominatorWalker(program).walk(visitor);
|
||||
if (visitor.changed) {
|
||||
new PhiUpdater().updatePhis(program, methodReference.parameterCount() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static class InsertionVisitor extends AbstractInstructionVisitor
|
||||
implements DominatorWalkerCallback<BlockBounds> {
|
||||
BlockBounds bounds;
|
||||
boolean changed;
|
||||
private boolean[] isConstant;
|
||||
private boolean[] isConstantSizedArray;
|
||||
private int[] constantValue;
|
||||
private IntSet[] upperArrayLengths;
|
||||
private boolean[] nonNegative;
|
||||
private int[] map;
|
||||
private int[] arrayLengthVars;
|
||||
private int[] arrayLengthReverseVars;
|
||||
private int[] comparisonLeft;
|
||||
private int[] comparisonRight;
|
||||
private int conditionBlock;
|
||||
private int comparisonValue;
|
||||
private int comparisonVariable;
|
||||
private ComparisonMode comparisonMode;
|
||||
|
||||
InsertionVisitor(int variableCount) {
|
||||
isConstant = new boolean[variableCount];
|
||||
isConstantSizedArray = new boolean[variableCount];
|
||||
constantValue = new int[variableCount];
|
||||
upperArrayLengths = new IntSet[variableCount];
|
||||
nonNegative = new boolean[variableCount];
|
||||
map = new int[variableCount];
|
||||
for (int i = 0; i < variableCount; ++i) {
|
||||
map[i] = i;
|
||||
}
|
||||
arrayLengthVars = new int[variableCount];
|
||||
Arrays.fill(arrayLengthVars, -1);
|
||||
arrayLengthReverseVars = new int[variableCount];
|
||||
comparisonLeft = new int[variableCount];
|
||||
Arrays.fill(comparisonLeft, -1);
|
||||
comparisonRight = new int[variableCount];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockBounds visit(BasicBlock block) {
|
||||
bounds = new BlockBounds();
|
||||
|
||||
if (comparisonMode != null && conditionBlock == block.getIndex()) {
|
||||
switch (comparisonMode) {
|
||||
case LESS_THAN_ARRAY_LENGTH:
|
||||
addArrayBound(comparisonVariable, comparisonValue);
|
||||
break;
|
||||
case NON_NEGATIVE:
|
||||
markAsNonNegative(comparisonVariable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Instruction instruction : block) {
|
||||
instruction.acceptVisitor(this);
|
||||
}
|
||||
|
||||
bounds.comparisonMode = comparisonMode;
|
||||
bounds.comparisonValue = comparisonValue;
|
||||
bounds.comparisonVariable = comparisonVariable;
|
||||
bounds.conditionBlock = conditionBlock;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(BasicBlock block, BlockBounds state) {
|
||||
IntArrayList addedArrayBounds = state.addedArrayBounds;
|
||||
int size = addedArrayBounds.size();
|
||||
for (int i = 0; i < size; i += 2) {
|
||||
int index = addedArrayBounds.get(i);
|
||||
int array = addedArrayBounds.get(i + 1);
|
||||
upperArrayLengths[index].removeAll(array);
|
||||
}
|
||||
|
||||
for (IntCursor cursor : state.nonNegatives) {
|
||||
nonNegative[cursor.value] = false;
|
||||
}
|
||||
|
||||
comparisonMode = state.comparisonMode;
|
||||
comparisonValue = state.comparisonValue;
|
||||
comparisonVariable = state.comparisonVariable;
|
||||
conditionBlock = state.conditionBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(JumpInstruction insn) {
|
||||
prepareJump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
prepareJump();
|
||||
int operand = index(insn.getOperand());
|
||||
int left = comparisonLeft[operand];
|
||||
int right = comparisonRight[operand];
|
||||
|
||||
if (left >= 0) {
|
||||
switch (insn.getCondition()) {
|
||||
case LESS:
|
||||
if (arrayLengthVars[right] >= 0) {
|
||||
comparisonMode = ComparisonMode.LESS_THAN_ARRAY_LENGTH;
|
||||
comparisonValue = arrayLengthVars[right];
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
} else if (isConstant[left] && constantValue[left] >= -1) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
} else if (isConstant[right] && constantValue[right] >= 0) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
}
|
||||
break;
|
||||
|
||||
case GREATER_OR_EQUAL:
|
||||
if (arrayLengthVars[right] >= 0) {
|
||||
comparisonMode = ComparisonMode.LESS_THAN_ARRAY_LENGTH;
|
||||
comparisonValue = arrayLengthVars[right];
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
} else if (isConstant[left] && constantValue[left] >= -1) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
} else if (isConstant[right] && constantValue[right] >= 0) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
}
|
||||
break;
|
||||
|
||||
case GREATER:
|
||||
if (arrayLengthVars[left] >= 0) {
|
||||
comparisonMode = ComparisonMode.LESS_THAN_ARRAY_LENGTH;
|
||||
comparisonValue = arrayLengthVars[left];
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
} else if (isConstant[left] && constantValue[left] >= 0) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
} else if (isConstant[right] && constantValue[right] >= -1) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
}
|
||||
break;
|
||||
|
||||
case LESS_OR_EQUAL:
|
||||
if (arrayLengthVars[left] >= 0) {
|
||||
comparisonMode = ComparisonMode.LESS_THAN_ARRAY_LENGTH;
|
||||
comparisonValue = arrayLengthVars[left];
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
} else if (isConstant[left] && constantValue[left] >= 0) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = right;
|
||||
conditionBlock = insn.getConsequent().getIndex();
|
||||
} else if (isConstant[right] && constantValue[right] >= -1) {
|
||||
comparisonMode = ComparisonMode.NON_NEGATIVE;
|
||||
comparisonVariable = left;
|
||||
conditionBlock = insn.getAlternative().getIndex();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryBranchingInstruction insn) {
|
||||
prepareJump();
|
||||
}
|
||||
|
||||
private void prepareJump() {
|
||||
conditionBlock = -1;
|
||||
comparisonMode = null;
|
||||
comparisonValue = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IntegerConstantInstruction insn) {
|
||||
int receiver = index(insn.getReceiver());
|
||||
isConstant[receiver] = true;
|
||||
constantValue[receiver] = insn.getConstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryInstruction insn) {
|
||||
int first = index(insn.getFirstOperand());
|
||||
int second = index(insn.getSecondOperand());
|
||||
int receiver = index(insn.getReceiver());
|
||||
if (isConstant[first] && isConstant[second]) {
|
||||
int a = constantValue[first];
|
||||
int b = constantValue[second];
|
||||
int r;
|
||||
switch (insn.getOperation()) {
|
||||
case ADD:
|
||||
r = a + b;
|
||||
break;
|
||||
case SUBTRACT:
|
||||
r = a - b;
|
||||
break;
|
||||
case COMPARE:
|
||||
r = Integer.compare(a, b);
|
||||
break;
|
||||
case DIVIDE:
|
||||
r = a / b;
|
||||
break;
|
||||
case MODULO:
|
||||
r = a % b;
|
||||
break;
|
||||
case MULTIPLY:
|
||||
r = a * b;
|
||||
break;
|
||||
case AND:
|
||||
r = a & b;
|
||||
break;
|
||||
case OR:
|
||||
r = a | b;
|
||||
break;
|
||||
case XOR:
|
||||
r = a ^ b;
|
||||
break;
|
||||
case SHIFT_LEFT:
|
||||
r = a << b;
|
||||
break;
|
||||
case SHIFT_RIGHT:
|
||||
r = a >> b;
|
||||
break;
|
||||
case SHIFT_RIGHT_UNSIGNED:
|
||||
r = a >>> b;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
isConstant[receiver] = true;
|
||||
constantValue[receiver] = r;
|
||||
} else if (insn.getOperation() == BinaryOperation.COMPARE
|
||||
&& insn.getOperandType() == NumericOperandType.INT) {
|
||||
comparisonLeft[receiver] = first;
|
||||
comparisonRight[receiver] = second;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConstructArrayInstruction insn) {
|
||||
int size = index(insn.getSize());
|
||||
int receiver = index(insn.getReceiver());
|
||||
if (isConstant[size]) {
|
||||
isConstantSizedArray[receiver] = true;
|
||||
constantValue[receiver] = constantValue[size];
|
||||
}
|
||||
arrayLengthVars[size] = receiver;
|
||||
arrayLengthReverseVars[receiver] = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ArrayLengthInstruction insn) {
|
||||
int array = index(insn.getArray());
|
||||
int receiver = index(insn.getReceiver());
|
||||
if (arrayLengthVars[receiver] >= 0) {
|
||||
map[receiver] = arrayLengthReverseVars[receiver];
|
||||
} else {
|
||||
if (isConstantSizedArray[array]) {
|
||||
isConstant[receiver] = true;
|
||||
constantValue[receiver] = constantValue[array];
|
||||
}
|
||||
arrayLengthVars[receiver] = array;
|
||||
arrayLengthReverseVars[array] = receiver;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignInstruction insn) {
|
||||
assign(insn.getAssignee(), insn.getReceiver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnwrapArrayInstruction insn) {
|
||||
assign(insn.getArray(), insn.getReceiver());
|
||||
}
|
||||
|
||||
private void assign(Variable from, Variable to) {
|
||||
map[to.getIndex()] = map[from.getIndex()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(GetElementInstruction insn) {
|
||||
tryInsertBoundCheck(insn.getIndex(), insn.getArray(), insn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PutElementInstruction insn) {
|
||||
tryInsertBoundCheck(insn.getIndex(), insn.getArray(), insn);
|
||||
}
|
||||
|
||||
private void tryInsertBoundCheck(Variable indexVar, Variable arrayVar, Instruction instruction) {
|
||||
boolean lower = true;
|
||||
boolean upper = true;
|
||||
int index = index(indexVar);
|
||||
int array = index(arrayVar);
|
||||
if (isConstant[index]) {
|
||||
if (isConstantSizedArray[array]) {
|
||||
upper = false;
|
||||
}
|
||||
}
|
||||
if (upper) {
|
||||
IntSet bounds = upperArrayLengths[index];
|
||||
if (bounds != null && bounds.contains(array)) {
|
||||
upper = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lower) {
|
||||
if ((isConstant[index] && constantValue[index] >= 0) || nonNegative[index]) {
|
||||
lower = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (upper) {
|
||||
addArrayBound(index, array);
|
||||
}
|
||||
markAsNonNegative(index);
|
||||
|
||||
if (lower || upper) {
|
||||
BoundCheckInstruction boundCheck = new BoundCheckInstruction();
|
||||
if (lower) {
|
||||
boundCheck.setLower(true);
|
||||
}
|
||||
if (upper) {
|
||||
boundCheck.setArray(arrayVar);
|
||||
}
|
||||
boundCheck.setIndex(indexVar);
|
||||
boundCheck.setReceiver(indexVar);
|
||||
boundCheck.setLocation(instruction.getLocation());
|
||||
instruction.insertPrevious(boundCheck);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void addArrayBound(int index, int array) {
|
||||
IntSet upperSet = upperArrayLengths[index];
|
||||
if (upperSet == null) {
|
||||
upperSet = new IntHashSet();
|
||||
upperArrayLengths[index] = upperSet;
|
||||
}
|
||||
if (upperSet.add(array)) {
|
||||
bounds.addedArrayBounds.add(index, array);
|
||||
}
|
||||
}
|
||||
|
||||
private void markAsNonNegative(int index) {
|
||||
if (!nonNegative[index]) {
|
||||
nonNegative[index] = true;
|
||||
bounds.nonNegatives.add(index);
|
||||
}
|
||||
}
|
||||
|
||||
private int index(Variable var) {
|
||||
return map[var.getIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
static class BlockBounds {
|
||||
IntArrayList addedArrayBounds = new IntArrayList();
|
||||
IntArrayList nonNegatives = new IntArrayList();
|
||||
|
||||
int conditionBlock;
|
||||
int comparisonValue;
|
||||
int comparisonVariable;
|
||||
ComparisonMode comparisonMode;
|
||||
}
|
||||
|
||||
enum ComparisonMode {
|
||||
LESS_THAN_ARRAY_LENGTH,
|
||||
NON_NEGATIVE
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.teavm.model.instructions.AssignInstruction;
|
|||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BinaryInstruction;
|
||||
import org.teavm.model.instructions.BinaryOperation;
|
||||
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||
|
@ -781,5 +782,15 @@ public class GlobalValueNumbering implements MethodOptimization {
|
|||
int val = replaceMap[insn.getObjectRef().getIndex()];
|
||||
insn.setObjectRef(program.variableAt(val));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BoundCheckInstruction insn) {
|
||||
int index = replaceMap[insn.getIndex().getIndex()];
|
||||
insn.setIndex(program.variableAt(index));
|
||||
if (insn.getArray() != null) {
|
||||
int array = replaceMap[insn.getArray().getIndex()];
|
||||
insn.setArray(program.variableAt(array));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -119,11 +119,11 @@ public class UnusedVariableElimination implements MethodOptimization {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static class InstructionOptimizer extends AbstractInstructionVisitor {
|
||||
static class InstructionOptimizer extends AbstractInstructionVisitor {
|
||||
private boolean[] used;
|
||||
boolean eliminate;
|
||||
|
||||
public InstructionOptimizer(boolean[] used) {
|
||||
InstructionOptimizer(boolean[] used) {
|
||||
this.used = used;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,5 +113,12 @@ public final class VariableEscapeAnalyzer {
|
|||
public void visit(MonitorExitInstruction insn) {
|
||||
escaping[insn.getObjectRef().getIndex()] = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BoundCheckInstruction insn) {
|
||||
if (insn.getArray() != null) {
|
||||
escaping[insn.getArray().getIndex()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ class InstructionStringifier implements InstructionReader {
|
|||
Set<String> occupiedLabels = new HashSet<>();
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
VariableReader var = program.variableAt(i);
|
||||
if (var == null) {
|
||||
continue;
|
||||
}
|
||||
String suggestedName = var.getLabel() != null ? var.getLabel() : Integer.toString(i);
|
||||
if (!occupiedLabels.add(suggestedName)) {
|
||||
int suffix = 1;
|
||||
|
|
|
@ -444,6 +444,7 @@ public class ListingParser {
|
|||
insn.setLower(true);
|
||||
}
|
||||
addInstruction(insn);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unexpected();
|
||||
|
|
|
@ -58,17 +58,14 @@ import org.teavm.model.instructions.ConstructArrayInstruction;
|
|||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||
import org.teavm.model.instructions.EmptyInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||
import org.teavm.model.instructions.GetElementInstruction;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InstructionVisitor;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
import org.teavm.model.instructions.LongConstantInstruction;
|
||||
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||
|
|
|
@ -630,3 +630,21 @@ var $rt_udiv = function(a, b) {
|
|||
var $rt_umod = function(a, b) {
|
||||
return ((a >>> 0) % (b >>> 0)) >>> 0;
|
||||
};
|
||||
function $rt_checkBounds(index, array) {
|
||||
if (index < 0 || index >= array.length) {
|
||||
$rt_throwAIOOBE();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function $rt_checkUpperBound(index, array) {
|
||||
if (index >= array.length) {
|
||||
$rt_throwAIOOBE();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function $rt_checkLowerBound(index) {
|
||||
if (index < 0) {
|
||||
$rt_throwAIOOBE();
|
||||
}
|
||||
return index;
|
||||
}
|
|
@ -74,7 +74,9 @@ public class ClassValueTest {
|
|||
}
|
||||
|
||||
private DependencyInfo runTest(String methodName) {
|
||||
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build();
|
||||
JavaScriptTarget target = new JavaScriptTarget();
|
||||
target.setStrict(true);
|
||||
TeaVM vm = new TeaVMBuilder(target).build();
|
||||
vm.add(new DependencyTestPatcher(getClass().getName(), methodName));
|
||||
vm.installPlugins();
|
||||
vm.entryPoint(getClass().getName());
|
||||
|
|
|
@ -207,6 +207,7 @@ public class IncrementalTest {
|
|||
vm.setProgramCache(programCache);
|
||||
target.setAstCache(astCache);
|
||||
target.setMinifying(false);
|
||||
target.setStrict(true);
|
||||
vm.add(new EntryPointTransformer(entryPoint));
|
||||
vm.entryPoint(EntryPoint.class.getName());
|
||||
vm.installPlugins();
|
||||
|
|
|
@ -770,7 +770,8 @@ public class CodeServlet extends HttpServlet {
|
|||
jsTarget.setMinifying(false);
|
||||
jsTarget.setAstCache(astCache);
|
||||
jsTarget.setDebugEmitter(debugInformationBuilder);
|
||||
jsTarget.setTopLevelNameLimit(500);
|
||||
jsTarget.setTopLevelNameLimit(2000);
|
||||
jsTarget.setStrict(true);
|
||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||
vm.setCacheStatus(classSource);
|
||||
vm.addVirtualMethods(m -> true);
|
||||
|
|
|
@ -565,6 +565,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
DebugInformationBuilder debugEmitter = new DebugInformationBuilder(new ReferenceCache());
|
||||
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
||||
JavaScriptTarget target = new JavaScriptTarget();
|
||||
target.setStrict(true);
|
||||
if (decodeStack) {
|
||||
target.setDebugEmitter(debugEmitter);
|
||||
target.setStackTraceIncluded(true);
|
||||
|
|
Loading…
Reference in New Issue
Block a user