org.teavm.dependency.DependencyAnalyzer Maven / Gradle / Ivy
/*
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.dependency;
import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
import org.objectweb.asm.tree.ClassNode;
import org.teavm.cache.IncrementalDependencyProvider;
import org.teavm.cache.IncrementalDependencyRegistration;
import org.teavm.callgraph.CallGraph;
import org.teavm.common.CachedFunction;
import org.teavm.common.ServiceRepository;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.PlatformMarker;
import org.teavm.model.AnnotationReader;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.InvokeDynamicInstruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ReferenceCache;
import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
import org.teavm.model.util.BasicBlockSplitter;
import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils;
import org.teavm.parsing.Parser;
public abstract class DependencyAnalyzer implements DependencyInfo {
private static final int PROPAGATION_STACK_THRESHOLD = 50;
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("", void.class);
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
static final boolean shouldTag = System.getProperty("org.teavm.tagDependencies", "false").equals("true")
|| shouldLog;
static final boolean dependencyReport = System.getProperty("org.teavm.dependencyReport", "false").equals("true");
private int classNameSuffix;
private ClassReaderSource unprocessedClassSource;
private DependencyClassSource classSource;
ClassReaderSource agentClassSource;
private ClassLoader classLoader;
private Map>> methodReaderCache = new HashMap<>(1000, 0.5f);
private Map implementationCache = new HashMap<>();
private Function fieldReaderCache;
private Map> methodCache = new HashMap<>();
private Set reachedMethods = new LinkedHashSet<>();
private Set readonlyReachedMethods = Collections.unmodifiableSet(reachedMethods);
private CachedFunction fieldCache;
private CachedFunction classCache;
private List listeners = new ArrayList<>();
private ServiceRepository services;
private Deque pendingTransitions = new ArrayDeque<>();
private Deque tasks = new ArrayDeque<>();
private Queue deferredTasks = new ArrayDeque<>();
List types = new ArrayList<>();
private Map typeMap = new HashMap<>();
private DependencyAnalyzerInterruptor interruptor;
private boolean interrupted;
private Diagnostics diagnostics;
DefaultCallGraph callGraph = new DefaultCallGraph();
private DependencyAgent agent;
Map bootstrapMethodSubstitutors = new HashMap<>();
Map dependencyPlugins = new HashMap<>();
private boolean completing;
private Map superClassFilters = new HashMap<>();
private List allNodes = new ArrayList<>();
private ClassHierarchy classHierarchy;
IncrementalCache incrementalCache = new IncrementalCache();
boolean asyncSupported;
private ReferenceCache referenceCache;
private Set generatedClassNames = new HashSet<>();
DependencyType classType;
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics, ReferenceCache referenceCache) {
this.unprocessedClassSource = classSource;
this.diagnostics = diagnostics;
this.referenceCache = referenceCache;
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache);
agentClassSource = this.classSource;
classHierarchy = new ClassHierarchy(this.classSource);
this.classLoader = classLoader;
this.services = services;
fieldReaderCache = new CachedFunction<>(preimage -> this.classSource.resolveMutable(preimage));
fieldCache = new CachedFunction<>(preimage -> {
FieldReader field = fieldReaderCache.apply(preimage);
if (field != null && !field.getReference().equals(preimage)) {
return fieldCache.apply(field.getReference());
}
FieldDependency node = createFieldNode(preimage, field);
if (field != null && field.getInitialValue() instanceof String) {
node.getValue().propagate(getType("java.lang.String"));
}
return node;
});
classCache = new CachedFunction<>(this::createClassDependency);
agent = new DependencyAgent(this);
classType = getType("java.lang.Class");
}
public void setObfuscated(boolean obfuscated) {
classSource.obfuscated = obfuscated;
}
public void setStrict(boolean strict) {
classSource.strict = strict;
}
public void setAsyncSupported(boolean asyncSupported) {
this.asyncSupported = asyncSupported;
}
public DependencyAgent getAgent() {
return agent;
}
public DependencyAnalyzerInterruptor getInterruptor() {
return interruptor;
}
public void setInterruptor(DependencyAnalyzerInterruptor interruptor) {
this.interruptor = interruptor;
}
public boolean wasInterrupted() {
return interrupted;
}
public DependencyType getType(String name) {
DependencyType type = typeMap.get(name);
if (type == null) {
type = new DependencyType(this, name, types.size());
types.add(type);
typeMap.put(name, type);
}
return type;
}
public DependencyNode createNode() {
return createNode(null);
}
DependencyNode createNode(ValueType typeFilter) {
if (typeFilter != null && typeFilter.isObject("java.lang.Object")) {
typeFilter = null;
}
DependencyNode node = new DependencyNode(this, typeFilter);
allNodes.add(node);
return node;
}
@Override
public ClassReaderSource getClassSource() {
return classSource != null ? classSource : agentClassSource;
}
public ClassReaderSource getUnprocessedClassSource() {
return unprocessedClassSource;
}
public boolean isSynthesizedClass(String className) {
return classSource != null ? classSource.isGeneratedClass(className) : generatedClassNames.contains(className);
}
public ClassHierarchy getClassHierarchy() {
return classHierarchy;
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
public String generateClassName() {
return "$$teavm_generated_class$$" + classNameSuffix++;
}
public String submitClassFile(byte[] data) {
ClassNode node = new ClassNode();
org.objectweb.asm.ClassReader reader = new org.objectweb.asm.ClassReader(data);
reader.accept(node, 0);
submitClass(new Parser(referenceCache).parseClass(node));
return node.name;
}
public void submitClass(ClassHolder cls) {
if (completing) {
throw new IllegalStateException("Can't submit class during completion phase");
}
classSource.submit(ModelUtils.copyClass(cls));
}
public void submitMethod(MethodReference methodRef, Program program) {
if (!completing) {
ClassHolder cls = classSource.get(methodRef.getClassName());
if (cls == null) {
throw new IllegalArgumentException("Class not found: " + methodRef.getClassName());
}
if (cls.getMethod(methodRef.getDescriptor()) != null) {
throw new IllegalArgumentException("Method already exists: " + methodRef.getClassName());
}
MethodHolder method = new MethodHolder(methodRef.getDescriptor());
method.getModifiers().add(ElementModifier.STATIC);
method.setProgram(ProgramUtils.copy(program));
new UnreachableBasicBlockEliminator().optimize(program);
cls.addMethod(method);
} else {
MethodDependency dep = getMethod(methodRef);
if (dep == null) {
throw new IllegalArgumentException("Method was not reached: " + methodRef);
}
MethodHolder method = dep.method;
if (!method.hasModifier(ElementModifier.NATIVE)) {
throw new IllegalArgumentException("Method is not native: " + methodRef);
}
if (!dep.used) {
return;
}
method.getModifiers().remove(ElementModifier.NATIVE);
method.setProgram(ProgramUtils.copy(program));
new UnreachableBasicBlockEliminator().optimize(method.getProgram());
dep.used = false;
lock(dep, false);
deferredTasks.add(() -> {
processInvokeDynamic(dep);
processMethod(dep);
dep.used = true;
});
processQueue();
}
}
protected abstract void processMethod(MethodDependency methodDep);
public void addDependencyListener(DependencyListener listener) {
listeners.add(listener);
listener.started(agent);
}
public void addClassTransformer(ClassHolderTransformer transformer) {
classSource.addTransformer(transformer);
}
public void addEntryPoint(MethodReference methodRef, String... argumentTypes) {
ValueType[] parameters = methodRef.getDescriptor().getParameterTypes();
if (parameters.length + 1 != argumentTypes.length) {
throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments");
}
MethodDependency method = linkMethod(methodRef);
method.use();
DependencyNode[] varNodes = method.getVariables();
varNodes[0].propagate(getType(methodRef.getClassName()));
for (int i = 0; i < argumentTypes.length; ++i) {
varNodes[i + 1].propagate(getType(argumentTypes[i]));
}
}
private int propagationDepth;
void schedulePropagation(DependencyConsumer consumer, DependencyType type) {
if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth;
consumer.consume(type);
--propagationDepth;
} else {
tasks.add(() -> consumer.consume(type));
}
}
void schedulePropagation(Transition consumer, DependencyType type) {
if (!consumer.destination.filter(type)) {
return;
}
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD
&& consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) {
++propagationDepth;
consumer.consume(type);
--propagationDepth;
} else {
if (consumer.pendingTypes == null) {
pendingTransitions.add(consumer);
consumer.pendingTypes = new IntHashSet(50);
}
consumer.pendingTypes.add(type.index);
}
}
void schedulePropagation(Transition consumer, DependencyType[] types) {
if (types.length == 0) {
return;
}
if (types.length == 1) {
schedulePropagation(consumer, types[0]);
return;
}
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD
&& consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) {
++propagationDepth;
consumer.consume(types);
--propagationDepth;
} else {
if (consumer.pendingTypes == null) {
pendingTransitions.add(consumer);
consumer.pendingTypes = new IntHashSet(Math.max(50, types.length));
}
consumer.pendingTypes.ensureCapacity(types.length + consumer.pendingTypes.size());
for (DependencyType type : types) {
consumer.pendingTypes.add(type.index);
}
}
}
void schedulePropagation(DependencyConsumer consumer, DependencyType[] types) {
if (types.length == 0) {
return;
}
if (types.length == 1) {
schedulePropagation(consumer, types[0]);
return;
}
if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth;
for (DependencyType type : types) {
consumer.consume(type);
}
--propagationDepth;
} else {
tasks.add(() -> {
for (DependencyType type : types) {
consumer.consume(type);
}
});
}
}
public void defer(Runnable task) {
deferredTasks.add(task);
}
public ClassDependency linkClass(String className) {
if (completing && getClass(className) == null) {
throw new IllegalStateException("Can't link class during completion phase");
}
ClassDependency dep = classCache.apply(className);
if (!dep.activated) {
dep.activated = true;
if (!dep.isMissing()) {
deferredTasks.add(() -> {
for (DependencyListener listener : listeners) {
listener.classReached(agent, className);
}
});
ClassReader cls = dep.getClassReader();
if (cls.getParent() != null && !classCache.caches(cls.getParent())) {
linkClass(cls.getParent());
}
for (String iface : cls.getInterfaces()) {
if (!classCache.caches(iface)) {
linkClass(iface);
}
}
}
}
return dep;
}
private ClassDependency createClassDependency(String className) {
ClassReader cls = classSource.get(className);
return new ClassDependency(this, className, cls);
}
public MethodDependency linkMethod(String className, MethodDescriptor descriptor) {
MethodDependency dep = getMethodDependency(className, descriptor);
if (!dep.activated) {
reachedMethods.add(dep.getReference());
dep.activated = true;
if (!dep.isMissing()) {
for (DependencyListener listener : listeners) {
listener.methodReached(agent, dep);
}
activateDependencyPlugin(dep);
}
}
return dep;
}
public MethodDependency linkMethod(MethodReference method) {
return linkMethod(method.getClassName(), method.getDescriptor());
}
void initClass(ClassDependency cls, CallLocation location) {
ClassReader reader = cls.getClassReader();
MethodReader method = reader.getMethod(CLINIT_METHOD);
if (method != null) {
deferredTasks.add(() -> {
MethodDependency initMethod = linkMethod(method.getReference());
if (location != null) {
initMethod.addLocation(location);
}
initMethod.use();
});
}
}
private MethodDependency createMethodDep(MethodReference methodRef, MethodHolder method) {
ValueType[] arguments = methodRef.getParameterTypes();
int paramCount = arguments.length + 1;
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
parameterNodes[0] = createParameterNode(methodRef, ValueType.object(methodRef.getClassName()), 0);
for (int i = 0; i < arguments.length; ++i) {
parameterNodes[i + 1] = createParameterNode(methodRef, arguments[i], i + 1);
}
DependencyNode resultNode;
if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
resultNode = null;
} else {
resultNode = createResultNode(methodRef);
}
DependencyNode thrown = createThrownNode(methodRef);
MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
method, methodRef);
if (method != null) {
deferredTasks.add(() -> linkClass(dep.getMethod().getOwnerName())
.initClass(new CallLocation(dep.getMethod().getReference())));
}
return dep;
}
abstract DependencyNode createParameterNode(MethodReference method, ValueType type, int index);
abstract DependencyNode createResultNode(MethodReference method);
abstract DependencyNode createThrownNode(MethodReference method);
abstract DependencyNode createFieldNode(FieldReference field, ValueType type);
abstract DependencyNode createArrayItemNode(DependencyNode parent);
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);
void scheduleMethodAnalysis(MethodDependency dep) {
deferredTasks.add(() -> {
processInvokeDynamic(dep);
processMethod(dep);
});
}
@Override
public Collection getReachableMethods() {
return readonlyReachedMethods;
}
@Override
public Collection getReachableFields() {
return fieldCache.getCachedPreimages();
}
@Override
public Collection getReachableClasses() {
return classCache.getCachedPreimages();
}
public FieldDependency linkField(FieldReference fieldRef) {
FieldDependency dep = fieldCache.apply(fieldRef);
if (!dep.activated) {
dep.activated = true;
if (!dep.isMissing()) {
for (DependencyListener listener : listeners) {
listener.fieldReached(agent, dep);
}
}
}
return dep;
}
@Override
public FieldDependency getField(FieldReference fieldRef) {
return fieldCache.getKnown(fieldRef);
}
@Override
public ClassDependency getClass(String className) {
return classCache.getKnown(className);
}
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field) {
DependencyNode node = createFieldNode(fieldRef, field != null ? field.getType() : null);
return new FieldDependency(node, field, fieldRef);
}
private void activateDependencyPlugin(MethodDependency methodDep) {
attachDependencyPlugin(methodDep);
if (methodDep.dependencyPlugin != null) {
methodDep.dependencyPlugin.methodReached(agent, methodDep);
}
}
private void attachDependencyPlugin(MethodDependency methodDep) {
if (methodDep.dependencyPluginAttached) {
return;
}
methodDep.dependencyPluginAttached = true;
methodDep.dependencyPlugin = dependencyPlugins.get(methodDep.getReference());
if (methodDep.dependencyPlugin != null || isBootstrap()) {
return;
}
AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName());
if (depAnnot == null) {
return;
}
ValueType depType = depAnnot.getValue("value").getJavaClass();
String depClassName = ((ValueType.Object) depType).getClassName();
Class> depClass;
try {
depClass = Class.forName(depClassName, true, classLoader);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Dependency plugin not found: " + depClassName, e);
}
try {
methodDep.dependencyPlugin = (DependencyPlugin) depClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e);
}
}
@PlatformMarker
private static boolean isBootstrap() {
return false;
}
@Override
public MethodDependency getMethod(MethodReference methodRef) {
return getMethod(methodRef.getClassName(), methodRef.getDescriptor());
}
public MethodDependency getMethod(String className, MethodDescriptor descriptor) {
Map map = methodCache.get(className);
if (map == null) {
return null;
}
return map.get(descriptor);
}
@Override
public MethodDependency getMethodImplementation(MethodReference methodRef) {
return implementationCache.computeIfAbsent(methodRef, m -> {
MethodReader resolved = agentClassSource.resolveImplementation(m);
return resolved != null ? getMethod(resolved.getReference()) : null;
});
}
private MethodHolder getMethodHolder(String className, MethodDescriptor descriptor) {
return methodReaderCache
.computeIfAbsent(className, k -> new HashMap<>(100, 0.5f))
.computeIfAbsent(descriptor, k -> Optional.ofNullable(
classSource.resolveMutableImplementation(className, k)))
.orElse(null);
}
private MethodDependency getMethodDependency(String className, MethodDescriptor descriptor) {
Map map = methodCache.computeIfAbsent(className, k -> new HashMap<>());
MethodDependency result = map.get(descriptor);
if (result == null) {
MethodHolder method = getMethodHolder(className, descriptor);
if (method != null && !(method.getDescriptor().equals(descriptor)
&& method.getOwnerName().equals(className))) {
result = getMethodDependency(method.getOwnerName(), method.getDescriptor());
} else {
MethodReference reference = method != null
? method.getReference()
: new MethodReference(className, descriptor);
result = createMethodDep(reference, method);
}
map.put(descriptor, result);
}
return result;
}
private void processQueue() {
if (interrupted) {
return;
}
while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) {
while (true) {
processNodeToNodeTransitionQueue();
if (tasks.isEmpty()) {
break;
}
while (!tasks.isEmpty()) {
tasks.remove().run();
}
if (interruptor != null && !interruptor.shouldContinue()) {
interrupted = true;
return;
}
}
propagationDepth = PROPAGATION_STACK_THRESHOLD;
while (!deferredTasks.isEmpty()) {
deferredTasks.remove().run();
}
propagationDepth = 0;
}
}
private void processNodeToNodeTransitionQueue() {
while (!pendingTransitions.isEmpty()) {
Transition transition = pendingTransitions.remove();
IntSet pendingTypes = transition.pendingTypes;
transition.pendingTypes = null;
if (pendingTypes.size() == 1) {
DependencyType type = types.get(pendingTypes.iterator().next().value);
transition.consume(type);
} else {
DependencyType[] typesToPropagate = new DependencyType[pendingTypes.size()];
int index = 0;
for (IntCursor cursor : pendingTypes) {
typesToPropagate[index++] = types.get(cursor.value);
}
transition.consume(typesToPropagate);
}
}
}
public void processDependencies() {
interrupted = false;
processQueue();
if (!interrupted) {
completing = true;
lock();
for (DependencyListener listener : listeners) {
listener.completing(agent);
}
}
for (DependencyListener listener : listeners) {
listener.complete();
}
if (dependencyReport) {
reportDependencies();
}
}
private void reportDependencies() {
List report = new ArrayList<>();
int domainCount = 0;
for (DependencyNode node : allNodes) {
String tag = node.tag + "";
if (node.typeSet != null && node.typeSet.origin == node) {
++domainCount;
tag += "{*}";
}
report.add(new ReportEntry(tag, node.getTypes().length));
}
report.sort(Comparator.comparingInt(n -> -n.count));
for (ReportEntry entry : report) {
System.out.println(entry.title + ": " + entry.count);
}
System.out.println("Total nodes: " + allNodes.size());
System.out.println("Total domains: " + domainCount);
}
public void cleanup(ClassSourcePacker classSourcePacker) {
for (DependencyNode node : allNodes) {
node.followers = null;
node.transitions = null;
node.transitionList = null;
node.method = null;
}
for (DependencyNode node : allNodes) {
if (node.typeSet != null) {
node.typeSet.cleanup();
}
}
for (Map, MethodDependency> map : methodCache.values()) {
for (MethodDependency methodDependency : map.values()) {
methodDependency.locationListeners = null;
methodDependency.locations = null;
methodDependency.cleanup();
}
}
for (FieldReference fieldRef : fieldCache.getCachedPreimages()) {
FieldDependency field = fieldCache.getKnown(fieldRef);
if (field != null) {
field.locationListeners = null;
field.locations = null;
field.cleanup();
}
}
for (String className : classCache.getCachedPreimages()) {
ClassDependency cls = classCache.getKnown(className);
cls.cleanup();
}
allNodes.clear();
classSource.cleanup();
agent.cleanup();
listeners.clear();
classSource.innerHierarchy = null;
agentClassSource = classSourcePacker.pack(classSource,
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
if (classSource != agentClassSource) {
classHierarchy = new ClassHierarchy(agentClassSource);
generatedClassNames.addAll(classSource.getGeneratedClassNames());
}
classSource = null;
methodReaderCache = null;
fieldReaderCache = null;
}
public void cleanupTypes() {
for (MethodReference reachableMethod : getReachableMethods()) {
MethodDependency dependency = getMethod(reachableMethod);
for (int i = dependency.getParameterCount() + 1; i < dependency.getVariableCount(); ++i) {
dependency.variableNodes[i] = null;
}
}
}
static class ReportEntry {
String title;
int count;
ReportEntry(String title, int count) {
this.title = title;
this.count = count;
}
}
private void lock() {
for (MethodReference method : getReachableMethods()) {
lock(getMethod(method), true);
}
for (FieldReference field : getReachableFields()) {
lock(getField(field));
}
}
private void lock(MethodDependency dep, boolean lock) {
for (DependencyNode node : dep.variableNodes) {
if (node != null) {
node.locked = lock;
}
}
if (dep.resultNode != null) {
dep.resultNode.locked = lock;
}
if (dep.thrown != null) {
dep.thrown.locked = lock;
}
}
private void lock(FieldDependency dep) {
dep.value.locked = true;
}
public T getService(Class type) {
return services.getService(type);
}
public Diagnostics getDiagnostics() {
return diagnostics;
}
@Override
public CallGraph getCallGraph() {
return callGraph;
}
public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) {
bootstrapMethodSubstitutors.put(method, substitutor);
}
public void addDependencyPlugin(MethodReference method, DependencyPlugin dependencyPlugin) {
dependencyPlugins.put(method, dependencyPlugin);
}
public IncrementalDependencyProvider getIncrementalDependencies() {
return incrementalCache;
}
DependencyTypeFilter getSuperClassFilter(String superClass) {
DependencyTypeFilter result = superClassFilters.get(superClass);
if (result == null) {
if (superClass.startsWith("[")) {
char second = superClass.charAt(1);
if (second == '[') {
result = new SuperArrayFilter(this, getSuperClassFilter(superClass.substring(1)));
} else if (second == 'L') {
ValueType.Object itemType = (ValueType.Object) ValueType.parse(superClass.substring(1));
result = new SuperArrayFilter(this, getSuperClassFilter(itemType.getClassName()));
} else {
result = new ExactTypeFilter(getType(superClass));
}
} else {
if (superClass.equals("java.lang.Object")) {
result = t -> true;
} else {
result = new SuperClassFilter(this, getType(superClass));
}
}
superClassFilters.put(superClass, result);
}
return result;
}
private void processInvokeDynamic(MethodDependency methodDep) {
if (methodDep.method == null) {
return;
}
Program program = methodDep.method.getProgram();
if (program == null) {
return;
}
ProgramEmitter pe = ProgramEmitter.create(program, classHierarchy);
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
if (!(insn instanceof InvokeDynamicInstruction)) {
continue;
}
block = insn.getBasicBlock();
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
BootstrapMethodSubstitutor substitutor = bootstrapMethodSubstitutors.get(bootstrapMethod);
if (substitutor == null) {
NullConstantInstruction nullInsn = new NullConstantInstruction();
nullInsn.setReceiver(indy.getReceiver());
nullInsn.setLocation(indy.getLocation());
insn.replace(nullInsn);
CallLocation location = new CallLocation(methodDep.getReference(), insn.getLocation());
diagnostics.error(location, "Substitutor for bootstrap method {{m0}} was not found",
bootstrapMethod);
continue;
}
BasicBlock splitBlock = splitter.split(block, insn);
pe.enter(block);
pe.setCurrentLocation(indy.getLocation());
insn.delete();
List arguments = new ArrayList<>();
for (int k = 0; k < indy.getArguments().size(); ++k) {
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
}
DynamicCallSite callSite = new DynamicCallSite(
methodDep.getReference(), indy.getMethod(),
indy.getInstance() != null ? pe.var(indy.getInstance(),
ValueType.object(methodDep.getMethod().getOwnerName())) : null,
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
agent);
ValueEmitter result = substitutor.substitute(callSite, pe);
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()
&& indy.getReceiver() != null) {
AssignInstruction assign = new AssignInstruction();
assign.setAssignee(result.getVariable());
assign.setReceiver(indy.getReceiver());
pe.addInstruction(assign);
}
pe.jump(splitBlock);
}
}
splitter.fixProgram();
}
static class IncrementalCache implements IncrementalDependencyProvider, IncrementalDependencyRegistration {
private final String[] emptyArray = new String[0];
private Map classes = new HashMap<>();
private Map methods = new HashMap<>();
@Override
public boolean isNoCache(String className) {
IncrementalItem item = classes.get(className);
return item != null && item.noCache;
}
@Override
public boolean isNoCache(MethodReference method) {
IncrementalItem item = methods.get(method);
return item != null && item.noCache;
}
@Override
public String[] getDependencies(String className) {
IncrementalItem item = classes.get(className);
return item != null && item.dependencies != null ? item.dependencies.toArray(new String[0]) : emptyArray;
}
@Override
public String[] getDependencies(MethodReference method) {
IncrementalItem item = methods.get(method);
return item != null && item.dependencies != null ? item.dependencies.toArray(new String[0]) : emptyArray;
}
@Override
public void setNoCache(String className) {
classes.computeIfAbsent(className, k -> new IncrementalItem()).noCache = true;
}
@Override
public void setNoCache(MethodReference method) {
methods.computeIfAbsent(method, k -> new IncrementalItem()).noCache = true;
}
@Override
public void addDependencies(String className, String... dependencies) {
IncrementalItem item = classes.computeIfAbsent(className, k -> new IncrementalItem());
if (item.dependencies == null) {
item.dependencies = new LinkedHashSet<>();
}
item.dependencies.addAll(Arrays.asList(dependencies));
}
@Override
public void addDependencies(MethodReference method, String... dependencies) {
IncrementalItem item = this.methods.computeIfAbsent(method, k -> new IncrementalItem());
if (item.dependencies == null) {
item.dependencies = new LinkedHashSet<>();
}
item.dependencies.addAll(Arrays.asList(dependencies));
}
}
static class IncrementalItem {
boolean noCache;
Set dependencies;
}
abstract boolean domainOptimizationEnabled();
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy