mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Refactor mechanism that resolves methods and fields parsed from bytecode and reports errors about missing items
This commit is contained in:
parent
89661e52fb
commit
3282ae3b59
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -251,36 +251,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"));
|
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 = dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "toString", String.class));
|
||||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.Object"));
|
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.Object"));
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
|
var exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||||
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(
|
|
||||||
RuntimeException.class, "<init>", String.class, void.class));
|
RuntimeException.class, "<init>", String.class, void.class));
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
||||||
exceptionCons.getVariable(1).propagate(stringType);
|
exceptionCons.getVariable(1).propagate(stringType);
|
||||||
|
|
|
@ -33,7 +33,6 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import org.teavm.cache.IncrementalDependencyProvider;
|
import org.teavm.cache.IncrementalDependencyProvider;
|
||||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
|
@ -51,7 +50,6 @@ import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
|
@ -87,7 +85,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f);
|
private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f);
|
||||||
private Map<MethodReference, MethodDependency> implementationCache = new HashMap<>();
|
private Map<MethodReference, MethodDependency> implementationCache = new HashMap<>();
|
||||||
private Function<FieldReference, FieldHolder> fieldReaderCache;
|
|
||||||
private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>();
|
private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>();
|
||||||
private Set<MethodReference> reachedMethods = new LinkedHashSet<>();
|
private Set<MethodReference> reachedMethods = new LinkedHashSet<>();
|
||||||
private Set<MethodReference> readonlyReachedMethods = Collections.unmodifiableSet(reachedMethods);
|
private Set<MethodReference> readonlyReachedMethods = Collections.unmodifiableSet(reachedMethods);
|
||||||
|
@ -118,18 +115,17 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
DependencyType classType;
|
DependencyType classType;
|
||||||
|
|
||||||
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||||
Diagnostics diagnostics, ReferenceCache referenceCache) {
|
Diagnostics diagnostics, ReferenceCache referenceCache, String[] platformTags) {
|
||||||
this.unprocessedClassSource = classSource;
|
this.unprocessedClassSource = classSource;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
this.referenceCache = referenceCache;
|
this.referenceCache = referenceCache;
|
||||||
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache);
|
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache, platformTags);
|
||||||
agentClassSource = this.classSource;
|
agentClassSource = this.classSource;
|
||||||
classHierarchy = new ClassHierarchy(this.classSource);
|
classHierarchy = new ClassHierarchy(this.classSource);
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.services = services;
|
this.services = services;
|
||||||
fieldReaderCache = new CachedFunction<>(preimage -> this.classSource.resolveMutable(preimage));
|
|
||||||
fieldCache = new CachedFunction<>(preimage -> {
|
fieldCache = new CachedFunction<>(preimage -> {
|
||||||
FieldReader field = fieldReaderCache.apply(preimage);
|
var field = this.classSource.getReferenceResolver().resolve(preimage);
|
||||||
if (field != null && !field.getReference().equals(preimage)) {
|
if (field != null && !field.getReference().equals(preimage)) {
|
||||||
return fieldCache.apply(field.getReference());
|
return fieldCache.apply(field.getReference());
|
||||||
}
|
}
|
||||||
|
@ -274,6 +270,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
lock(dep, false);
|
lock(dep, false);
|
||||||
deferredTasks.add(() -> {
|
deferredTasks.add(() -> {
|
||||||
processInvokeDynamic(dep);
|
processInvokeDynamic(dep);
|
||||||
|
classSource.getReferenceResolver().use(dep.method.getReference(), diagnostics);
|
||||||
processMethod(dep);
|
processMethod(dep);
|
||||||
dep.used = true;
|
dep.used = true;
|
||||||
});
|
});
|
||||||
|
@ -499,8 +496,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);
|
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);
|
||||||
|
|
||||||
void scheduleMethodAnalysis(MethodDependency dep) {
|
void scheduleMethodAnalysis(MethodDependency dep) {
|
||||||
|
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
|
||||||
deferredTasks.add(() -> {
|
deferredTasks.add(() -> {
|
||||||
processInvokeDynamic(dep);
|
processInvokeDynamic(dep);
|
||||||
|
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
|
||||||
processMethod(dep);
|
processMethod(dep);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -762,10 +761,8 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
allNodes.clear();
|
allNodes.clear();
|
||||||
classSource.cleanup();
|
|
||||||
agent.cleanup();
|
agent.cleanup();
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
classSource.innerHierarchy = null;
|
|
||||||
|
|
||||||
agentClassSource = classSourcePacker.pack(classSource,
|
agentClassSource = classSourcePacker.pack(classSource,
|
||||||
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
|
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
|
||||||
|
@ -773,9 +770,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
classHierarchy = new ClassHierarchy(agentClassSource);
|
classHierarchy = new ClassHierarchy(agentClassSource);
|
||||||
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
||||||
}
|
}
|
||||||
|
classSource.innerHierarchy = null;
|
||||||
|
classSource.dispose();
|
||||||
classSource = null;
|
classSource = null;
|
||||||
methodReaderCache = null;
|
methodReaderCache = null;
|
||||||
fieldReaderCache = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanupTypes() {
|
public void cleanupTypes() {
|
||||||
|
|
|
@ -22,5 +22,5 @@ import org.teavm.model.ReferenceCache;
|
||||||
|
|
||||||
public interface DependencyAnalyzerFactory {
|
public interface DependencyAnalyzerFactory {
|
||||||
DependencyAnalyzer create(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
DependencyAnalyzer create(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||||
Diagnostics diagnostics, ReferenceCache referenceCache);
|
Diagnostics diagnostics, ReferenceCache referenceCache, String[] platformTags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
import org.teavm.model.transformation.ClassInitInsertion;
|
||||||
import org.teavm.model.util.ModelUtils;
|
import org.teavm.model.util.ModelUtils;
|
||||||
|
|
||||||
class DependencyClassSource implements ClassHolderSource {
|
class DependencyClassSource implements ClassHolderSource {
|
||||||
|
@ -44,13 +45,21 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
boolean obfuscated;
|
boolean obfuscated;
|
||||||
boolean strict;
|
boolean strict;
|
||||||
Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
|
Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
|
||||||
|
private ReferenceResolver referenceResolver;
|
||||||
|
private ClassInitInsertion classInitInsertion;
|
||||||
|
|
||||||
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
|
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
|
||||||
IncrementalDependencyRegistration dependencyRegistration) {
|
IncrementalDependencyRegistration dependencyRegistration, String[] platformTags) {
|
||||||
this.innerSource = innerSource;
|
this.innerSource = innerSource;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
innerHierarchy = new ClassHierarchy(innerSource);
|
innerHierarchy = new ClassHierarchy(innerSource);
|
||||||
this.dependencyRegistration = dependencyRegistration;
|
this.dependencyRegistration = dependencyRegistration;
|
||||||
|
referenceResolver = new ReferenceResolver(this, platformTags);
|
||||||
|
classInitInsertion = new ClassInitInsertion(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReferenceResolver getReferenceResolver() {
|
||||||
|
return referenceResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,10 +84,22 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassHolder findAndTransformClass(String name) {
|
private ClassHolder findAndTransformClass(String name) {
|
||||||
ClassHolder cls = findClass(name);
|
var cls = findClass(name);
|
||||||
if (cls != null && !transformers.isEmpty()) {
|
if (cls != null) {
|
||||||
for (ClassHolderTransformer transformer : transformers) {
|
if (!transformers.isEmpty()) {
|
||||||
transformer.transformClass(cls, transformContext);
|
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;
|
return cls;
|
||||||
|
@ -108,7 +129,7 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
transformers.add(transformer);
|
transformers.add(transformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void dispose() {
|
||||||
transformers.clear();
|
transformers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,9 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
private Map<String, DependencyNode> subtypeNodes = new HashMap<>();
|
private Map<String, DependencyNode> subtypeNodes = new HashMap<>();
|
||||||
|
|
||||||
public FastDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
public FastDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
||||||
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache) {
|
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache,
|
||||||
super(classSource, classLoader, services, diagnostics, referenceCache);
|
String[] platformTags) {
|
||||||
|
super(classSource, classLoader, services, diagnostics, referenceCache, platformTags);
|
||||||
|
|
||||||
instancesNode = new DependencyNode(this, null);
|
instancesNode = new DependencyNode(this, null);
|
||||||
classesNode = new DependencyNode(this, null);
|
classesNode = new DependencyNode(this, null);
|
||||||
|
|
|
@ -15,31 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import org.teavm.model.AccessLevel;
|
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassReader;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
|
||||||
import org.teavm.model.MethodReference;
|
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 {
|
public class Linker {
|
||||||
private DependencyInfo dependency;
|
private DependencyInfo dependency;
|
||||||
private ClassInitInsertion classInitInsertion;
|
|
||||||
|
|
||||||
public Linker(DependencyInfo dependency) {
|
public Linker(DependencyInfo dependency) {
|
||||||
this.dependency = dependency;
|
this.dependency = dependency;
|
||||||
classInitInsertion = new ClassInitInsertion(dependency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void link(ClassHolder cls) {
|
public void link(ClassHolder cls) {
|
||||||
|
@ -54,8 +41,6 @@ public class Linker {
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
method.setProgram(null);
|
method.setProgram(null);
|
||||||
}
|
}
|
||||||
} else if (method.getProgram() != null) {
|
|
||||||
link(method, method.getProgram());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
|
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
|
||||||
|
@ -64,63 +49,6 @@ public class Linker {
|
||||||
cls.removeField(field);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,6 @@ public class MethodDependency implements MethodDependencyInfo {
|
||||||
return external;
|
return external;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
present = true;
|
present = true;
|
||||||
|
|
|
@ -25,8 +25,9 @@ import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class PreciseDependencyAnalyzer extends DependencyAnalyzer {
|
public class PreciseDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
public PreciseDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
public PreciseDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
||||||
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache) {
|
ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache,
|
||||||
super(classSource, classLoader, services, diagnostics, referenceCache);
|
String[] platformTags) {
|
||||||
|
super(classSource, classLoader, services, diagnostics, referenceCache, platformTags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
408
core/src/main/java/org/teavm/dependency/ReferenceResolver.java
Normal file
408
core/src/main/java/org/teavm/dependency/ReferenceResolver.java
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.interop.SupportedOn;
|
||||||
|
import org.teavm.interop.UnsupportedOn;
|
||||||
|
import org.teavm.model.AccessLevel;
|
||||||
|
import org.teavm.model.AnnotationContainerReader;
|
||||||
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.AnnotationValue;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReader;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
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.TextLocation;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.InstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
|
public class ReferenceResolver {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private MethodReference currentMethod;
|
||||||
|
private Program program;
|
||||||
|
private boolean modified;
|
||||||
|
private List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
private Map<FieldReference, FieldWrapper> fieldCache = new HashMap<>();
|
||||||
|
private Map<String, Map<MethodDescriptor, Optional<MethodReader>>> methodCache = new HashMap<>(1000, 0.5f);
|
||||||
|
private Set<String> platformTags = new HashSet<>();
|
||||||
|
private UnreachableBasicBlockEliminator unreachableBlockEliminator;
|
||||||
|
private Map<MethodReference, List<Consumer<Diagnostics>>> pendingErrors = new HashMap<>();
|
||||||
|
private boolean shouldStop;
|
||||||
|
|
||||||
|
public ReferenceResolver(ClassReaderSource classSource, String[] platformTags) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
this.platformTags.addAll(List.of(platformTags));
|
||||||
|
unreachableBlockEliminator = new UnreachableBasicBlockEliminator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void use(MethodReference method, Diagnostics diagnostics) {
|
||||||
|
var errors = pendingErrors.remove(method);
|
||||||
|
if (errors != null) {
|
||||||
|
for (var error : errors) {
|
||||||
|
error.accept(diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Program resolve(MethodHolder method, Program program) {
|
||||||
|
this.currentMethod = method.getReference();
|
||||||
|
this.program = program;
|
||||||
|
for (var block : program.getBasicBlocks()) {
|
||||||
|
shouldStop = false;
|
||||||
|
for (var insn : block) {
|
||||||
|
insn.acceptVisitor(visitor);
|
||||||
|
if (shouldStop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modified) {
|
||||||
|
unreachableBlockEliminator.optimize(program);
|
||||||
|
modified = false;
|
||||||
|
}
|
||||||
|
this.program = null;
|
||||||
|
this.currentMethod = null;
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstructionVisitor visitor = new AbstractInstructionVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (!resolve(insn)) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
if (!resolve(insn)) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
if (!resolve(insn)) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastInstruction insn) {
|
||||||
|
if (!checkType(insn, insn.getTargetType())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IsInstanceInstruction insn) {
|
||||||
|
if (!checkType(insn, insn.getType())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InitClassInstruction insn) {
|
||||||
|
if (!checkClass(insn, insn.getClassName())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructInstruction insn) {
|
||||||
|
if (!checkClass(insn, insn.getType())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
|
if (!checkType(insn, insn.getItemType())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
|
if (!checkType(insn, insn.getItemType())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
if (!checkType(insn, insn.getConstant())) {
|
||||||
|
shouldStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private boolean resolve(InvokeInstruction instruction) {
|
||||||
|
var calledRef = instruction.getMethod();
|
||||||
|
|
||||||
|
if (!checkClass(instruction, calledRef.getClassName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instruction.getType() == InvocationType.SPECIAL) {
|
||||||
|
return resolveSpecial(instruction, calledRef);
|
||||||
|
} else {
|
||||||
|
return resolveVirtual(instruction, calledRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolve(GetFieldInstruction instruction) {
|
||||||
|
var resolvedField = resolve(instruction.getField());
|
||||||
|
if (resolvedField == null) {
|
||||||
|
reportError(instruction.getLocation(), "Field {{f0}} was not found", instruction.getField());
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoSuchFieldError.class.getName(),
|
||||||
|
"Field not found: " + instruction.getField());
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
instruction.setField(resolvedField.getReference());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolve(PutFieldInstruction instruction) {
|
||||||
|
var resolvedField = resolve(instruction.getField());
|
||||||
|
if (resolvedField == null) {
|
||||||
|
reportError(instruction.getLocation(), "Field {{f0}} was not found", instruction.getField());
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoSuchFieldError.class.getName(),
|
||||||
|
"Field not found: " + instruction.getField());
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
instruction.setField(resolvedField.getReference());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolveSpecial(InvokeInstruction instruction, MethodReference calledRef) {
|
||||||
|
var resolvedMethod = resolve(calledRef);
|
||||||
|
if (resolvedMethod == null) {
|
||||||
|
reportError(instruction.getLocation(), "Method {{m0}} was not found", calledRef);
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoSuchMethodError.class.getName(),
|
||||||
|
"Method not found: " + instruction.getMethod());
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checkMethod(instruction, resolvedMethod.getReference())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction.setMethod(resolvedMethod.getReference());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolveVirtual(InvokeInstruction instruction, MethodReference calledRef) {
|
||||||
|
var resolvedMethod = resolve(calledRef);
|
||||||
|
if (resolvedMethod == null) {
|
||||||
|
reportError(instruction.getLocation(), "Method {{m0}} was not found", calledRef);
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoSuchMethodError.class.getName(),
|
||||||
|
"Method not found: " + instruction.getMethod());
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkMethod(instruction, resolvedMethod.getReference())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isFinal = false;
|
||||||
|
if (resolvedMethod.hasModifier(ElementModifier.FINAL)
|
||||||
|
|| resolvedMethod.getLevel() == AccessLevel.PRIVATE) {
|
||||||
|
isFinal = true;
|
||||||
|
} else {
|
||||||
|
var cls = classSource.get(resolvedMethod.getOwnerName());
|
||||||
|
if (cls.hasModifier(ElementModifier.FINAL)) {
|
||||||
|
isFinal = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFinal) {
|
||||||
|
instruction.setType(InvocationType.SPECIAL);
|
||||||
|
instruction.setMethod(resolvedMethod.getReference());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldReader resolve(FieldReference field) {
|
||||||
|
return fieldCache.computeIfAbsent(field, f -> {
|
||||||
|
var result = classSource.resolve(f);
|
||||||
|
return new FieldWrapper(result);
|
||||||
|
}).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkType(Instruction instruction, ValueType type) {
|
||||||
|
while (type instanceof ValueType.Array) {
|
||||||
|
type = ((ValueType.Array) type).getItemType();
|
||||||
|
}
|
||||||
|
if (type instanceof ValueType.Object) {
|
||||||
|
return checkClass(instruction, ((ValueType.Object) type).getClassName());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkClass(Instruction instruction, String className) {
|
||||||
|
var cls = classSource.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
reportError(instruction.getLocation(), "Class {{c0}} was not found", className);
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoClassDefFoundError.class.getName(),
|
||||||
|
"Class not found: " + className);
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checkPlatformSupported(cls.getAnnotations())) {
|
||||||
|
reportError(instruction.getLocation(), "Class {{c0}} is not supported on current target", className);
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoClassDefFoundError.class.getName(),
|
||||||
|
"Class not found: " + className);
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkMethod(Instruction instruction, MethodReference methodRef) {
|
||||||
|
var cls = classSource.get(methodRef.getClassName());
|
||||||
|
if (cls != null) {
|
||||||
|
var methodReader = cls.getMethod(methodRef.getDescriptor());
|
||||||
|
if (methodReader != null && !checkPlatformSupported(methodReader.getAnnotations())) {
|
||||||
|
reportError(instruction.getLocation(), "Method {{m0}} is not supported on current target", methodRef);
|
||||||
|
emitExceptionThrow(instruction.getLocation(), NoSuchMethodError.class.getName(),
|
||||||
|
"Method not found: " + methodRef);
|
||||||
|
truncateBlock(instruction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkPlatformSupported(AnnotationContainerReader annotations) {
|
||||||
|
AnnotationReader supportedAnnot = annotations.get(SupportedOn.class.getName());
|
||||||
|
AnnotationReader unsupportedAnnot = annotations.get(UnsupportedOn.class.getName());
|
||||||
|
if (supportedAnnot != null) {
|
||||||
|
for (AnnotationValue value : supportedAnnot.getValue("value").getList()) {
|
||||||
|
if (platformTags.contains(value.getString())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (unsupportedAnnot != null) {
|
||||||
|
for (AnnotationValue value : unsupportedAnnot.getValue("value").getList()) {
|
||||||
|
if (platformTags.contains(value.getString())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodReader resolve(MethodReference method) {
|
||||||
|
return methodCache
|
||||||
|
.computeIfAbsent(method.getClassName(), k -> new HashMap<>(100, 0.5f))
|
||||||
|
.computeIfAbsent(method.getDescriptor(), k -> {
|
||||||
|
var methodReader = classSource.resolve(method);
|
||||||
|
return Optional.ofNullable(methodReader);
|
||||||
|
})
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void truncateBlock(Instruction instruction) {
|
||||||
|
modified = true;
|
||||||
|
ProgramUtils.truncateBlock(instruction);
|
||||||
|
instruction.insertNextAll(instructionsToAdd);
|
||||||
|
instructionsToAdd.clear();
|
||||||
|
instruction.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emitExceptionThrow(TextLocation location, String exceptionName, String text) {
|
||||||
|
var exceptionVar = program.createVariable();
|
||||||
|
var newExceptionInsn = new ConstructInstruction();
|
||||||
|
newExceptionInsn.setType(exceptionName);
|
||||||
|
newExceptionInsn.setReceiver(exceptionVar);
|
||||||
|
newExceptionInsn.setLocation(location);
|
||||||
|
instructionsToAdd.add(newExceptionInsn);
|
||||||
|
|
||||||
|
var constVar = program.createVariable();
|
||||||
|
var constInsn = new StringConstantInstruction();
|
||||||
|
constInsn.setConstant(text);
|
||||||
|
constInsn.setReceiver(constVar);
|
||||||
|
constInsn.setLocation(location);
|
||||||
|
instructionsToAdd.add(constInsn);
|
||||||
|
|
||||||
|
var initExceptionInsn = new InvokeInstruction();
|
||||||
|
initExceptionInsn.setInstance(exceptionVar);
|
||||||
|
initExceptionInsn.setMethod(new MethodReference(exceptionName, "<init>", ValueType.object("java.lang.String"),
|
||||||
|
ValueType.VOID));
|
||||||
|
initExceptionInsn.setType(InvocationType.SPECIAL);
|
||||||
|
initExceptionInsn.setArguments(constVar);
|
||||||
|
initExceptionInsn.setLocation(location);
|
||||||
|
instructionsToAdd.add(initExceptionInsn);
|
||||||
|
|
||||||
|
var raiseInsn = new RaiseInstruction();
|
||||||
|
raiseInsn.setException(exceptionVar);
|
||||||
|
raiseInsn.setLocation(location);
|
||||||
|
instructionsToAdd.add(raiseInsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportError(TextLocation location, String message, Object param) {
|
||||||
|
var method = currentMethod;
|
||||||
|
pendingErrors.computeIfAbsent(method, k -> new ArrayList<>()).add(diagnostics ->
|
||||||
|
diagnostics.error(new CallLocation(method, location), message, param));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldWrapper {
|
||||||
|
final FieldReader value;
|
||||||
|
|
||||||
|
FieldWrapper(FieldReader value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,9 @@ package org.teavm.model.transformation;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
@ -36,13 +36,13 @@ import org.teavm.model.util.DominatorWalkerContext;
|
||||||
|
|
||||||
public class ClassInitInsertion {
|
public class ClassInitInsertion {
|
||||||
private static final MethodDescriptor CLINIT = new MethodDescriptor("<clinit>", void.class);
|
private static final MethodDescriptor CLINIT = new MethodDescriptor("<clinit>", void.class);
|
||||||
private DependencyInfo dependencyInfo;
|
private ClassReaderSource classSource;
|
||||||
|
|
||||||
public ClassInitInsertion(DependencyInfo dependencyInfo) {
|
public ClassInitInsertion(ClassReaderSource classSource) {
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.classSource = classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(Program program, MethodReader method) {
|
public void apply(MethodReader method, Program program) {
|
||||||
if (program.basicBlockCount() == 0) {
|
if (program.basicBlockCount() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ public class ClassInitInsertion {
|
||||||
|
|
||||||
private void initializeClass(String className, Instruction instruction) {
|
private void initializeClass(String className, Instruction instruction) {
|
||||||
if (markClassAsInitialized(className)) {
|
if (markClassAsInitialized(className)) {
|
||||||
ClassReader cls = dependencyInfo.getClassSource().get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
if (cls == null || cls.getMethod(CLINIT) != null) {
|
if (cls == null || cls.getMethod(CLINIT) != null) {
|
||||||
InitClassInstruction initInsn = new InitClassInstruction();
|
InitClassInstruction initInsn = new InitClassInstruction();
|
||||||
initInsn.setClassName(className);
|
initInsn.setClassName(className);
|
||||||
|
|
|
@ -1,333 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 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.util;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.dependency.DependencyInfo;
|
|
||||||
import org.teavm.dependency.MethodDependencyInfo;
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.interop.SupportedOn;
|
|
||||||
import org.teavm.interop.UnsupportedOn;
|
|
||||||
import org.teavm.model.AnnotationContainerReader;
|
|
||||||
import org.teavm.model.AnnotationReader;
|
|
||||||
import org.teavm.model.AnnotationValue;
|
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.ClassHierarchy;
|
|
||||||
import org.teavm.model.ClassHolder;
|
|
||||||
import org.teavm.model.ClassReader;
|
|
||||||
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.TextLocation;
|
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
import org.teavm.model.Variable;
|
|
||||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
|
||||||
import org.teavm.model.instructions.CastInstruction;
|
|
||||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
|
||||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
|
||||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.GetFieldInstruction;
|
|
||||||
import org.teavm.model.instructions.InitClassInstruction;
|
|
||||||
import org.teavm.model.instructions.InstructionVisitor;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
|
||||||
import org.teavm.model.instructions.IsInstanceInstruction;
|
|
||||||
import org.teavm.model.instructions.PutFieldInstruction;
|
|
||||||
import org.teavm.model.instructions.RaiseInstruction;
|
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
|
||||||
|
|
||||||
public class MissingItemsProcessor {
|
|
||||||
private DependencyInfo dependencyInfo;
|
|
||||||
private ClassHierarchy hierarchy;
|
|
||||||
private Diagnostics diagnostics;
|
|
||||||
private List<Instruction> instructionsToAdd = new ArrayList<>();
|
|
||||||
private MethodReference methodRef;
|
|
||||||
private Program program;
|
|
||||||
private Collection<String> reachableClasses;
|
|
||||||
private Collection<MethodReference> reachableMethods;
|
|
||||||
private Collection<FieldReference> reachableFields;
|
|
||||||
private Set<String> platformTags = new HashSet<>();
|
|
||||||
|
|
||||||
public MissingItemsProcessor(DependencyInfo dependencyInfo, ClassHierarchy hierarchy, Diagnostics diagnostics,
|
|
||||||
String[] platformTags) {
|
|
||||||
this.dependencyInfo = dependencyInfo;
|
|
||||||
this.diagnostics = diagnostics;
|
|
||||||
this.hierarchy = hierarchy;
|
|
||||||
reachableClasses = dependencyInfo.getReachableClasses();
|
|
||||||
reachableMethods = dependencyInfo.getReachableMethods();
|
|
||||||
reachableFields = dependencyInfo.getReachableFields();
|
|
||||||
this.platformTags.addAll(Arrays.asList(platformTags));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processClass(ClassHolder cls) {
|
|
||||||
for (var method : cls.getMethods()) {
|
|
||||||
if (reachableMethods.contains(method.getReference()) && method.getProgram() != null) {
|
|
||||||
var methodDep = dependencyInfo.getMethod(method.getReference());
|
|
||||||
if (methodDep != null && methodDep.isUsed()) {
|
|
||||||
processMethod(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processMethod(MethodHolder method) {
|
|
||||||
processMethod(method.getReference(), method.getProgram());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processMethod(MethodReference method, Program program) {
|
|
||||||
this.methodRef = method;
|
|
||||||
this.program = program;
|
|
||||||
boolean wasModified = false;
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
instructionsToAdd.clear();
|
|
||||||
boolean missing = false;
|
|
||||||
for (var insn : block) {
|
|
||||||
insn.acceptVisitor(instructionProcessor);
|
|
||||||
if (!instructionsToAdd.isEmpty()) {
|
|
||||||
wasModified = true;
|
|
||||||
truncateBlock(insn);
|
|
||||||
missing = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!missing) {
|
|
||||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
|
||||||
checkClass(null, tryCatch.getExceptionType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wasModified) {
|
|
||||||
new UnreachableBasicBlockEliminator().optimize(program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void truncateBlock(Instruction instruction) {
|
|
||||||
ProgramUtils.truncateBlock(instruction);
|
|
||||||
instruction.insertNextAll(instructionsToAdd);
|
|
||||||
instruction.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void emitExceptionThrow(TextLocation location, String exceptionName, String text) {
|
|
||||||
Variable exceptionVar = program.createVariable();
|
|
||||||
ConstructInstruction newExceptionInsn = new ConstructInstruction();
|
|
||||||
newExceptionInsn.setType(exceptionName);
|
|
||||||
newExceptionInsn.setReceiver(exceptionVar);
|
|
||||||
newExceptionInsn.setLocation(location);
|
|
||||||
instructionsToAdd.add(newExceptionInsn);
|
|
||||||
|
|
||||||
Variable constVar = program.createVariable();
|
|
||||||
StringConstantInstruction constInsn = new StringConstantInstruction();
|
|
||||||
constInsn.setConstant(text);
|
|
||||||
constInsn.setReceiver(constVar);
|
|
||||||
constInsn.setLocation(location);
|
|
||||||
instructionsToAdd.add(constInsn);
|
|
||||||
|
|
||||||
InvokeInstruction initExceptionInsn = new InvokeInstruction();
|
|
||||||
initExceptionInsn.setInstance(exceptionVar);
|
|
||||||
initExceptionInsn.setMethod(new MethodReference(exceptionName, "<init>", ValueType.object("java.lang.String"),
|
|
||||||
ValueType.VOID));
|
|
||||||
initExceptionInsn.setType(InvocationType.SPECIAL);
|
|
||||||
initExceptionInsn.setArguments(constVar);
|
|
||||||
initExceptionInsn.setLocation(location);
|
|
||||||
instructionsToAdd.add(initExceptionInsn);
|
|
||||||
|
|
||||||
RaiseInstruction raiseInsn = new RaiseInstruction();
|
|
||||||
raiseInsn.setException(exceptionVar);
|
|
||||||
raiseInsn.setLocation(location);
|
|
||||||
instructionsToAdd.add(raiseInsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkClass(TextLocation location, String className) {
|
|
||||||
if (!reachableClasses.contains(className)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dependencyInfo.getClass(className).isMissing()) {
|
|
||||||
ClassReader cls = dependencyInfo.getClassSource().get(className);
|
|
||||||
if (cls != null && !checkPlatformSupported(cls.getAnnotations())) {
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Class {{c0}} is not supported on "
|
|
||||||
+ "current target", className);
|
|
||||||
emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Class {{c0}} was not found",
|
|
||||||
className);
|
|
||||||
emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkClass(TextLocation location, ValueType type) {
|
|
||||||
while (type instanceof ValueType.Array) {
|
|
||||||
type = ((ValueType.Array) type).getItemType();
|
|
||||||
}
|
|
||||||
if (type instanceof ValueType.Object) {
|
|
||||||
return checkClass(location, ((ValueType.Object) type).getClassName());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkMethod(TextLocation location, MethodReference method) {
|
|
||||||
if (!checkClass(location, method.getClassName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!reachableMethods.contains(method)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
MethodDependencyInfo methodDep = dependencyInfo.getMethod(method);
|
|
||||||
if (!methodDep.isUsed()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!methodDep.isMissing()) {
|
|
||||||
ClassReader cls = dependencyInfo.getClassSource().get(method.getClassName());
|
|
||||||
if (cls != null) {
|
|
||||||
MethodReader methodReader = cls.getMethod(method.getDescriptor());
|
|
||||||
if (methodReader != null && !checkPlatformSupported(methodReader.getAnnotations())) {
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} is not supported on "
|
|
||||||
+ "current target", method);
|
|
||||||
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} was not found",
|
|
||||||
method);
|
|
||||||
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkVirtualMethod(TextLocation location, MethodReference method) {
|
|
||||||
if (!checkClass(location, method.getClassName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hierarchy.resolve(method) != null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} was not found", method);
|
|
||||||
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkField(TextLocation location, FieldReference field) {
|
|
||||||
if (!checkClass(location, field.getClassName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!reachableFields.contains(field) || !dependencyInfo.getField(field).isMissing()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
diagnostics.error(new CallLocation(methodRef, location), "Field {{f0}} was not found",
|
|
||||||
field);
|
|
||||||
emitExceptionThrow(location, NoSuchFieldError.class.getName(), "Field not found: " + field);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkPlatformSupported(AnnotationContainerReader annotations) {
|
|
||||||
AnnotationReader supportedAnnot = annotations.get(SupportedOn.class.getName());
|
|
||||||
AnnotationReader unsupportedAnnot = annotations.get(UnsupportedOn.class.getName());
|
|
||||||
if (supportedAnnot != null) {
|
|
||||||
for (AnnotationValue value : supportedAnnot.getValue("value").getList()) {
|
|
||||||
if (platformTags.contains(value.getString())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (unsupportedAnnot != null) {
|
|
||||||
for (AnnotationValue value : unsupportedAnnot.getValue("value").getList()) {
|
|
||||||
if (platformTags.contains(value.getString())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstructionVisitor instructionProcessor = new AbstractInstructionVisitor() {
|
|
||||||
@Override
|
|
||||||
public void visit(InitClassInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(IsInstanceInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InvokeInstruction insn) {
|
|
||||||
if (insn.getType() != InvocationType.VIRTUAL) {
|
|
||||||
checkMethod(insn.getLocation(), insn.getMethod());
|
|
||||||
} else {
|
|
||||||
checkVirtualMethod(insn.getLocation(), insn.getMethod());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(PutFieldInstruction insn) {
|
|
||||||
checkField(insn.getLocation(), insn.getField());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(GetFieldInstruction insn) {
|
|
||||||
checkField(insn.getLocation(), insn.getField());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ConstructMultiArrayInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getItemType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ConstructInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ConstructArrayInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getItemType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(CastInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getTargetType());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ClassConstantInstruction insn) {
|
|
||||||
checkClass(insn.getLocation(), insn.getConstant());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -95,7 +95,6 @@ import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
||||||
import org.teavm.model.optimization.UnusedVariableElimination;
|
import org.teavm.model.optimization.UnusedVariableElimination;
|
||||||
import org.teavm.model.text.ListingBuilder;
|
import org.teavm.model.text.ListingBuilder;
|
||||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||||
import org.teavm.model.util.MissingItemsProcessor;
|
|
||||||
import org.teavm.model.util.ModelUtils;
|
import org.teavm.model.util.ModelUtils;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
import org.teavm.model.util.RegisterAllocator;
|
import org.teavm.model.util.RegisterAllocator;
|
||||||
|
@ -170,7 +169,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
classLoader = builder.classLoader;
|
classLoader = builder.classLoader;
|
||||||
classSourcePacker = builder.classSourcePacker;
|
classSourcePacker = builder.classSourcePacker;
|
||||||
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
|
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
|
||||||
this, diagnostics, builder.referenceCache);
|
this, diagnostics, builder.referenceCache, target.getPlatformTags());
|
||||||
dependencyAnalyzer.setObfuscated(builder.obfuscated);
|
dependencyAnalyzer.setObfuscated(builder.obfuscated);
|
||||||
dependencyAnalyzer.setStrict(builder.strict);
|
dependencyAnalyzer.setStrict(builder.strict);
|
||||||
progressListener = new TeaVMProgressListener() {
|
progressListener = new TeaVMProgressListener() {
|
||||||
|
@ -569,8 +568,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||||
Linker linker = new Linker(dependency);
|
Linker linker = new Linker(dependency);
|
||||||
var cutClasses = new MutableClassHolderSource();
|
var cutClasses = new MutableClassHolderSource();
|
||||||
var missingItemsProcessor = new MissingItemsProcessor(dependency,
|
|
||||||
dependency.getClassHierarchy(), diagnostics, target.getPlatformTags());
|
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return cutClasses;
|
return cutClasses;
|
||||||
}
|
}
|
||||||
|
@ -584,7 +581,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
if (clsReader != null) {
|
if (clsReader != null) {
|
||||||
ClassHolder cls = ModelUtils.copyClass(clsReader);
|
ClassHolder cls = ModelUtils.copyClass(clsReader);
|
||||||
cutClasses.putClassHolder(cls);
|
cutClasses.putClassHolder(cls);
|
||||||
missingItemsProcessor.processClass(cls);
|
|
||||||
linker.link(cls);
|
linker.link(cls);
|
||||||
}
|
}
|
||||||
reportCompileProgress(++compileProgressValue);
|
reportCompileProgress(++compileProgressValue);
|
||||||
|
@ -962,8 +958,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
|
|
||||||
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
||||||
private Linker linker = new Linker(dependencyAnalyzer);
|
private Linker linker = new Linker(dependencyAnalyzer);
|
||||||
private MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependencyAnalyzer,
|
|
||||||
dependencyAnalyzer.getClassHierarchy(), diagnostics, target.getPlatformTags());
|
|
||||||
private Map<String, ClassHolder> cache = new HashMap<>();
|
private Map<String, ClassHolder> cache = new HashMap<>();
|
||||||
private Set<String> classNames = Collections.unmodifiableSet(new HashSet<>(
|
private Set<String> classNames = Collections.unmodifiableSet(new HashSet<>(
|
||||||
dependencyAnalyzer.getReachableClasses().stream()
|
dependencyAnalyzer.getReachableClasses().stream()
|
||||||
|
@ -980,6 +974,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ClassHolder cls = ModelUtils.copyClass(classReader, false);
|
ClassHolder cls = ModelUtils.copyClass(classReader, false);
|
||||||
|
linker.link(cls);
|
||||||
|
|
||||||
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
|
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
|
||||||
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
|
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
|
||||||
|
@ -994,8 +989,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
: null;
|
: null;
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
program = ProgramUtils.copy(classReader.getMethod(method.getDescriptor()).getProgram());
|
program = ProgramUtils.copy(classReader.getMethod(method.getDescriptor()).getProgram());
|
||||||
missingItemsProcessor.processMethod(method.getReference(), program);
|
|
||||||
linker.link(method, program);
|
|
||||||
clinitInsertion.apply(method, program);
|
clinitInsertion.apply(method, program);
|
||||||
target.beforeInlining(program, method);
|
target.beforeInlining(program, method);
|
||||||
program = optimizeMethodCacheMiss(method, program);
|
program = optimizeMethodCacheMiss(method, program);
|
||||||
|
|
|
@ -1,182 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.platform.plugin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
|
||||||
import org.teavm.dependency.DependencyAgent;
|
|
||||||
import org.teavm.dependency.MethodDependency;
|
|
||||||
import org.teavm.interop.Async;
|
|
||||||
import org.teavm.interop.AsyncCallback;
|
|
||||||
import org.teavm.model.AnnotationReader;
|
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.ClassHolder;
|
|
||||||
import org.teavm.model.ElementModifier;
|
|
||||||
import org.teavm.model.FieldHolder;
|
|
||||||
import org.teavm.model.FieldReference;
|
|
||||||
import org.teavm.model.MethodHolder;
|
|
||||||
import org.teavm.model.MethodReader;
|
|
||||||
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.ExitInstruction;
|
|
||||||
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.runtime.Fiber;
|
|
||||||
|
|
||||||
public class AsyncLowLevelDependencyListener extends AbstractDependencyListener {
|
|
||||||
private Set<String> generatedClassNames = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
|
||||||
if (method.getMethod() != null && method.getMethod().getAnnotations().get(Async.class.getName()) != null) {
|
|
||||||
ClassHolder cls = generateCall(method.getMethod());
|
|
||||||
if (cls != null) {
|
|
||||||
agent.submitClass(cls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassHolder generateCall(MethodReader method) {
|
|
||||||
ClassHolder cls = generateClassDecl(method);
|
|
||||||
if (cls == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
cls.addMethod(generateConstructor(method, cls.getName()));
|
|
||||||
cls.addMethod(generateRun(method, cls.getName()));
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassHolder generateClassDecl(MethodReader method) {
|
|
||||||
AnnotationReader annot = method.getAnnotations().get(AsyncCaller.class.getName());
|
|
||||||
String className = annot.getValue("value").getString();
|
|
||||||
if (!generatedClassNames.add(className)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ClassHolder cls = new ClassHolder(className);
|
|
||||||
|
|
||||||
cls.getInterfaces().add(Fiber.class.getName() + "$AsyncCall");
|
|
||||||
|
|
||||||
List<ValueType> types = new ArrayList<>();
|
|
||||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
|
||||||
types.add(ValueType.object(method.getOwnerName()));
|
|
||||||
FieldHolder field = new FieldHolder("instance");
|
|
||||||
field.setType(ValueType.object(method.getOwnerName()));
|
|
||||||
cls.addField(field);
|
|
||||||
}
|
|
||||||
ValueType[] parameterTypes = method.getParameterTypes();
|
|
||||||
for (int i = 0; i < parameterTypes.length; ++i) {
|
|
||||||
types.add(parameterTypes[i]);
|
|
||||||
FieldHolder field = new FieldHolder("param" + i);
|
|
||||||
field.setType(parameterTypes[i]);
|
|
||||||
cls.addField(field);
|
|
||||||
}
|
|
||||||
types.add(ValueType.VOID);
|
|
||||||
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
|
||||||
cls.addMethod(constructor);
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodHolder generateConstructor(MethodReader method, String className) {
|
|
||||||
List<ValueType> types = new ArrayList<>();
|
|
||||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
|
||||||
types.add(ValueType.object(method.getOwnerName()));
|
|
||||||
}
|
|
||||||
Collections.addAll(types, method.getParameterTypes());
|
|
||||||
types.add(ValueType.VOID);
|
|
||||||
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
|
||||||
|
|
||||||
Program program = new Program();
|
|
||||||
constructor.setProgram(program);
|
|
||||||
BasicBlock block = program.createBasicBlock();
|
|
||||||
Variable instance = program.createVariable();
|
|
||||||
|
|
||||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
|
||||||
PutFieldInstruction putField = new PutFieldInstruction();
|
|
||||||
putField.setValue(program.createVariable());
|
|
||||||
putField.setField(new FieldReference(className, "instance"));
|
|
||||||
putField.setFieldType(ValueType.object(method.getOwnerName()));
|
|
||||||
putField.setInstance(instance);
|
|
||||||
block.add(putField);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
|
||||||
PutFieldInstruction putField = new PutFieldInstruction();
|
|
||||||
putField.setValue(program.createVariable());
|
|
||||||
putField.setField(new FieldReference(className, "param" + i));
|
|
||||||
putField.setFieldType(method.parameterType(i));
|
|
||||||
putField.setInstance(instance);
|
|
||||||
block.add(putField);
|
|
||||||
}
|
|
||||||
|
|
||||||
block.add(new ExitInstruction());
|
|
||||||
return constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodHolder generateRun(MethodReader method, String className) {
|
|
||||||
MethodHolder runMethod = new MethodHolder("run", ValueType.parse(AsyncCallback.class), ValueType.VOID);
|
|
||||||
Program program = new Program();
|
|
||||||
runMethod.setProgram(program);
|
|
||||||
BasicBlock block = program.createBasicBlock();
|
|
||||||
Variable instance = program.createVariable();
|
|
||||||
Variable callback = program.createVariable();
|
|
||||||
|
|
||||||
InvokeInstruction call = new InvokeInstruction();
|
|
||||||
call.setType(InvocationType.SPECIAL);
|
|
||||||
List<ValueType> types = new ArrayList<>();
|
|
||||||
ValueType[] parameterTypes = method.getParameterTypes();
|
|
||||||
List<Variable> arguments = new ArrayList<>(call.getArguments());
|
|
||||||
|
|
||||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
|
||||||
GetFieldInstruction getField = new GetFieldInstruction();
|
|
||||||
getField.setReceiver(program.createVariable());
|
|
||||||
getField.setInstance(instance);
|
|
||||||
getField.setField(new FieldReference(className, "instance"));
|
|
||||||
getField.setFieldType(ValueType.object(method.getOwnerName()));
|
|
||||||
block.add(getField);
|
|
||||||
call.setInstance(getField.getReceiver());
|
|
||||||
}
|
|
||||||
for (int i = 0; i < parameterTypes.length; ++i) {
|
|
||||||
GetFieldInstruction getField = new GetFieldInstruction();
|
|
||||||
getField.setReceiver(program.createVariable());
|
|
||||||
getField.setInstance(instance);
|
|
||||||
getField.setField(new FieldReference(className, "param" + i));
|
|
||||||
getField.setFieldType(parameterTypes[i]);
|
|
||||||
block.add(getField);
|
|
||||||
arguments.add(getField.getReceiver());
|
|
||||||
types.add(parameterTypes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
types.add(ValueType.parse(AsyncCallback.class));
|
|
||||||
arguments.add(callback);
|
|
||||||
|
|
||||||
types.add(ValueType.VOID);
|
|
||||||
call.setMethod(new MethodReference(method.getOwnerName(), method.getName(), types.toArray(new ValueType[0])));
|
|
||||||
call.setArguments(arguments.toArray(new Variable[0]));
|
|
||||||
block.add(call);
|
|
||||||
|
|
||||||
block.add(new ExitInstruction());
|
|
||||||
|
|
||||||
return runMethod;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
|
@ -28,8 +29,11 @@ import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassHolderTransformerContext;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldHolder;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.PrimitiveType;
|
import org.teavm.model.PrimitiveType;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
@ -38,9 +42,11 @@ import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.CastInstruction;
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
import org.teavm.runtime.Fiber;
|
import org.teavm.runtime.Fiber;
|
||||||
|
|
||||||
public class AsyncMethodProcessor implements ClassHolderTransformer {
|
public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
|
@ -75,7 +81,7 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lowLevel) {
|
if (lowLevel) {
|
||||||
generateLowLevelCall(method, suffix++);
|
generateLowLevelCall(method, suffix++, context);
|
||||||
} else {
|
} else {
|
||||||
generateCallerMethod(cls, method);
|
generateCallerMethod(cls, method);
|
||||||
}
|
}
|
||||||
|
@ -83,11 +89,9 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateLowLevelCall(MethodHolder method, int suffix) {
|
private void generateLowLevelCall(MethodHolder method, int suffix, ClassHolderTransformerContext context) {
|
||||||
String className = method.getOwnerName() + "$" + method.getName() + "$" + suffix;
|
String className = method.getOwnerName() + "$" + method.getName() + "$" + suffix;
|
||||||
AnnotationHolder classNameAnnot = new AnnotationHolder(AsyncCaller.class.getName());
|
context.submit(generateCall(method, className));
|
||||||
classNameAnnot.getValues().put("value", new AnnotationValue(className));
|
|
||||||
method.getAnnotations().add(classNameAnnot);
|
|
||||||
|
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
|
|
||||||
|
@ -295,4 +299,122 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||||
cast.setTargetType(ValueType.object(call.getMethod().getClassName()));
|
cast.setTargetType(ValueType.object(call.getMethod().getClassName()));
|
||||||
return call.getReceiver();
|
return call.getReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClassHolder generateCall(MethodReader method, String className) {
|
||||||
|
ClassHolder cls = generateClassDecl(method, className);
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cls.addMethod(generateConstructor(method, cls.getName()));
|
||||||
|
cls.addMethod(generateRun(method, cls.getName()));
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassHolder generateClassDecl(MethodReader method, String className) {
|
||||||
|
ClassHolder cls = new ClassHolder(className);
|
||||||
|
|
||||||
|
cls.getInterfaces().add(Fiber.class.getName() + "$AsyncCall");
|
||||||
|
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
types.add(ValueType.object(method.getOwnerName()));
|
||||||
|
FieldHolder field = new FieldHolder("instance");
|
||||||
|
field.setType(ValueType.object(method.getOwnerName()));
|
||||||
|
cls.addField(field);
|
||||||
|
}
|
||||||
|
ValueType[] parameterTypes = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
types.add(parameterTypes[i]);
|
||||||
|
FieldHolder field = new FieldHolder("param" + i);
|
||||||
|
field.setType(parameterTypes[i]);
|
||||||
|
cls.addField(field);
|
||||||
|
}
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
||||||
|
cls.addMethod(constructor);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHolder generateConstructor(MethodReader method, String className) {
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
types.add(ValueType.object(method.getOwnerName()));
|
||||||
|
}
|
||||||
|
Collections.addAll(types, method.getParameterTypes());
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
MethodHolder constructor = new MethodHolder("<init>", types.toArray(new ValueType[0]));
|
||||||
|
|
||||||
|
Program program = new Program();
|
||||||
|
constructor.setProgram(program);
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
Variable instance = program.createVariable();
|
||||||
|
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
PutFieldInstruction putField = new PutFieldInstruction();
|
||||||
|
putField.setValue(program.createVariable());
|
||||||
|
putField.setField(new FieldReference(className, "instance"));
|
||||||
|
putField.setFieldType(ValueType.object(method.getOwnerName()));
|
||||||
|
putField.setInstance(instance);
|
||||||
|
block.add(putField);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
PutFieldInstruction putField = new PutFieldInstruction();
|
||||||
|
putField.setValue(program.createVariable());
|
||||||
|
putField.setField(new FieldReference(className, "param" + i));
|
||||||
|
putField.setFieldType(method.parameterType(i));
|
||||||
|
putField.setInstance(instance);
|
||||||
|
block.add(putField);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.add(new ExitInstruction());
|
||||||
|
return constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHolder generateRun(MethodReader method, String className) {
|
||||||
|
MethodHolder runMethod = new MethodHolder("run", ValueType.parse(AsyncCallback.class), ValueType.VOID);
|
||||||
|
Program program = new Program();
|
||||||
|
runMethod.setProgram(program);
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
Variable instance = program.createVariable();
|
||||||
|
Variable callback = program.createVariable();
|
||||||
|
|
||||||
|
InvokeInstruction call = new InvokeInstruction();
|
||||||
|
call.setType(InvocationType.SPECIAL);
|
||||||
|
List<ValueType> types = new ArrayList<>();
|
||||||
|
ValueType[] parameterTypes = method.getParameterTypes();
|
||||||
|
List<Variable> arguments = new ArrayList<>(call.getArguments());
|
||||||
|
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
GetFieldInstruction getField = new GetFieldInstruction();
|
||||||
|
getField.setReceiver(program.createVariable());
|
||||||
|
getField.setInstance(instance);
|
||||||
|
getField.setField(new FieldReference(className, "instance"));
|
||||||
|
getField.setFieldType(ValueType.object(method.getOwnerName()));
|
||||||
|
block.add(getField);
|
||||||
|
call.setInstance(getField.getReceiver());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
GetFieldInstruction getField = new GetFieldInstruction();
|
||||||
|
getField.setReceiver(program.createVariable());
|
||||||
|
getField.setInstance(instance);
|
||||||
|
getField.setField(new FieldReference(className, "param" + i));
|
||||||
|
getField.setFieldType(parameterTypes[i]);
|
||||||
|
block.add(getField);
|
||||||
|
arguments.add(getField.getReceiver());
|
||||||
|
types.add(parameterTypes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
types.add(ValueType.parse(AsyncCallback.class));
|
||||||
|
arguments.add(callback);
|
||||||
|
|
||||||
|
types.add(ValueType.VOID);
|
||||||
|
call.setMethod(new MethodReference(method.getOwnerName(), method.getName(), types.toArray(new ValueType[0])));
|
||||||
|
call.setArguments(arguments.toArray(new Variable[0]));
|
||||||
|
block.add(call);
|
||||||
|
|
||||||
|
block.add(new ExitInstruction());
|
||||||
|
|
||||||
|
return runMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,10 +125,6 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration {
|
||||||
host.add(new EnumDependencySupport());
|
host.add(new EnumDependencySupport());
|
||||||
host.add(new PlatformDependencyListener());
|
host.add(new PlatformDependencyListener());
|
||||||
|
|
||||||
if (host.getExtension(TeaVMJavaScriptHost.class) == null) {
|
|
||||||
host.add(new AsyncLowLevelDependencyListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
||||||
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user