mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add local type inference to use in optimizations
This commit is contained in:
parent
5d1e558401
commit
645b2b7cd5
|
@ -90,6 +90,7 @@
|
||||||
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PackageAccessibility" enabled="false" level="ERROR" enabled_by_default="false" />
|
<inspection_tool class="PackageAccessibility" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="RawUseOfParameterizedType" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="RedundantThrows" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
<inspection_tool class="RedundantThrows" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="SameParameterValue" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="SameParameterValue" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<scope name="classlib-emu" level="WEAK WARNING" enabled="true" />
|
<scope name="classlib-emu" level="WEAK WARNING" enabled="true" />
|
||||||
|
|
|
@ -21,10 +21,6 @@ import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public interface DependencyInfo {
|
public interface DependencyInfo {
|
||||||
ClassReaderSource getClassSource();
|
ClassReaderSource getClassSource();
|
||||||
|
|
||||||
|
|
|
@ -191,11 +191,11 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
if (DependencyChecker.shouldLog) {
|
if (DependencyChecker.shouldLog) {
|
||||||
arrayItemNode.tag = tag + "[";
|
arrayItemNode.tag = tag + "[";
|
||||||
}
|
}
|
||||||
arrayItemNode.addConsumer(this::propagate);
|
|
||||||
}
|
}
|
||||||
return arrayItemNode;
|
return arrayItemNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public DependencyNode getClassValueNode() {
|
public DependencyNode getClassValueNode() {
|
||||||
if (classValueNode == null) {
|
if (classValueNode == null) {
|
||||||
classValueNode = new DependencyNode(dependencyChecker, degree);
|
classValueNode = new DependencyNode(dependencyChecker, degree);
|
||||||
|
@ -203,7 +203,6 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
if (DependencyChecker.shouldLog) {
|
if (DependencyChecker.shouldLog) {
|
||||||
classValueNode.tag = tag + "@";
|
classValueNode.tag = tag + "@";
|
||||||
}
|
}
|
||||||
classValueNode.addConsumer(this::propagate);
|
|
||||||
}
|
}
|
||||||
return classValueNode;
|
return classValueNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,6 @@ import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public class MethodDependency implements MethodDependencyInfo {
|
public class MethodDependency implements MethodDependencyInfo {
|
||||||
private DependencyChecker dependencyChecker;
|
private DependencyChecker dependencyChecker;
|
||||||
DependencyNode[] variableNodes;
|
DependencyNode[] variableNodes;
|
||||||
|
|
|
@ -17,10 +17,6 @@ package org.teavm.dependency;
|
||||||
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public interface MethodDependencyInfo {
|
public interface MethodDependencyInfo {
|
||||||
ValueDependencyInfo[] getVariables();
|
ValueDependencyInfo[] getVariables();
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public interface ValueDependencyInfo {
|
public interface ValueDependencyInfo {
|
||||||
String[] getTypes();
|
String[] getTypes();
|
||||||
|
|
||||||
|
|
465
core/src/main/java/org/teavm/model/analysis/ClassInference.java
Normal file
465
core/src/main/java/org/teavm/model/analysis/ClassInference.java
Normal file
|
@ -0,0 +1,465 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.analysis;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntObjectMap;
|
||||||
|
import com.carrotsearch.hppc.IntObjectOpenHashMap;
|
||||||
|
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphBuilder;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.dependency.FieldDependencyInfo;
|
||||||
|
import org.teavm.dependency.MethodDependencyInfo;
|
||||||
|
import org.teavm.dependency.ValueDependencyInfo;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
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.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
|
public class ClassInference {
|
||||||
|
private DependencyInfo dependencyInfo;
|
||||||
|
private Graph assignmentGraph;
|
||||||
|
private Graph cloneGraph;
|
||||||
|
private Graph arrayGraph;
|
||||||
|
private Graph itemGraph;
|
||||||
|
private List<IntObjectMap<ValueType>> casts;
|
||||||
|
private IntObjectMap<IntSet> exceptionMap;
|
||||||
|
private VirtualCallSite[][] virtualCallSites;
|
||||||
|
private List<Task> initialTasks;
|
||||||
|
private List<List<Set<String>>> types;
|
||||||
|
private List<Set<String>> finalTypes;
|
||||||
|
|
||||||
|
public ClassInference(DependencyInfo dependencyInfo) {
|
||||||
|
this.dependencyInfo = dependencyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void infer(Program program, MethodReference methodReference) {
|
||||||
|
buildGraphs(program);
|
||||||
|
|
||||||
|
MethodDependencyInfo thisMethodDep = dependencyInfo.getMethod(methodReference);
|
||||||
|
for (String thisType : thisMethodDep.getVariable(0).getTypes()) {
|
||||||
|
initialTasks.add(new Task(0, 0, thisType));
|
||||||
|
}
|
||||||
|
|
||||||
|
types = new ArrayList<>(program.variableCount());
|
||||||
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
|
List<Set<String>> variableTypes = new ArrayList<>();
|
||||||
|
types.add(variableTypes);
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
variableTypes.add(new LinkedHashSet<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propagate(program);
|
||||||
|
assignmentGraph = null;
|
||||||
|
cloneGraph = null;
|
||||||
|
arrayGraph = null;
|
||||||
|
itemGraph = null;
|
||||||
|
casts = null;
|
||||||
|
exceptionMap = null;
|
||||||
|
virtualCallSites = null;
|
||||||
|
|
||||||
|
finalTypes = new ArrayList<>(program.variableCount());
|
||||||
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
|
finalTypes.add(types.get(i).get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
types = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] classesOf(int variableIndex) {
|
||||||
|
return finalTypes.get(variableIndex).toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildGraphs(Program program) {
|
||||||
|
GraphBuildingVisitor visitor = new GraphBuildingVisitor(program.variableCount(), dependencyInfo);
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
visitor.currentBlock = block;
|
||||||
|
for (Phi phi : block.getPhis()) {
|
||||||
|
visitor.visit(phi);
|
||||||
|
}
|
||||||
|
for (Instruction insn : block) {
|
||||||
|
insn.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assignmentGraph = visitor.assignmentGraphBuilder.build();
|
||||||
|
cloneGraph = visitor.cloneGraphBuilder.build();
|
||||||
|
arrayGraph = visitor.arrayGraphBuilder.build();
|
||||||
|
itemGraph = visitor.itemGraphBuilder.build();
|
||||||
|
casts = visitor.casts;
|
||||||
|
exceptionMap = visitor.exceptionMap;
|
||||||
|
initialTasks = visitor.tasks;
|
||||||
|
|
||||||
|
virtualCallSites = new VirtualCallSite[program.variableCount()][];
|
||||||
|
for (int i = 0; i < virtualCallSites.length; ++i) {
|
||||||
|
List<VirtualCallSite> buildCallSites = visitor.virtualCallSites.get(i);
|
||||||
|
if (buildCallSites != null) {
|
||||||
|
virtualCallSites[i] = buildCallSites.toArray(new VirtualCallSite[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagate(Program program) {
|
||||||
|
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
||||||
|
|
||||||
|
Queue<Task> queue = new ArrayDeque<>();
|
||||||
|
queue.addAll(initialTasks);
|
||||||
|
initialTasks = null;
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
Task task = queue.remove();
|
||||||
|
|
||||||
|
if (task.degree < 0) {
|
||||||
|
BasicBlock block = program.basicBlockAt(task.variable);
|
||||||
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
|
if (tryCatch.getExceptionType() == null
|
||||||
|
|| classSource.isSuperType(tryCatch.getExceptionType(), task.className).orElse(false)) {
|
||||||
|
Variable exception = tryCatch.getHandler().getExceptionVariable();
|
||||||
|
if (exception != null) {
|
||||||
|
queue.add(new Task(exception.getIndex(), 0, task.className));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Set<String>> variableTypes = types.get(task.variable);
|
||||||
|
if (task.degree >= variableTypes.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> typeSet = variableTypes.get(task.degree);
|
||||||
|
if (!typeSet.add(task.className)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int successor : assignmentGraph.outgoingEdges(task.variable)) {
|
||||||
|
queue.add(new Task(successor, task.degree, task.className));
|
||||||
|
int itemDegree = task.degree + 1;
|
||||||
|
if (itemDegree < variableTypes.size()) {
|
||||||
|
for (String type : variableTypes.get(itemDegree)) {
|
||||||
|
queue.add(new Task(successor, itemDegree, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Set<String>> successorVariableTypes = types.get(successor);
|
||||||
|
if (itemDegree < successorVariableTypes.size()) {
|
||||||
|
for (String type : successorVariableTypes.get(itemDegree)) {
|
||||||
|
queue.add(new Task(task.variable, itemDegree, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.degree > 0) {
|
||||||
|
for (int predecessor : assignmentGraph.incomingEdges(task.variable)) {
|
||||||
|
queue.add(new Task(predecessor, task.degree, task.className));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int successor : itemGraph.outgoingEdges(task.variable)) {
|
||||||
|
queue.add(new Task(successor, task.degree - 1, task.className));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int successor : cloneGraph.outgoingEdges(task.variable)) {
|
||||||
|
queue.add(new Task(successor, 0, task.className));
|
||||||
|
}
|
||||||
|
|
||||||
|
IntSet blocks = exceptionMap.get(task.variable);
|
||||||
|
if (blocks != null) {
|
||||||
|
for (int block : blocks.toArray()) {
|
||||||
|
queue.add(new Task(block, -1, task.className));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualCallSite[] callSites = virtualCallSites[task.variable];
|
||||||
|
if (callSites != null) {
|
||||||
|
for (VirtualCallSite callSite : callSites) {
|
||||||
|
MethodReference rawMethod = new MethodReference(task.className,
|
||||||
|
callSite.method.getDescriptor());
|
||||||
|
MethodReader resolvedMethod = classSource.resolve(rawMethod);
|
||||||
|
if (resolvedMethod == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MethodReference resolvedMethodRef = resolvedMethod.getReference();
|
||||||
|
if (callSite.resolvedMethods.add(resolvedMethodRef)) {
|
||||||
|
MethodDependencyInfo methodDep = dependencyInfo.getMethod(resolvedMethodRef);
|
||||||
|
if (callSite.receiver >= 0) {
|
||||||
|
readValue(methodDep.getResult(), program.variableAt(callSite.receiver), queue);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < callSite.arguments.length; ++i) {
|
||||||
|
writeValue(methodDep.getVariable(i + 1), program.variableAt(callSite.arguments[i]),
|
||||||
|
queue);
|
||||||
|
}
|
||||||
|
for (String type : methodDep.getThrown().getTypes()) {
|
||||||
|
queue.add(new Task(callSite.block, -1, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int successor : arrayGraph.outgoingEdges(task.variable)) {
|
||||||
|
queue.add(new Task(successor, task.degree + 1, task.className));
|
||||||
|
}
|
||||||
|
|
||||||
|
IntObjectMap<ValueType> variableCasts = casts.get(task.variable);
|
||||||
|
if (variableCasts != null) {
|
||||||
|
ValueType type = task.className.startsWith("[")
|
||||||
|
? ValueType.parse(task.className)
|
||||||
|
: ValueType.object(task.className);
|
||||||
|
for (int target : variableCasts.keys().toArray()) {
|
||||||
|
ValueType targetType = variableCasts.get(target);
|
||||||
|
if (classSource.isSuperType(targetType, type).orElse(false)) {
|
||||||
|
queue.add(new Task(target, 0, task.className));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class GraphBuildingVisitor extends AbstractInstructionVisitor {
|
||||||
|
DependencyInfo dependencyInfo;
|
||||||
|
GraphBuilder assignmentGraphBuilder;
|
||||||
|
GraphBuilder cloneGraphBuilder;
|
||||||
|
GraphBuilder arrayGraphBuilder;
|
||||||
|
GraphBuilder itemGraphBuilder;
|
||||||
|
List<IntObjectMap<ValueType>> casts = new ArrayList<>();
|
||||||
|
IntObjectMap<IntSet> exceptionMap = new IntObjectOpenHashMap<>();
|
||||||
|
List<Task> tasks = new ArrayList<>();
|
||||||
|
List<List<VirtualCallSite>> virtualCallSites = new ArrayList<>();
|
||||||
|
BasicBlock currentBlock;
|
||||||
|
|
||||||
|
GraphBuildingVisitor(int variableCount, DependencyInfo dependencyInfo) {
|
||||||
|
this.dependencyInfo = dependencyInfo;
|
||||||
|
assignmentGraphBuilder = new GraphBuilder(variableCount);
|
||||||
|
cloneGraphBuilder = new GraphBuilder(variableCount);
|
||||||
|
arrayGraphBuilder = new GraphBuilder(variableCount);
|
||||||
|
itemGraphBuilder = new GraphBuilder(variableCount);
|
||||||
|
casts = new ArrayList<>(variableCount);
|
||||||
|
for (int i = 0; i < variableCount; ++i) {
|
||||||
|
casts.add(new IntObjectOpenHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtualCallSites = new ArrayList<>(Collections.nCopies(variableCount, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visit(Phi phi) {
|
||||||
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
|
assignmentGraphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
tasks.add(new Task(insn.getReceiver().getIndex(), 0, "java.lang.Class"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(StringConstantInstruction insn) {
|
||||||
|
tasks.add(new Task(insn.getReceiver().getIndex(), 0, "java.lang.String"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(AssignInstruction insn) {
|
||||||
|
assignmentGraphBuilder.addEdge(insn.getAssignee().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastInstruction insn) {
|
||||||
|
casts.get(insn.getValue().getIndex()).put(insn.getReceiver().getIndex(), insn.getTargetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(RaiseInstruction insn) {
|
||||||
|
IntSet blockIndexes = exceptionMap.get(insn.getException().getIndex());
|
||||||
|
if (blockIndexes == null) {
|
||||||
|
blockIndexes = new IntOpenHashSet();
|
||||||
|
exceptionMap.put(insn.getException().getIndex(), blockIndexes);
|
||||||
|
}
|
||||||
|
blockIndexes.add(currentBlock.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
|
tasks.add(new Task(insn.getReceiver().getIndex(), 0, ValueType.arrayOf(insn.getItemType()).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructInstruction insn) {
|
||||||
|
tasks.add(new Task(insn.getReceiver().getIndex(), 0, insn.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
|
ValueType type = insn.getItemType();
|
||||||
|
for (int i = 0; i < insn.getDimensions().size(); ++i) {
|
||||||
|
type = ValueType.arrayOf(type);
|
||||||
|
}
|
||||||
|
tasks.add(new Task(insn.getReceiver().getIndex(), 0, type.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
FieldDependencyInfo fieldDep = dependencyInfo.getField(insn.getField());
|
||||||
|
ValueDependencyInfo valueDep = fieldDep.getValue();
|
||||||
|
readValue(valueDep, insn.getReceiver(), tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
FieldDependencyInfo fieldDep = dependencyInfo.getField(insn.getField());
|
||||||
|
ValueDependencyInfo valueDep = fieldDep.getValue();
|
||||||
|
writeValue(valueDep, insn.getValue(), tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CloneArrayInstruction insn) {
|
||||||
|
cloneGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnwrapArrayInstruction insn) {
|
||||||
|
assignmentGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetElementInstruction insn) {
|
||||||
|
itemGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutElementInstruction insn) {
|
||||||
|
arrayGraphBuilder.addEdge(insn.getValue().getIndex(), insn.getArray().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (insn.getType() == InvocationType.VIRTUAL) {
|
||||||
|
int instance = insn.getInstance().getIndex();
|
||||||
|
List<VirtualCallSite> callSites = virtualCallSites.get(instance);
|
||||||
|
if (callSites == null) {
|
||||||
|
callSites = new ArrayList<>();
|
||||||
|
virtualCallSites.set(instance, callSites);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualCallSite callSite = new VirtualCallSite();
|
||||||
|
callSite.method = insn.getMethod();
|
||||||
|
callSite.arguments = new int[insn.getArguments().size()];
|
||||||
|
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||||
|
callSite.arguments[i] = insn.getArguments().get(i).getIndex();
|
||||||
|
}
|
||||||
|
callSite.receiver = insn.getReceiver() != null ? insn.getReceiver().getIndex() : -1;
|
||||||
|
callSite.block = currentBlock.getIndex();
|
||||||
|
callSites.add(callSite);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDependencyInfo methodDep = dependencyInfo.getMethod(insn.getMethod());
|
||||||
|
|
||||||
|
if (insn.getReceiver() != null) {
|
||||||
|
readValue(methodDep.getResult(), insn.getReceiver(), tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||||
|
writeValue(methodDep.getVariable(i + 1), insn.getArguments().get(i), tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String type : methodDep.getThrown().getTypes()) {
|
||||||
|
tasks.add(new Task(currentBlock.getIndex(), -1, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readValue(ValueDependencyInfo valueDep, Variable receiver, Collection<Task> tasks) {
|
||||||
|
int depth = 0;
|
||||||
|
boolean hasArrayType;
|
||||||
|
do {
|
||||||
|
for (String type : valueDep.getTypes()) {
|
||||||
|
tasks.add(new Task(receiver.getIndex(), depth, type));
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
hasArrayType = valueDep.hasArrayType();
|
||||||
|
valueDep = valueDep.getArrayItem();
|
||||||
|
} while (hasArrayType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeValue(ValueDependencyInfo valueDep, Variable source, Collection<Task> tasks) {
|
||||||
|
int depth = 0;
|
||||||
|
while (valueDep.hasArrayType()) {
|
||||||
|
depth++;
|
||||||
|
valueDep = valueDep.getArrayItem();
|
||||||
|
for (String type : valueDep.getTypes()) {
|
||||||
|
tasks.add(new Task(source.getIndex(), depth, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Task {
|
||||||
|
int variable;
|
||||||
|
int degree;
|
||||||
|
String className;
|
||||||
|
|
||||||
|
Task(int variable, int degree, String className) {
|
||||||
|
this.variable = variable;
|
||||||
|
this.degree = degree;
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class VirtualCallSite {
|
||||||
|
Set<MethodReference> resolvedMethods = new HashSet<>();
|
||||||
|
MethodReference method;
|
||||||
|
int[] arguments;
|
||||||
|
int receiver;
|
||||||
|
int block;
|
||||||
|
}
|
||||||
|
}
|
291
tests/src/test/java/org/teavm/dependency/DependencyTest.java
Normal file
291
tests/src/test/java/org/teavm/dependency/DependencyTest.java
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.dependency;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TestName;
|
||||||
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
|
import org.teavm.common.DisjointSet;
|
||||||
|
import org.teavm.diagnostics.Problem;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.ClassInference;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
import org.teavm.vm.TeaVMBuilder;
|
||||||
|
import org.teavm.vm.TeaVMPhase;
|
||||||
|
import org.teavm.vm.TeaVMProgressFeedback;
|
||||||
|
import org.teavm.vm.TeaVMProgressListener;
|
||||||
|
|
||||||
|
public class DependencyTest {
|
||||||
|
@Rule
|
||||||
|
public final TestName testName = new TestName();
|
||||||
|
|
||||||
|
private static ClassHolderSource classSource;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void prepare() {
|
||||||
|
classSource = new ClasspathClassHolderSource(DependencyTest.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanup() {
|
||||||
|
classSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void virtualCall() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void instanceOf() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void catchException() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void propagateException() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void arrays() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void arraysPassed() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void arraysRetrieved() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest() {
|
||||||
|
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget())
|
||||||
|
.setClassLoader(DependencyTest.class.getClassLoader())
|
||||||
|
.setClassSource(classSource)
|
||||||
|
.build();
|
||||||
|
vm.setProgressListener(new TeaVMProgressListener() {
|
||||||
|
@Override
|
||||||
|
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
|
||||||
|
return phase == TeaVMPhase.DEPENDENCY_CHECKING
|
||||||
|
? TeaVMProgressFeedback.CONTINUE
|
||||||
|
: TeaVMProgressFeedback.CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TeaVMProgressFeedback progressReached(int progress) {
|
||||||
|
return TeaVMProgressFeedback.CONTINUE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vm.installPlugins();
|
||||||
|
|
||||||
|
MethodReference testMethod = new MethodReference(DependencyTestData.class,
|
||||||
|
testName.getMethodName(), void.class);
|
||||||
|
vm.entryPoint(testMethod).withValue(0, DependencyTestData.class.getName());
|
||||||
|
vm.build(fileName -> new ByteArrayOutputStream(), "out");
|
||||||
|
|
||||||
|
List<Problem> problems = vm.getProblemProvider().getSevereProblems();
|
||||||
|
if (!problems.isEmpty()) {
|
||||||
|
Problem problem = problems.get(0);
|
||||||
|
Assert.fail("Error at " + problem.getLocation().getSourceLocation() + ": " + problem.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodHolder method = classSource.get(testMethod.getClassName()).getMethod(testMethod.getDescriptor());
|
||||||
|
List<Assertion> assertions = collectAssertions(method);
|
||||||
|
processAssertions(assertions, vm.getDependencyInfo().getMethod(testMethod), vm.getDependencyInfo(),
|
||||||
|
method.getProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processAssertions(List<Assertion> assertions, MethodDependencyInfo methodDep,
|
||||||
|
DependencyInfo dependencyInfo, Program program) {
|
||||||
|
ClassInference classInference = new ClassInference(dependencyInfo);
|
||||||
|
classInference.infer(program, methodDep.getReference());
|
||||||
|
|
||||||
|
for (Assertion assertion : assertions) {
|
||||||
|
ValueDependencyInfo valueDep = methodDep.getVariable(assertion.value);
|
||||||
|
String[] actualTypes = valueDep.getTypes();
|
||||||
|
String[] expectedTypes = assertion.expectedTypes.clone();
|
||||||
|
Arrays.sort(actualTypes);
|
||||||
|
Arrays.sort(expectedTypes);
|
||||||
|
Assert.assertArrayEquals("Assertion at " + assertion.location, expectedTypes, actualTypes);
|
||||||
|
|
||||||
|
actualTypes = classInference.classesOf(assertion.value);
|
||||||
|
Arrays.sort(actualTypes);
|
||||||
|
Assert.assertArrayEquals("Assertion at " + assertion.location + " (class inference)",
|
||||||
|
expectedTypes, actualTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Assertion> collectAssertions(MethodHolder method) {
|
||||||
|
Program program = method.getProgram();
|
||||||
|
|
||||||
|
AliasCollector aliasCollector = new AliasCollector(program.variableCount());
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(aliasCollector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int[] aliases = aliasCollector.disjointSet.pack(program.variableCount());
|
||||||
|
|
||||||
|
AssertionCollector assertionCollector = new AssertionCollector(aliases);
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(assertionCollector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertionCollector.postProcess();
|
||||||
|
|
||||||
|
return assertionCollector.assertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AliasCollector extends AbstractInstructionVisitor {
|
||||||
|
DisjointSet disjointSet = new DisjointSet();
|
||||||
|
|
||||||
|
AliasCollector(int variableCount) {
|
||||||
|
for (int i = 0; i < variableCount; ++i) {
|
||||||
|
disjointSet.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(AssignInstruction insn) {
|
||||||
|
disjointSet.union(insn.getReceiver().getIndex(), insn.getAssignee().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnwrapArrayInstruction insn) {
|
||||||
|
disjointSet.union(insn.getReceiver().getIndex(), insn.getArray().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AssertionCollector extends AbstractInstructionVisitor {
|
||||||
|
static final MethodReference assertionMethod = new MethodReference(MetaAssertions.class,
|
||||||
|
"assertTypes", Object.class, Class[].class, void.class);
|
||||||
|
int[] aliases;
|
||||||
|
List<Assertion> assertions = new ArrayList<>();
|
||||||
|
IntSet[] arrayContent;
|
||||||
|
ValueType[] classConstants;
|
||||||
|
|
||||||
|
AssertionCollector(int[] aliases) {
|
||||||
|
this.aliases = aliases;
|
||||||
|
classConstants = new ValueType[aliases.length];
|
||||||
|
arrayContent = new IntSet[aliases.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
void postProcess() {
|
||||||
|
int[] aliasInstances = new int[aliases.length];
|
||||||
|
Arrays.fill(aliasInstances, -1);
|
||||||
|
for (int i = 0; i < aliases.length; ++i) {
|
||||||
|
int alias = aliases[i];
|
||||||
|
if (aliasInstances[alias] < 0) {
|
||||||
|
aliasInstances[alias] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Assertion assertion : assertions) {
|
||||||
|
IntSet items = arrayContent[assertion.array];
|
||||||
|
if (items != null) {
|
||||||
|
Set<String> expectedClasses = new HashSet<>();
|
||||||
|
for (int item : items.toArray()) {
|
||||||
|
ValueType constant = classConstants[item];
|
||||||
|
if (constant != null) {
|
||||||
|
String expectedClass;
|
||||||
|
if (constant instanceof ValueType.Object) {
|
||||||
|
expectedClass = ((ValueType.Object) constant).getClassName();
|
||||||
|
} else {
|
||||||
|
expectedClass = constant.toString();
|
||||||
|
}
|
||||||
|
expectedClasses.add(expectedClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertion.expectedTypes = expectedClasses.toArray(new String[0]);
|
||||||
|
} else {
|
||||||
|
assertion.expectedTypes = new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
assertion.value = aliasInstances[assertion.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (insn.getMethod().equals(assertionMethod)) {
|
||||||
|
Assertion assertion = new Assertion();
|
||||||
|
assertion.value = aliases[insn.getArguments().get(0).getIndex()];
|
||||||
|
assertion.array = aliases[insn.getArguments().get(1).getIndex()];
|
||||||
|
assertion.location = insn.getLocation();
|
||||||
|
assertions.add(assertion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
classConstants[aliases[insn.getReceiver().getIndex()]] = insn.getConstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutElementInstruction insn) {
|
||||||
|
int array = aliases[insn.getArray().getIndex()];
|
||||||
|
int value = aliases[insn.getValue().getIndex()];
|
||||||
|
IntSet items = arrayContent[array];
|
||||||
|
if (items == null) {
|
||||||
|
items = new IntOpenHashSet();
|
||||||
|
arrayContent[array] = items;
|
||||||
|
}
|
||||||
|
items.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Assertion {
|
||||||
|
int value;
|
||||||
|
int array;
|
||||||
|
TextLocation location;
|
||||||
|
String[] expectedTypes;
|
||||||
|
}
|
||||||
|
}
|
138
tests/src/test/java/org/teavm/dependency/DependencyTestData.java
Normal file
138
tests/src/test/java/org/teavm/dependency/DependencyTestData.java
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.dependency;
|
||||||
|
|
||||||
|
public class DependencyTestData {
|
||||||
|
public void virtualCall() {
|
||||||
|
MetaAssertions.assertTypes(getI(0).foo(), String.class, Integer.class, Class.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void instanceOf() {
|
||||||
|
MetaAssertions.assertTypes((String) getI(0).foo(), String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void catchException() throws Exception {
|
||||||
|
try {
|
||||||
|
throw createException(0);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
MetaAssertions.assertTypes(e, IndexOutOfBoundsException.class);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
MetaAssertions.assertTypes(e, UnsupportedOperationException.class, IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void propagateException() {
|
||||||
|
try {
|
||||||
|
catchException();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
MetaAssertions.assertTypes(e, Exception.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrays() {
|
||||||
|
Object[] array = { new String("123"), new Integer(123), String.class };
|
||||||
|
MetaAssertions.assertTypes(array[0], String.class, Integer.class, Class.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arraysPassed() {
|
||||||
|
Object[] array = new Object[3];
|
||||||
|
fillArray(array);
|
||||||
|
MetaAssertions.assertTypes(array[0], String.class, Integer.class, Class.class);
|
||||||
|
|
||||||
|
Object[] array2 = new Object[3];
|
||||||
|
staticArrayField = array2;
|
||||||
|
fillStaticArray();
|
||||||
|
MetaAssertions.assertTypes(array2[0], Long.class, RuntimeException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arraysRetrieved() {
|
||||||
|
Object[] array = createArray();
|
||||||
|
MetaAssertions.assertTypes(array[0], String.class, Integer.class, Class.class);
|
||||||
|
|
||||||
|
staticArrayField = new Object[3];
|
||||||
|
Object[] array2 = staticArrayField;
|
||||||
|
fillStaticArray();
|
||||||
|
MetaAssertions.assertTypes(array2[0], Long.class, RuntimeException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object[] staticArrayField;
|
||||||
|
|
||||||
|
private Object[] createArray() {
|
||||||
|
Object[] array = new Object[3];
|
||||||
|
fillArray(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillArray(Object[] array) {
|
||||||
|
array[0] = "123";
|
||||||
|
array[1] = 123;
|
||||||
|
array[2] = String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillStaticArray() {
|
||||||
|
staticArrayField[0] = 42L;
|
||||||
|
staticArrayField[0] = new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private I getI(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return new A();
|
||||||
|
case 1:
|
||||||
|
return new B();
|
||||||
|
default:
|
||||||
|
return new C();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Exception createException(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
case 1:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
case 2:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
default:
|
||||||
|
return new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
Object foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
class A implements I {
|
||||||
|
@Override
|
||||||
|
public Object foo() {
|
||||||
|
return "123";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B implements I {
|
||||||
|
@Override
|
||||||
|
public Object foo() {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class C implements I {
|
||||||
|
@Override
|
||||||
|
public Object foo() {
|
||||||
|
return 123;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
tests/src/test/java/org/teavm/dependency/MetaAssertions.java
Normal file
25
tests/src/test/java/org/teavm/dependency/MetaAssertions.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.dependency;
|
||||||
|
|
||||||
|
public final class MetaAssertions {
|
||||||
|
private MetaAssertions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertTypes(Object value, Class<?>... types) {
|
||||||
|
// do nothing, process by TeaVM test infrastructure
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user