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();
|
||||
|
||||
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);
|
||||
|
|
|
@ -33,7 +33,6 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.teavm.cache.IncrementalDependencyProvider;
|
||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||
|
@ -51,7 +50,6 @@ import org.teavm.model.ClassHolderTransformer;
|
|||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -274,6 +270,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
lock(dep, false);
|
||||
deferredTasks.add(() -> {
|
||||
processInvokeDynamic(dep);
|
||||
classSource.getReferenceResolver().use(dep.method.getReference(), diagnostics);
|
||||
processMethod(dep);
|
||||
dep.used = true;
|
||||
});
|
||||
|
@ -499,8 +496,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
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);
|
||||
});
|
||||
}
|
||||
|
@ -762,10 +761,8 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
|
||||
allNodes.clear();
|
||||
classSource.cleanup();
|
||||
agent.cleanup();
|
||||
listeners.clear();
|
||||
classSource.innerHierarchy = null;
|
||||
|
||||
agentClassSource = classSourcePacker.pack(classSource,
|
||||
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
|
||||
|
@ -773,9 +770,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
classHierarchy = new ClassHierarchy(agentClassSource);
|
||||
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
||||
}
|
||||
classSource.innerHierarchy = null;
|
||||
classSource.dispose();
|
||||
classSource = null;
|
||||
methodReaderCache = null;
|
||||
fieldReaderCache = null;
|
||||
}
|
||||
|
||||
public void cleanupTypes() {
|
||||
|
|
|
@ -22,5 +22,5 @@ import org.teavm.model.ReferenceCache;
|
|||
|
||||
public interface DependencyAnalyzerFactory {
|
||||
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.MethodHolder;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
import org.teavm.model.transformation.ClassInitInsertion;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
|
||||
class DependencyClassSource implements ClassHolderSource {
|
||||
|
@ -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
|
||||
|
@ -75,12 +84,24 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
}
|
||||
|
||||
private ClassHolder findAndTransformClass(String name) {
|
||||
ClassHolder cls = findClass(name);
|
||||
if (cls != null && !transformers.isEmpty()) {
|
||||
for (ClassHolderTransformer transformer : transformers) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -108,7 +129,7 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
transformers.add(transformer);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
public void dispose() {
|
||||
transformers.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
@ -54,8 +41,6 @@ public class Linker {
|
|||
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])) {
|
||||
|
@ -64,63 +49,6 @@ public class Linker {
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
void cleanup() {
|
||||
if (method != null) {
|
||||
present = true;
|
||||
|
|
|
@ -25,8 +25,9 @@ import org.teavm.model.ValueType;
|
|||
|
||||
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
|
||||
|
|
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.Set;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
|
@ -36,13 +36,13 @@ import org.teavm.model.util.DominatorWalkerContext;
|
|||
|
||||
public class ClassInitInsertion {
|
||||
private static final MethodDescriptor CLINIT = new MethodDescriptor("<clinit>", void.class);
|
||||
private DependencyInfo dependencyInfo;
|
||||
private ClassReaderSource classSource;
|
||||
|
||||
public ClassInitInsertion(DependencyInfo dependencyInfo) {
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
public ClassInitInsertion(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
||||
public void apply(Program program, MethodReader method) {
|
||||
public void apply(MethodReader method, Program program) {
|
||||
if (program.basicBlockCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ public class ClassInitInsertion {
|
|||
|
||||
private void initializeClass(String className, Instruction instruction) {
|
||||
if (markClassAsInitialized(className)) {
|
||||
ClassReader cls = dependencyInfo.getClassSource().get(className);
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null || cls.getMethod(CLINIT) != null) {
|
||||
InitClassInstruction initInsn = new InitClassInstruction();
|
||||
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.text.ListingBuilder;
|
||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||
import org.teavm.model.util.MissingItemsProcessor;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.RegisterAllocator;
|
||||
|
@ -170,7 +169,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
classLoader = builder.classLoader;
|
||||
classSourcePacker = builder.classSourcePacker;
|
||||
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
|
||||
this, diagnostics, builder.referenceCache);
|
||||
this, diagnostics, builder.referenceCache, target.getPlatformTags());
|
||||
dependencyAnalyzer.setObfuscated(builder.obfuscated);
|
||||
dependencyAnalyzer.setStrict(builder.strict);
|
||||
progressListener = new TeaVMProgressListener() {
|
||||
|
@ -569,8 +568,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||
Linker linker = new Linker(dependency);
|
||||
var cutClasses = new MutableClassHolderSource();
|
||||
var missingItemsProcessor = new MissingItemsProcessor(dependency,
|
||||
dependency.getClassHierarchy(), diagnostics, target.getPlatformTags());
|
||||
if (wasCancelled()) {
|
||||
return cutClasses;
|
||||
}
|
||||
|
@ -584,7 +581,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
if (clsReader != null) {
|
||||
ClassHolder cls = ModelUtils.copyClass(clsReader);
|
||||
cutClasses.putClassHolder(cls);
|
||||
missingItemsProcessor.processClass(cls);
|
||||
linker.link(cls);
|
||||
}
|
||||
reportCompileProgress(++compileProgressValue);
|
||||
|
@ -962,8 +958,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
|
||||
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
||||
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 Set<String> classNames = Collections.unmodifiableSet(new HashSet<>(
|
||||
dependencyAnalyzer.getReachableClasses().stream()
|
||||
|
@ -980,6 +974,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return null;
|
||||
}
|
||||
ClassHolder cls = ModelUtils.copyClass(classReader, false);
|
||||
linker.link(cls);
|
||||
|
||||
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
|
||||
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
|
||||
|
@ -994,8 +989,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
: null;
|
||||
if (program == null) {
|
||||
program = ProgramUtils.copy(classReader.getMethod(method.getDescriptor()).getProgram());
|
||||
missingItemsProcessor.processMethod(method.getReference(), program);
|
||||
linker.link(method, program);
|
||||
clinitInsertion.apply(method, program);
|
||||
target.beforeInlining(program, method);
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.interop.Async;
|
||||
|
@ -28,8 +29,11 @@ import org.teavm.model.ClassHolder;
|
|||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.PrimitiveType;
|
||||
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.ConstructInstruction;
|
||||
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.JumpInstruction;
|
||||
import org.teavm.model.instructions.PutFieldInstruction;
|
||||
import org.teavm.runtime.Fiber;
|
||||
|
||||
public class AsyncMethodProcessor implements ClassHolderTransformer {
|
||||
|
@ -75,7 +81,7 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
|||
}
|
||||
|
||||
if (lowLevel) {
|
||||
generateLowLevelCall(method, suffix++);
|
||||
generateLowLevelCall(method, suffix++, context);
|
||||
} else {
|
||||
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;
|
||||
AnnotationHolder classNameAnnot = new AnnotationHolder(AsyncCaller.class.getName());
|
||||
classNameAnnot.getValues().put("value", new AnnotationValue(className));
|
||||
method.getAnnotations().add(classNameAnnot);
|
||||
context.submit(generateCall(method, className));
|
||||
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
|
||||
|
@ -295,4 +299,122 @@ public class AsyncMethodProcessor implements ClassHolderTransformer {
|
|||
cast.setTargetType(ValueType.object(call.getMethod().getClassName()));
|
||||
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 PlatformDependencyListener());
|
||||
|
||||
if (host.getExtension(TeaVMJavaScriptHost.class) == null) {
|
||||
host.add(new AsyncLowLevelDependencyListener());
|
||||
}
|
||||
|
||||
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
||||
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user