Refactor mechanism that resolves methods and fields parsed from bytecode and reports errors about missing items

This commit is contained in:
Alexey Andreev 2023-11-09 21:16:31 +01:00
parent 89661e52fb
commit 3282ae3b59
16 changed files with 611 additions and 658 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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() {

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -181,7 +181,6 @@ public class MethodDependency implements MethodDependencyInfo {
return external;
}
void cleanup() {
if (method != null) {
present = true;

View File

@ -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

View 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;
}
}
}

View File

@ -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);

View File

@ -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());
}
};
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);