mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Improvements in reflection:
1. During dependency analysis, propagate class literals from Class.forName return node 2. Use original class source to generate reflection metadata 3. Link classes when they appear in signature of reflectable methods 4. Turn Class.forName(string_literal) into class literal.
This commit is contained in:
parent
f49ec16021
commit
ff7232ac3e
|
@ -26,15 +26,20 @@ import org.teavm.model.Instruction;
|
|||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
|
||||
public class ClassForNameTransformer implements ClassHolderTransformer {
|
||||
private static final MethodReference getNameMethod = new MethodReference(Class.class, "getName", String.class);
|
||||
private static final MethodReference forNameMethod = new MethodReference(Class.class, "forName", String.class,
|
||||
boolean.class, ClassLoader.class, Class.class);
|
||||
private static final MethodReference forNameShortMethod = new MethodReference(Class.class, "forName",
|
||||
String.class, Class.class);
|
||||
private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class);
|
||||
|
||||
@Override
|
||||
|
@ -42,17 +47,18 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
for (MethodHolder method : cls.getMethods()) {
|
||||
Program program = method.getProgram();
|
||||
if (program != null) {
|
||||
transformProgram(program);
|
||||
transformProgram(program, innerSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void transformProgram(Program program) {
|
||||
private void transformProgram(Program program, ClassReaderSource classSource) {
|
||||
DisjointSet varSet = new DisjointSet();
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
varSet.create();
|
||||
}
|
||||
int[] nameIndexes = new int[program.variableCount()];
|
||||
String[] constants = new String[program.variableCount()];
|
||||
Arrays.fill(nameIndexes, -1);
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
|
@ -64,6 +70,9 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
nameIndexes[invoke.getReceiver().getIndex()] = invoke.getInstance().getIndex();
|
||||
}
|
||||
}
|
||||
} else if (instruction instanceof StringConstantInstruction) {
|
||||
StringConstantInstruction stringConstant = (StringConstantInstruction) instruction;
|
||||
constants[stringConstant.getReceiver().getIndex()] = stringConstant.getConstant();
|
||||
} else if (instruction instanceof AssignInstruction) {
|
||||
AssignInstruction assign = (AssignInstruction) instruction;
|
||||
varSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex());
|
||||
|
@ -74,6 +83,7 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
nameIndexes = Arrays.copyOf(nameIndexes, varSet.size());
|
||||
int[] nameRepresentatives = new int[nameIndexes.length];
|
||||
Arrays.fill(nameRepresentatives, -1);
|
||||
String[] constantsByClasses = new String[varSet.size()];
|
||||
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
int varClass = varSet.find(i);
|
||||
|
@ -83,6 +93,8 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
if (nameIndexes[i] >= 0) {
|
||||
nameIndexes[varClass] = varSet.find(nameIndexes[i]);
|
||||
}
|
||||
|
||||
constantsByClasses[varClass] = constants[i];
|
||||
}
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
|
@ -92,18 +104,31 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
}
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
|
||||
if (!invoke.getMethod().equals(forNameMethod)) {
|
||||
if (!invoke.getMethod().equals(forNameMethod) && !invoke.getMethod().equals(forNameShortMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable representative;
|
||||
|
||||
int classNameIndex = invoke.getArguments().get(0).getIndex();
|
||||
int nameIndex = nameIndexes[classNameIndex];
|
||||
if (nameIndex < 0) {
|
||||
String constant = constantsByClasses[invoke.getArguments().get(0).getIndex()];
|
||||
if (nameIndex >= 0) {
|
||||
representative = program.variableAt(nameRepresentatives[nameIndex]);
|
||||
} else if (constant != null) {
|
||||
if (classSource.get(constant) == null) {
|
||||
continue;
|
||||
}
|
||||
ClassConstantInstruction classConstant = new ClassConstantInstruction();
|
||||
classConstant.setConstant(ValueType.object(constant));
|
||||
classConstant.setReceiver(program.createVariable());
|
||||
classConstant.setLocation(invoke.getLocation());
|
||||
invoke.insertPrevious(classConstant);
|
||||
representative = classConstant.getReceiver();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable representative = program.variableAt(nameRepresentatives[nameIndex]);
|
||||
|
||||
InvokeInstruction initInvoke = new InvokeInstruction();
|
||||
initInvoke.setLocation(invoke.getLocation());
|
||||
initInvoke.setType(InvocationType.SPECIAL);
|
||||
|
|
|
@ -54,6 +54,9 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
Constructor[].class);
|
||||
private MethodReference getMethods = new MethodReference(Class.class, "getDeclaredMethods",
|
||||
Method[].class);
|
||||
private MethodReference forName = new MethodReference(Class.class, "forName", String.class, Boolean.class,
|
||||
ClassLoader.class, Class.class);
|
||||
private MethodReference forNameShort = new MethodReference(Class.class, "forName", String.class, Class.class);
|
||||
private boolean fieldGetHandled;
|
||||
private boolean fieldSetHandled;
|
||||
private boolean newInstanceHandled;
|
||||
|
@ -62,11 +65,17 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
private Map<String, Set<MethodDescriptor>> accessibleMethodCache = new LinkedHashMap<>();
|
||||
private Set<String> classesWithReflectableFields = new LinkedHashSet<>();
|
||||
private Set<String> classesWithReflectableMethods = new LinkedHashSet<>();
|
||||
private DependencyNode allClasses;
|
||||
|
||||
public ReflectionDependencyListener(List<ReflectionSupplier> reflectionSuppliers) {
|
||||
this.reflectionSuppliers = reflectionSuppliers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
allClasses = agent.createNode();
|
||||
}
|
||||
|
||||
public Set<String> getClassesWithReflectableFields() {
|
||||
return classesWithReflectableFields;
|
||||
}
|
||||
|
@ -83,6 +92,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
return accessibleMethodCache.get(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
||||
allClasses.propagate(agent.getType(className));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (method.getReference().equals(fieldGet)) {
|
||||
|
@ -97,14 +111,29 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
method.getVariable(0).getClassValueNode().addConsumer(type -> {
|
||||
if (!type.getName().startsWith("[")) {
|
||||
classesWithReflectableFields.add(type.getName());
|
||||
|
||||
ClassReader cls = agent.getClassSource().get(type.getName());
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
linkType(agent, field.getType());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (method.getReference().equals(getConstructors) || method.getReference().equals(getMethods)) {
|
||||
method.getVariable(0).getClassValueNode().addConsumer(type -> {
|
||||
if (!type.getName().startsWith("[")) {
|
||||
classesWithReflectableMethods.add(type.getName());
|
||||
|
||||
ClassReader cls = agent.getClassSource().get(type.getName());
|
||||
for (MethodReader reflectableMethod : cls.getMethods()) {
|
||||
linkType(agent, reflectableMethod.getResultType());
|
||||
for (ValueType param : reflectableMethod.getParameterTypes()) {
|
||||
linkType(agent, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (method.getReference().equals(forName) || method.getReference().equals(forNameShort)) {
|
||||
allClasses.connect(method.getResult().getClassValueNode());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,6 +242,14 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
});
|
||||
}
|
||||
|
||||
private void linkType(DependencyAgent agent, ValueType type) {
|
||||
while (type instanceof ValueType.Array) {
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
}
|
||||
if (type instanceof ValueType.Object) {
|
||||
agent.linkClass(((ValueType.Object) type).getClassName(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) {
|
||||
if (member.hasModifier(ElementModifier.STATIC)) {
|
||||
|
|
|
@ -159,7 +159,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
|
||||
Set<MethodDescriptor> accessibleMethods = reflection.getAccessibleMethods(className);
|
||||
|
||||
ClassReader cls = context.getClassSource().get(className);
|
||||
ClassReader cls = context.getInitialClassSource().get(className);
|
||||
if (cls == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -250,7 +250,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
if (debugEmitterToUse == null) {
|
||||
debugEmitterToUse = new DummyDebugInformationEmitter();
|
||||
}
|
||||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, classes,
|
||||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
||||
controller.getUnprocessedClassSource(), classes,
|
||||
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming);
|
||||
renderingContext.setMinifying(minifying);
|
||||
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.teavm.debugging.information.DebugInformationEmitter;
|
|||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
|
@ -859,6 +860,11 @@ public class Renderer implements RenderingManager {
|
|||
return classSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReaderSource getInitialClassSource() {
|
||||
return context.getInitialClassSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.teavm.debugging.information.DebugInformationEmitter;
|
|||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -41,6 +42,7 @@ import org.teavm.model.ValueType;
|
|||
|
||||
public class RenderingContext {
|
||||
private final DebugInformationEmitter debugEmitter;
|
||||
private ClassReaderSource initialClassSource;
|
||||
private ListableClassReaderSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private ServiceRepository services;
|
||||
|
@ -53,10 +55,12 @@ public class RenderingContext {
|
|||
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
|
||||
private boolean minifying;
|
||||
|
||||
public RenderingContext(DebugInformationEmitter debugEmitter, ListableClassReaderSource classSource,
|
||||
public RenderingContext(DebugInformationEmitter debugEmitter,
|
||||
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
||||
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
||||
NamingStrategy naming) {
|
||||
this.debugEmitter = debugEmitter;
|
||||
this.initialClassSource = initialClassSource;
|
||||
this.classSource = classSource;
|
||||
this.classLoader = classLoader;
|
||||
this.services = services;
|
||||
|
@ -64,6 +68,10 @@ public class RenderingContext {
|
|||
this.naming = naming;
|
||||
}
|
||||
|
||||
public ClassReaderSource getInitialClassSource() {
|
||||
return initialClassSource;
|
||||
}
|
||||
|
||||
public ListableClassReaderSource getClassSource() {
|
||||
return classSource;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.javascript.spi;
|
|||
import java.util.Properties;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -25,6 +26,8 @@ import org.teavm.model.ValueType;
|
|||
public interface GeneratorContext extends ServiceRepository {
|
||||
String getParameterName(int index);
|
||||
|
||||
ClassReaderSource getInitialClassSource();
|
||||
|
||||
ListableClassReaderSource getClassSource();
|
||||
|
||||
ClassLoader getClassLoader();
|
||||
|
|
|
@ -41,6 +41,7 @@ public class ClassLookupDependencySupport extends AbstractDependencyListener {
|
|||
if (cls == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MethodReader initMethod = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||
if (initMethod != null) {
|
||||
agent.linkMethod(initMethod.getReference(), location).use();
|
||||
|
|
Loading…
Reference in New Issue
Block a user