Skip to content

Commit

Permalink
Refactor mechanism that resolves methods and fields parsed from bytec…
Browse files Browse the repository at this point in the history
…ode and reports errors about missing items
  • Loading branch information
konsoletyper committed Nov 20, 2023
1 parent 89661e5 commit 3282ae3
Show file tree
Hide file tree
Showing 16 changed files with 611 additions and 658 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 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.classlib.java.lang;

public class TIllegalAccessError extends TIncompatibleClassChangeError {
public TIllegalAccessError() {
}

public TIllegalAccessError(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,36 +251,12 @@ public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
dep.use();

dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"));
dependencyAnalyzer.linkMethod(new MethodReference(String.class, "hashCode", int.class))
.propagate(0, "java.lang.String")
.use();
dependencyAnalyzer.linkMethod(new MethodReference(String.class, "equals", Object.class, boolean.class))
.propagate(0, "java.lang.String")
.propagate(1, "java.lang.String")
.use();

dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class));
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
NoClassDefFoundError.class, "<init>", String.class, void.class));

dep = dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "toString", String.class));
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.Object"));
dep.use();

exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
exceptionCons.getVariable(1).propagate(stringType);
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchFieldError.class, "<init>",
String.class, void.class));
exceptionCons.use();
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchFieldError.class.getName()));
exceptionCons.getVariable(1).propagate(stringType);
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchMethodError.class, "<init>",
String.class, void.class));
exceptionCons.use();
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName()));
exceptionCons.getVariable(1).propagate(stringType);

exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
var exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
RuntimeException.class, "<init>", String.class, void.class));
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
exceptionCons.getVariable(1).propagate(stringType);
Expand Down
18 changes: 8 additions & 10 deletions core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
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;
Expand All @@ -51,7 +50,6 @@
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;
Expand Down Expand Up @@ -87,7 +85,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
private ClassLoader classLoader;
private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f);
private Map<MethodReference, MethodDependency> implementationCache = new HashMap<>();
private Function<FieldReference, FieldHolder> fieldReaderCache;
private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>();
private Set<MethodReference> reachedMethods = new LinkedHashSet<>();
private Set<MethodReference> readonlyReachedMethods = Collections.unmodifiableSet(reachedMethods);
Expand Down Expand Up @@ -118,18 +115,17 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
DependencyType classType;

DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics, ReferenceCache referenceCache) {
Diagnostics diagnostics, ReferenceCache referenceCache, String[] platformTags) {
this.unprocessedClassSource = classSource;
this.diagnostics = diagnostics;
this.referenceCache = referenceCache;
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache);
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache, platformTags);
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);
var field = this.classSource.getReferenceResolver().resolve(preimage);
if (field != null && !field.getReference().equals(preimage)) {
return fieldCache.apply(field.getReference());
}
Expand Down Expand Up @@ -274,6 +270,7 @@ public void submitMethod(MethodReference methodRef, Program program) {
lock(dep, false);
deferredTasks.add(() -> {
processInvokeDynamic(dep);
classSource.getReferenceResolver().use(dep.method.getReference(), diagnostics);
processMethod(dep);
dep.used = true;
});
Expand Down Expand Up @@ -499,8 +496,10 @@ private MethodDependency createMethodDep(MethodReference methodRef, MethodHolder
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);

void scheduleMethodAnalysis(MethodDependency dep) {
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
deferredTasks.add(() -> {
processInvokeDynamic(dep);
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
processMethod(dep);
});
}
Expand Down Expand Up @@ -762,20 +761,19 @@ public void cleanup(ClassSourcePacker classSourcePacker) {
}

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.innerHierarchy = null;
classSource.dispose();
classSource = null;
methodReaderCache = null;
fieldReaderCache = null;
}

public void cleanupTypes() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@

public interface DependencyAnalyzerFactory {
DependencyAnalyzer create(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics, ReferenceCache referenceCache);
Diagnostics diagnostics, ReferenceCache referenceCache, String[] platformTags);
}
33 changes: 27 additions & 6 deletions core/src/main/java/org/teavm/dependency/DependencyClassSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodHolder;
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
import org.teavm.model.transformation.ClassInitInsertion;
import org.teavm.model.util.ModelUtils;

