mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-21 01:00:54 +08:00
wasm gc: reduce number of generated virtual tables, fix non-matching types in case of null literals
This commit is contained in:
parent
9b5e1e7661
commit
e4fa6bd364
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.teavm.classlib.impl.console;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
@ -30,7 +30,7 @@ public abstract class JsConsolePrintStream extends PrintStream {
|
||||
private Runnable flushAction;
|
||||
|
||||
public JsConsolePrintStream() {
|
||||
super(new ByteArrayOutputStream());
|
||||
super((OutputStream) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -103,6 +103,11 @@ public class PreciseTypeInference extends BaseTypeInference<PreciseValueType> {
|
||||
return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PreciseValueType arrayType(PreciseValueType preciseValueType) {
|
||||
return new PreciseValueType(ValueType.arrayOf(preciseValueType.valueType), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||
if (invocationType == InvocationType.SPECIAL) {
|
||||
|
@ -38,6 +38,7 @@ public class WasmGCVariableCategoryProvider implements VariableCategoryProvider
|
||||
public Object[] getCategories(Program program, MethodReference method) {
|
||||
inference = new PreciseTypeInference(program, method, hierarchy, returnTypes);
|
||||
inference.setPhisSkipped(true);
|
||||
inference.setBackPropagation(true);
|
||||
var result = new Object[program.variableCount()];
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
var type = inference.typeOf(program.variableAt(i));
|
||||
|
@ -104,7 +104,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
|
||||
private WasmGenerationContext context;
|
||||
private WasmClassGenerator classGenerator;
|
||||
private MethodReference currentMethod;
|
||||
|
||||
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
|
||||
private WasmBlock lastTryBlock;
|
||||
@ -118,11 +117,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
public WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
||||
BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod,
|
||||
int firstVariable, boolean async) {
|
||||
super(context, function, firstVariable, async);
|
||||
super(context, currentMethod, function, firstVariable, async);
|
||||
this.context = context;
|
||||
this.classGenerator = classGenerator;
|
||||
this.binaryWriter = binaryWriter;
|
||||
this.currentMethod = currentMethod;
|
||||
this.managed = context.characteristics.isManaged(currentMethod);
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
|
||||
private static final int SWITCH_TABLE_THRESHOLD = 256;
|
||||
private BaseWasmGenerationContext context;
|
||||
protected final MethodReference currentMethod;
|
||||
protected final WasmTypeInference typeInference;
|
||||
protected final WasmFunction function;
|
||||
private int firstVariable;
|
||||
@ -131,9 +132,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
protected WasmExpression result;
|
||||
protected List<WasmExpression> resultConsumer;
|
||||
|
||||
public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, WasmFunction function,
|
||||
int firstVariable, boolean async) {
|
||||
public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, MethodReference currentMethod,
|
||||
WasmFunction function, int firstVariable, boolean async) {
|
||||
this.context = context;
|
||||
this.currentMethod = currentMethod;
|
||||
this.function = function;
|
||||
this.firstVariable = firstVariable;
|
||||
tempVars = new TemporaryVariablePool(function);
|
||||
@ -833,6 +835,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
|
||||
protected abstract CallSiteIdentifier generateCallSiteId(TextLocation location);
|
||||
|
||||
protected void acceptWithType(Expr expr, ValueType type) {
|
||||
accept(expr);
|
||||
}
|
||||
|
||||
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
|
||||
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
||||
var method = context.classes().resolve(expr.getMethod());
|
||||
@ -842,8 +848,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
: context.functions().forInstanceMethod(reference);
|
||||
|
||||
var call = new WasmCall(function);
|
||||
for (var argument : expr.getArguments()) {
|
||||
accept(argument);
|
||||
var arguments = expr.getArguments();
|
||||
for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) {
|
||||
var argument = arguments.get(i);
|
||||
var type = expr.getType() == InvocationType.STATIC
|
||||
? reference.parameterType(i)
|
||||
: i == 0 ? ValueType.object(reference.getClassName()) : reference.parameterType(i - 1);
|
||||
acceptWithType(argument, type);
|
||||
call.getArguments().add(result);
|
||||
}
|
||||
if (expr.getType() == InvocationType.SPECIAL) {
|
||||
@ -866,8 +877,12 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
var function = context.functions().forInstanceMethod(expr.getMethod());
|
||||
var call = new WasmCall(function);
|
||||
call.getArguments().add(new WasmGetLocal(tmp));
|
||||
for (var argument : expr.getArguments()) {
|
||||
accept(argument);
|
||||
var arguments = expr.getArguments();
|
||||
acceptWithType(arguments.get(0), ValueType.object(expr.getMethod().getClassName()));
|
||||
call.getArguments().add(result);
|
||||
for (int i = 1; i < arguments.size(); i++) {
|
||||
var argument = arguments.get(i);
|
||||
acceptWithType(argument, expr.getMethod().parameterType(i));
|
||||
call.getArguments().add(result);
|
||||
}
|
||||
if (callSiteId != null) {
|
||||
@ -881,7 +896,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
return block;
|
||||
} else {
|
||||
var reference = expr.getMethod();
|
||||
accept(expr.getArguments().get(0));
|
||||
acceptWithType(expr.getArguments().get(0), ValueType.object(expr.getMethod().getClassName()));
|
||||
var instance = result;
|
||||
var block = new WasmBlock(false);
|
||||
block.setType(mapType(reference.getReturnType()));
|
||||
@ -893,7 +908,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
var arguments = new ArrayList<WasmExpression>();
|
||||
arguments.add(instance);
|
||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||
accept(expr.getArguments().get(i));
|
||||
acceptWithType(expr.getArguments().get(i), expr.getMethod().parameterType(i - 1));
|
||||
arguments.add(result);
|
||||
}
|
||||
if (callSiteId != null) {
|
||||
@ -1091,7 +1106,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
@Override
|
||||
public void visit(ReturnStatement statement) {
|
||||
if (statement.getResult() != null) {
|
||||
accept(statement.getResult());
|
||||
acceptWithType(statement.getResult(), currentMethod.getReturnType());
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
@ -1102,7 +1117,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
|
||||
@Override
|
||||
public void visit(InstanceOfExpr expr) {
|
||||
accept(expr.getExpr());
|
||||
acceptWithType(expr.getExpr(), expr.getType());
|
||||
|
||||
var block = new WasmBlock(false);
|
||||
block.setType(WasmType.INT32);
|
||||
@ -1143,7 +1158,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||
block.setType(wasmTargetType);
|
||||
block.setLocation(expr.getLocation());
|
||||
|
||||
accept(expr.getValue());
|
||||
acceptWithType(expr.getValue(), expr.getTarget());
|
||||
result.acceptVisitor(typeInference);
|
||||
var wasmSourceType = typeInference.getResult();
|
||||
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
|
||||
|
@ -20,10 +20,12 @@ import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||
@ -182,6 +184,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
classInfo = new WasmGCClassInfo(type);
|
||||
classInfoQueue.add(classInfo);
|
||||
classInfoMap.put(type, classInfo);
|
||||
VirtualTable virtualTable = null;
|
||||
if (!(type instanceof ValueType.Primitive)) {
|
||||
var name = type instanceof ValueType.Object
|
||||
? ((ValueType.Object) type).getClassName()
|
||||
@ -189,6 +192,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null);
|
||||
if (name != null) {
|
||||
var classReader = classSource.get(name);
|
||||
if (classReader == null || !classReader.hasModifier(ElementModifier.ABSTRACT)
|
||||
&& !classReader.hasModifier(ElementModifier.INTERFACE)) {
|
||||
virtualTable = virtualTables.lookup(name);
|
||||
}
|
||||
if (classReader != null && classReader.getParent() != null) {
|
||||
classInfo.structure.setSupertype(getClassInfo(classReader.getParent()).structure);
|
||||
}
|
||||
@ -199,7 +206,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
fillFields(classInfo, type);
|
||||
}
|
||||
var pointerName = names.forClassInstance(type);
|
||||
var classStructure = type instanceof ValueType.Object
|
||||
var classStructure = virtualTable != null && virtualTable.hasValidEntries()
|
||||
? initRegularClassStructure(((ValueType.Object) type).getClassName())
|
||||
: standardClasses.classClass().getStructure();
|
||||
classInfo.virtualTableStructure = classStructure;
|
||||
@ -213,7 +220,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
initArrayClass(classInfo, (ValueType.Array) type);
|
||||
} else if (type instanceof ValueType.Object) {
|
||||
initRegularClass(classInfo, classStructure, ((ValueType.Object) type).getClassName());
|
||||
initRegularClass(classInfo, virtualTable, classStructure, ((ValueType.Object) type).getClassName());
|
||||
}
|
||||
}
|
||||
return classInfo;
|
||||
@ -276,7 +283,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
};
|
||||
}
|
||||
|
||||
private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) {
|
||||
private void initRegularClass(WasmGCClassInfo classInfo, VirtualTable virtualTable, WasmStructure classStructure,
|
||||
String name) {
|
||||
var cls = classSource.get(name);
|
||||
if (classInitializerInfo.isDynamicInitializer(name)) {
|
||||
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
|
||||
@ -290,35 +298,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
var ranges = tagRegistry.getRanges(name);
|
||||
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
||||
target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag)));
|
||||
var metadataReg = metadataRequirements.getInfo(name);
|
||||
if (metadataReg.name()) {
|
||||
var metadataReq = metadataRequirements.getInfo(name);
|
||||
if (metadataReq.name()) {
|
||||
var namePtr = strings.getStringConstant(name).global;
|
||||
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
||||
}
|
||||
if (cls != null) {
|
||||
if (metadataReg.simpleName() && cls.getSimpleName() != null) {
|
||||
if (metadataReq.simpleName() && cls.getSimpleName() != null) {
|
||||
var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
|
||||
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
||||
}
|
||||
if (cls.getParent() != null) {
|
||||
if (cls.getParent() != null && metadataReq.superclass()) {
|
||||
var parent = getClassInfo(cls.getParent());
|
||||
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
|
||||
}
|
||||
}
|
||||
var virtualTable = virtualTables.lookup(name);
|
||||
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTableFieldOffset,
|
||||
name);
|
||||
if (virtualTable != null && virtualTable.hasValidEntries()) {
|
||||
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable,
|
||||
virtualTableFieldOffset, name, new HashSet<>());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
|
||||
VirtualTable virtualTable, int index, String origin) {
|
||||
if (virtualTable.getParent() != null) {
|
||||
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin);
|
||||
}
|
||||
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) {
|
||||
for (var method : virtualTable.getMethods()) {
|
||||
var entry = virtualTable.getEntry(method);
|
||||
if (entry != null && entry.getImplementor() != null) {
|
||||
if (entry != null && entry.getImplementor() != null && filled.add(method)) {
|
||||
var function = functionProvider.forInstanceMethod(entry.getImplementor());
|
||||
if (!origin.equals(entry.getImplementor().getClassName())) {
|
||||
var functionType = getFunctionType(virtualTable.getClassName(), method);
|
||||
@ -341,6 +347,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
|
||||
}
|
||||
}
|
||||
if (virtualTable.getParent() != null) {
|
||||
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
|
||||
filled);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -62,14 +62,32 @@ import org.teavm.model.ValueType;
|
||||
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
private WasmGCGenerationContext context;
|
||||
private WasmGCGenerationUtil generationUtil;
|
||||
private WasmType expectedType;
|
||||
|
||||
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
|
||||
int firstVariable, boolean async) {
|
||||
super(context, function, firstVariable, async);
|
||||
public WasmGCGenerationVisitor(WasmGCGenerationContext context, MethodReference currentMethod,
|
||||
WasmFunction function, int firstVariable, boolean async) {
|
||||
super(context, currentMethod, function, firstVariable, async);
|
||||
this.context = context;
|
||||
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void accept(Expr expr) {
|
||||
accept(expr, null);
|
||||
}
|
||||
|
||||
protected void accept(Expr expr, WasmType type) {
|
||||
var previousExpectedType = expectedType;
|
||||
expectedType = type;
|
||||
super.accept(expr);
|
||||
expectedType = previousExpectedType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acceptWithType(Expr expr, ValueType type) {
|
||||
accept(expr, mapType(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isManaged() {
|
||||
return false;
|
||||
@ -137,24 +155,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
@Override
|
||||
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
|
||||
if (qualified == null) {
|
||||
accept(value);
|
||||
var wasmValue = result;
|
||||
var global = context.classInfoProvider().getStaticFieldLocation(field);
|
||||
accept(value, global.getType());
|
||||
var wasmValue = result;
|
||||
var result = new WasmSetGlobal(global, wasmValue);
|
||||
result.setLocation(location);
|
||||
resultConsumer.add(result);
|
||||
} else {
|
||||
accept(qualified);
|
||||
acceptWithType(qualified, ValueType.object(field.getClassName()));
|
||||
var target = result;
|
||||
accept(value);
|
||||
var wasmValue = result;
|
||||
|
||||
target.acceptVisitor(typeInference);
|
||||
var type = (WasmType.CompositeReference) typeInference.getResult();
|
||||
var struct = (WasmStructure) type.composite;
|
||||
|
||||
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
|
||||
|
||||
accept(value, struct.getFields().get(fieldIndex).asUnpackedType());
|
||||
var wasmValue = result;
|
||||
|
||||
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
|
||||
expr.setLocation(location);
|
||||
resultConsumer.add(expr);
|
||||
@ -175,7 +192,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
|
||||
@Override
|
||||
protected WasmExpression nullLiteral() {
|
||||
return new WasmNullConstant(WasmType.Reference.STRUCT);
|
||||
return new WasmNullConstant(expectedType instanceof WasmType.Reference
|
||||
? (WasmType.Reference) expectedType
|
||||
: WasmType.Reference.STRUCT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -404,7 +423,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||
result = new WasmGetGlobal(global);
|
||||
result.setLocation(expr.getLocation());
|
||||
} else {
|
||||
accept(expr.getQualified());
|
||||
acceptWithType(expr.getQualified(), ValueType.object(expr.getField().getClassName()));
|
||||
var target = result;
|
||||
|
||||
target.acceptVisitor(typeInference);
|
||||
|
@ -249,7 +249,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||
}
|
||||
|
||||
addInitializerErase(method, function);
|
||||
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), function, firstVar, false);
|
||||
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), method.getReference(),
|
||||
function, firstVar, false);
|
||||
visitor.generate(ast.getBody(), function.getBody());
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ package org.teavm.model.analysis;
|
||||
|
||||
import com.carrotsearch.hppc.IntStack;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Objects;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphBuilder;
|
||||
@ -39,6 +40,7 @@ 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.ExitInstruction;
|
||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||
import org.teavm.model.instructions.GetElementInstruction;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
@ -51,6 +53,7 @@ import org.teavm.model.instructions.NegateInstruction;
|
||||
import org.teavm.model.instructions.NullCheckInstruction;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.instructions.NumericOperandType;
|
||||
import org.teavm.model.instructions.PutFieldInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||
|
||||
@ -62,6 +65,7 @@ public abstract class BaseTypeInference<T> {
|
||||
private Graph arrayGraph;
|
||||
private Graph arrayUnwrapGraph;
|
||||
private boolean phisSkipped;
|
||||
private boolean backPropagation;
|
||||
|
||||
public BaseTypeInference(Program program, MethodReference reference) {
|
||||
this.program = program;
|
||||
@ -72,6 +76,10 @@ public abstract class BaseTypeInference<T> {
|
||||
this.phisSkipped = phisSkipped;
|
||||
}
|
||||
|
||||
public void setBackPropagation(boolean backPropagation) {
|
||||
this.backPropagation = backPropagation;
|
||||
}
|
||||
|
||||
private void prepare() {
|
||||
types = new Object[program.variableCount()];
|
||||
var visitor = new InitialTypeVisitor(program.variableCount());
|
||||
@ -153,10 +161,80 @@ public abstract class BaseTypeInference<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private void propagateBack() {
|
||||
if (!backPropagation) {
|
||||
return;
|
||||
}
|
||||
var hasNullTypes = false;
|
||||
for (var type : types) {
|
||||
if (type == null) {
|
||||
hasNullTypes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasNullTypes) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nullTypes = new boolean[program.variableCount()];
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
nullTypes[i] = types[i] == null;
|
||||
}
|
||||
var stack = new IntStack();
|
||||
var typeStack = new ArrayDeque<T>();
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
if (nullTypes[i]) {
|
||||
for (var j : graph.outgoingEdges(i)) {
|
||||
if (!nullTypes[j]) {
|
||||
typeStack.push((T) types[j]);
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var visitor = new BackPropagationVisitor(nullTypes, stack, typeStack);
|
||||
for (var block : program.getBasicBlocks()) {
|
||||
for (var insn : block) {
|
||||
insn.acceptVisitor(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
var variable = stack.pop();
|
||||
var type = typeStack.pop();
|
||||
var formerType = (T) types[variable];
|
||||
if (Objects.equals(formerType, type)) {
|
||||
continue;
|
||||
}
|
||||
type = doMerge(type, formerType);
|
||||
if (Objects.equals(type, formerType) || type == null) {
|
||||
continue;
|
||||
}
|
||||
types[variable] = type;
|
||||
for (var pred : graph.incomingEdges(variable)) {
|
||||
if (nullTypes[pred] && !Objects.equals(types[pred], type)) {
|
||||
stack.push(pred);
|
||||
typeStack.push(type);
|
||||
}
|
||||
}
|
||||
if (arrayGraph.incomingEdgesCount(variable) > 0) {
|
||||
var arrayType = arrayType(type);
|
||||
for (var pred : arrayGraph.incomingEdges(variable)) {
|
||||
if (nullTypes[pred] && !Objects.equals(types[pred], arrayType)) {
|
||||
stack.push(pred);
|
||||
typeStack.push(arrayType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ensure() {
|
||||
if (types == null) {
|
||||
prepare();
|
||||
propagate();
|
||||
propagateBack();
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,6 +263,10 @@ public abstract class BaseTypeInference<T> {
|
||||
|
||||
protected abstract T elementType(T t);
|
||||
|
||||
protected T arrayType(T t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected T methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||
return mapType(methodRef.getReturnType());
|
||||
}
|
||||
@ -380,4 +462,55 @@ public abstract class BaseTypeInference<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BackPropagationVisitor extends AbstractInstructionVisitor {
|
||||
private boolean[] nullTypes;
|
||||
private IntStack stack;
|
||||
private Deque<T> typeStack;
|
||||
|
||||
BackPropagationVisitor(boolean[] nullTypes, IntStack stack, Deque<T> typeStack) {
|
||||
this.nullTypes = nullTypes;
|
||||
this.stack = stack;
|
||||
this.typeStack = typeStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ExitInstruction insn) {
|
||||
if (insn.getValueToReturn() != null) {
|
||||
push(insn.getValueToReturn(), reference.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeInstruction insn) {
|
||||
if (insn.getInstance() != null) {
|
||||
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||
}
|
||||
for (var i = 0; i < insn.getArguments().size(); ++i) {
|
||||
push(insn.getArguments().get(i), reference.parameterType(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(GetFieldInstruction insn) {
|
||||
if (insn.getInstance() != null) {
|
||||
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PutFieldInstruction insn) {
|
||||
if (insn.getInstance() != null) {
|
||||
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||
}
|
||||
push(insn.getValue(), insn.getFieldType());
|
||||
}
|
||||
|
||||
private void push(Variable variable, ValueType type) {
|
||||
if (nullTypes[variable.getIndex()]) {
|
||||
stack.push(variable.getIndex());
|
||||
typeStack.push(mapType(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ public class ClassMetadataRequirements {
|
||||
private static final MethodReference GET_NAME_METHOD = new MethodReference(Class.class, "getName", String.class);
|
||||
private static final MethodReference GET_SIMPLE_NAME_METHOD = new MethodReference(Class.class,
|
||||
"getSimpleName", String.class);
|
||||
private static final MethodReference GET_SUPERCLASS_METHOD = new MethodReference(Class.class, "getSuperclass",
|
||||
Class.class);
|
||||
private static final MethodReference GET_DECLARING_CLASS_METHOD = new MethodReference(Class.class,
|
||||
"getDeclaringClass", Class.class);
|
||||
private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class,
|
||||
@ -49,6 +51,14 @@ public class ClassMetadataRequirements {
|
||||
}
|
||||
}
|
||||
|
||||
var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD);
|
||||
if (getSuperclassMethod != null) {
|
||||
String[] classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes();
|
||||
for (String className : classNames) {
|
||||
requirements.computeIfAbsent(className, k -> new ClassInfo()).declaringClass = true;
|
||||
}
|
||||
}
|
||||
|
||||
MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD);
|
||||
if (getDeclaringClassMethod != null) {
|
||||
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
|
||||
@ -95,6 +105,7 @@ public class ClassMetadataRequirements {
|
||||
boolean simpleName;
|
||||
boolean declaringClass;
|
||||
boolean enclosingClass;
|
||||
boolean superclass;
|
||||
|
||||
@Override
|
||||
public boolean name() {
|
||||
@ -115,6 +126,11 @@ public class ClassMetadataRequirements {
|
||||
public boolean enclosingClass() {
|
||||
return enclosingClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean superclass() {
|
||||
return superclass;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Info {
|
||||
@ -125,5 +141,7 @@ public class ClassMetadataRequirements {
|
||||
boolean declaringClass();
|
||||
|
||||
boolean enclosingClass();
|
||||
|
||||
boolean superclass();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ public class VirtualTable {
|
||||
private List<? extends MethodDescriptor> methods;
|
||||
private Set<MethodDescriptor> methodSet;
|
||||
private Map<MethodDescriptor, VirtualTableEntry> entryMap;
|
||||
private boolean hasValidEntries;
|
||||
private boolean hasValidEntriesComputed;
|
||||
|
||||
VirtualTable(String className, VirtualTable parent, List<? extends MethodDescriptor> methods,
|
||||
Set<MethodDescriptor> methodSet, Map<MethodDescriptor, VirtualTableEntry> entryMap) {
|
||||
@ -70,4 +72,23 @@ public class VirtualTable {
|
||||
public int size() {
|
||||
return methods.size() + (parent != null ? parent.size() : 0);
|
||||
}
|
||||
|
||||
public boolean hasValidEntries() {
|
||||
if (!hasValidEntriesComputed) {
|
||||
hasValidEntriesComputed = true;
|
||||
hasValidEntries = false;
|
||||
if (entryMap != null) {
|
||||
for (var entry : entryMap.values()) {
|
||||
if (entry.getImplementor() != null) {
|
||||
hasValidEntries = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent != null && parent.hasValidEntries()) {
|
||||
hasValidEntries = true;
|
||||
}
|
||||
}
|
||||
return hasValidEntries;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user