Implement array bounds checking in JavaScript

This commit is contained in:
Alexey Andreev 2019-10-02 19:42:47 +03:00
parent a1d7153cab
commit bffb50f0cd
18 changed files with 583 additions and 16 deletions

View File

@ -136,8 +136,10 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
data[i] ^= 0xFFFFFFFF;
}
if ((toIndex & 31) != 0) {
data[toDataIndex] ^= trailingOneBits(toIndex);
}
}
if (toIndex == length) {
recalculateLength();
}
@ -177,9 +179,11 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
data[i] = 0xFFFFFFFF;
}
if ((toIndex & 31) != 0) {
data[toDataIndex] |= trailingOneBits(toIndex);
}
}
}
private int trailingZeroBits(int num) {
num %= 32;
@ -226,8 +230,10 @@ public class TBitSet extends TObject implements TCloneable, TSerializable {
for (int i = fromDataIndex + 1; i < toDataIndex; ++i) {
data[i] = 0;
}
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];
}

View File

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

View File

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

View File

@ -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()

View File

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

View File

@ -1564,7 +1564,36 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
@Override
public void visit(BoundCheckExpr expr) {
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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -444,6 +444,7 @@ public class ListingParser {
insn.setLower(true);
}
addInstruction(insn);
break;
}
default:
unexpected();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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