class DependencyClassSource implements ClassHolderSource {
Expand All @@ -44,13 +45,21 @@ class DependencyClassSource implements ClassHolderSource {
boolean obfuscated;
boolean strict;
Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
private ReferenceResolver referenceResolver;
private ClassInitInsertion classInitInsertion;

DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
IncrementalDependencyRegistration dependencyRegistration) {
IncrementalDependencyRegistration dependencyRegistration, String[] platformTags) {
this.innerSource = innerSource;
this.diagnostics = diagnostics;
innerHierarchy = new ClassHierarchy(innerSource);
this.dependencyRegistration = dependencyRegistration;
referenceResolver = new ReferenceResolver(this, platformTags);
classInitInsertion = new ClassInitInsertion(this);
}

public ReferenceResolver getReferenceResolver() {
return referenceResolver;
}

@Override
Expand All @@ -75,10 +84,22 @@ public void submit(ClassHolder cls) {
}

private ClassHolder findAndTransformClass(String name) {
ClassHolder cls = findClass(name);
if (cls != null && !transformers.isEmpty()) {
for (ClassHolderTransformer transformer : transformers) {
transformer.transformClass(cls, transformContext);
var cls = findClass(name);
if (cls != null) {
if (!transformers.isEmpty()) {
for (var transformer : transformers) {
transformer.transformClass(cls, transformContext);
}
}
for (var method : cls.getMethods()) {
if (method.getProgram() != null) {
var program = method.getProgram();
method.setProgramSupplier(m -> {
referenceResolver.resolve(m, program);
classInitInsertion.apply(m, program);
return program;
});
}
}
}
return cls;
Expand Down Expand Up @@ -108,7 +129,7 @@ public void addTransformer(ClassHolderTransformer transformer) {
transformers.add(transformer);
}

public void cleanup() {
public void dispose() {
transformers.clear();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
private Map<String, DependencyNode> subtypeNodes = new HashMap<>();

public FastDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache) {
super(classSource, classLoader, services, diagnostics, referenceCache);
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache,
String[] platformTags) {
super(classSource, classLoader, services, diagnostics, referenceCache, platformTags);

instancesNode = new DependencyNode(this, null);
classesNode = new DependencyNode(this, null);
Expand Down
72 changes: 0 additions & 72 deletions core/src/main/java/org/teavm/dependency/Linker.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,18 @@
*/
package org.teavm.dependency;

import org.teavm.model.AccessLevel;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.transformation.ClassInitInsertion;

public class Linker {
private DependencyInfo dependency;
private ClassInitInsertion classInitInsertion;

public Linker(DependencyInfo dependency) {
this.dependency = dependency;
classInitInsertion = new ClassInitInsertion(dependency);
}

public void link(ClassHolder cls) {
Expand All @@ -54,8 +41,6 @@ public void link(ClassHolder cls) {
method.getModifiers().remove(ElementModifier.NATIVE);
method.setProgram(null);
}
} else if (method.getProgram() != null) {
link(method, method.getProgram());
}
}
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
Expand All @@ -64,63 +49,6 @@ public void link(ClassHolder cls) {
cls.removeField(field);
}
}
}

public void link(MethodReader method, Program program) {
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn;
MethodReference calledRef = invoke.getMethod();
if (invoke.getType() == InvocationType.SPECIAL) {
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef);
if (linkedMethod != null) {
invoke.setMethod(linkedMethod.getReference());
}
} else if (invoke.getType() == InvocationType.VIRTUAL) {
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef);
if (linkedMethod == null || linkedMethod.isMissing()) {
continue;
}
calledRef = linkedMethod.getReference();
ClassReader cls = dependency.getClassSource().get(calledRef.getClassName());
boolean isFinal = false;
if (cls != null) {
if (cls.hasModifier(ElementModifier.FINAL)) {
isFinal = true;
} else {
MethodReader calledMethod = cls.getMethod(calledRef.getDescriptor());
if (calledMethod != null) {
if (calledMethod.hasModifier(ElementModifier.FINAL)
|| calledMethod.getLevel() == AccessLevel.PRIVATE) {
isFinal = true;
}
}
}
}
if (isFinal) {
invoke.setType(InvocationType.SPECIAL);
invoke.setMethod(calledRef);
}
}
} else if (insn instanceof GetFieldInstruction) {
GetFieldInstruction getField = (GetFieldInstruction) insn;
FieldDependencyInfo linkedField = dependency.getField(getField.getField());
if (linkedField != null) {
getField.setField(linkedField.getReference());
}

} else if (insn instanceof PutFieldInstruction) {
PutFieldInstruction putField = (PutFieldInstruction) insn;
FieldDependencyInfo linkedField = dependency.getField(putField.getField());
if (linkedField != null) {
putField.setField(linkedField.getReference());
}
}
}
}

classInitInsertion.apply(program, method);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ public boolean isCalled() {
return external;
}


void cleanup() {
if (method != null) {
present = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@

public class PreciseDependencyAnalyzer extends DependencyAnalyzer {
public PreciseDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache) {
super(classSource, classLoader, services, diagnostics, referenceCache);
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache,
String[] platformTags) {
super(classSource, classLoader, services, diagnostics, referenceCache, platformTags);
}

@Override
Expand Down
Loading

0 comments on commit 3282ae3

Please sign in to comment.