mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
Fast dependency analyzer, fix bugs in incremental compiler
This commit is contained in:
parent
eaf0f5a24e
commit
d74bcbe2b9
|
@ -99,6 +99,27 @@
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>generate-tz-cache</id>
|
||||||
|
<goals>
|
||||||
|
<goal>java</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>process-classes</phase>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.teavm.classlib.impl.tz.TimeZoneCache</mainClass>
|
||||||
|
<arguments>
|
||||||
|
<argument>${project.build.directory}/classes/org/teavm/classlib/impl/tz/cache</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
|
|
@ -17,11 +17,11 @@ package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.teavm.common.DisjointSet;
|
import org.teavm.common.DisjointSet;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -43,16 +43,20 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
||||||
private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class);
|
private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
Program program = method.getProgram();
|
Program program = method.getProgram();
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
transformProgram(program, innerSource);
|
transformProgram(program, context.getHierarchy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformProgram(Program program, ClassReaderSource classSource) {
|
private void transformProgram(Program program, ClassHierarchy hierarchy) {
|
||||||
|
if (!hasForNameCall(program)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DisjointSet varSet = new DisjointSet();
|
DisjointSet varSet = new DisjointSet();
|
||||||
for (int i = 0; i < program.variableCount(); i++) {
|
for (int i = 0; i < program.variableCount(); i++) {
|
||||||
varSet.create();
|
varSet.create();
|
||||||
|
@ -116,7 +120,7 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
||||||
if (nameIndex >= 0) {
|
if (nameIndex >= 0) {
|
||||||
representative = program.variableAt(nameRepresentatives[nameIndex]);
|
representative = program.variableAt(nameRepresentatives[nameIndex]);
|
||||||
} else if (constant != null) {
|
} else if (constant != null) {
|
||||||
if (classSource.get(constant) == null || !filterClassName(constant)) {
|
if (hierarchy.getClassSource().get(constant) == null || !filterClassName(constant)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ClassConstantInstruction classConstant = new ClassConstantInstruction();
|
ClassConstantInstruction classConstant = new ClassConstantInstruction();
|
||||||
|
@ -149,6 +153,24 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasForNameCall(Program program) {
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (!(instruction instanceof InvokeInstruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||||
|
|
||||||
|
if (invoke.getMethod().equals(forNameMethod) || invoke.getMethod().equals(forNameShortMethod)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean filterClassName(String className) {
|
private boolean filterClassName(String className) {
|
||||||
switch (className) {
|
switch (className) {
|
||||||
// It's a hack for Kotlin. Kotlin full reflection library is too heavyweight for TeaVM.
|
// It's a hack for Kotlin. Kotlin full reflection library is too heavyweight for TeaVM.
|
||||||
|
|
|
@ -48,9 +48,6 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
jsExtension.add(loadServicesMethod, serviceLoaderSupp);
|
jsExtension.add(loadServicesMethod, serviceLoaderSupp);
|
||||||
jsExtension.addVirtualMethods(new AnnotationVirtualMethods());
|
jsExtension.addVirtualMethods(new AnnotationVirtualMethods());
|
||||||
}
|
}
|
||||||
|
|
||||||
JavacSupport javacSupport = new JavacSupport();
|
|
||||||
host.add(javacSupport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBootstrap()) {
|
if (!isBootstrap()) {
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2014 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.impl;
|
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.*;
|
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
|
||||||
|
|
||||||
public class JavacSupport implements ClassHolderTransformer {
|
|
||||||
@Override
|
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
|
||||||
if (cls.getName().equals("javax.tools.ToolProvider")) {
|
|
||||||
MethodHolder method = cls.getMethod(new MethodDescriptor("getSystemJavaCompiler",
|
|
||||||
ValueType.object("javax.tools.JavaCompiler")));
|
|
||||||
Program program = new Program();
|
|
||||||
BasicBlock block = program.createBasicBlock();
|
|
||||||
program.createVariable();
|
|
||||||
Variable var = program.createVariable();
|
|
||||||
ConstructInstruction construct = new ConstructInstruction();
|
|
||||||
construct.setReceiver(var);
|
|
||||||
construct.setType("com.sun.tools.javac.api.JavacTool");
|
|
||||||
block.add(construct);
|
|
||||||
InvokeInstruction init = new InvokeInstruction();
|
|
||||||
init.setInstance(var);
|
|
||||||
init.setType(InvocationType.SPECIAL);
|
|
||||||
init.setMethod(new MethodReference("com.sun.tools.javac.api.JavacTool", "<init>", ValueType.VOID));
|
|
||||||
block.add(init);
|
|
||||||
ExitInstruction exit = new ExitInstruction();
|
|
||||||
exit.setValueToReturn(var);
|
|
||||||
block.add(exit);
|
|
||||||
method.setProgram(program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -33,7 +32,7 @@ import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
|
||||||
public class NumericClassTransformer implements ClassHolderTransformer {
|
public class NumericClassTransformer implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
switch (cls.getName()) {
|
switch (cls.getName()) {
|
||||||
case "java.lang.Integer":
|
case "java.lang.Integer":
|
||||||
transformInteger(cls);
|
transformInteger(cls);
|
||||||
|
|
|
@ -21,9 +21,10 @@ import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -50,16 +51,17 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getProgram() != null) {
|
if (method.getProgram() != null) {
|
||||||
transformProgram(method.getReference(), method.getProgram(), innerSource, diagnostics);
|
transformProgram(method.getReference(), method.getProgram(), context.getHierarchy(),
|
||||||
|
context.getDiagnostics());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformProgram(MethodReference containingMethod, Program program,
|
private void transformProgram(MethodReference containingMethod, Program program,
|
||||||
ClassReaderSource innerSource, Diagnostics diagnostics) {
|
ClassHierarchy hierarchy, Diagnostics diagnostics) {
|
||||||
boolean hasChanges = false;
|
boolean hasChanges = false;
|
||||||
|
|
||||||
for (BasicBlock block : program.getBasicBlocks()) {
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
@ -68,7 +70,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
MarkerKind kind;
|
MarkerKind kind;
|
||||||
if (instruction instanceof InvokeInstruction) {
|
if (instruction instanceof InvokeInstruction) {
|
||||||
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
|
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
|
||||||
MethodReader method = innerSource.resolve(methodRef);
|
MethodReader method = hierarchy.getClassSource().resolveImplementation(methodRef);
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +94,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
receiver = ((InvokeInstruction) instruction).getReceiver();
|
receiver = ((InvokeInstruction) instruction).getReceiver();
|
||||||
} else if (instruction instanceof GetFieldInstruction) {
|
} else if (instruction instanceof GetFieldInstruction) {
|
||||||
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
|
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
|
||||||
FieldReader field = innerSource.resolve(fieldRef);
|
FieldReader field = hierarchy.getClassSource().resolve(fieldRef);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,6 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
Class[].class);
|
Class[].class);
|
||||||
private MethodReference constructorGetParameterTypes = new MethodReference(Constructor.class, "getParameterTypes",
|
private MethodReference constructorGetParameterTypes = new MethodReference(Constructor.class, "getParameterTypes",
|
||||||
Class[].class);
|
Class[].class);
|
||||||
private boolean fieldGetHandled;
|
|
||||||
private boolean fieldSetHandled;
|
|
||||||
private boolean newInstanceHandled;
|
|
||||||
private boolean invokeHandled;
|
|
||||||
private Map<String, Set<String>> accessibleFieldCache = new LinkedHashMap<>();
|
private Map<String, Set<String>> accessibleFieldCache = new LinkedHashMap<>();
|
||||||
private Map<String, Set<MethodDescriptor>> accessibleMethodCache = new LinkedHashMap<>();
|
private Map<String, Set<MethodDescriptor>> accessibleMethodCache = new LinkedHashMap<>();
|
||||||
private Set<String> classesWithReflectableFields = new LinkedHashSet<>();
|
private Set<String> classesWithReflectableFields = new LinkedHashSet<>();
|
||||||
|
@ -101,20 +97,20 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
public void classReached(DependencyAgent agent, String className) {
|
||||||
allClasses.propagate(agent.getType(className));
|
allClasses.propagate(agent.getType(className));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
if (method.getReference().equals(fieldGet)) {
|
if (method.getReference().equals(fieldGet)) {
|
||||||
handleFieldGet(agent, method, location);
|
handleFieldGet(agent, method);
|
||||||
} else if (method.getReference().equals(fieldSet)) {
|
} else if (method.getReference().equals(fieldSet)) {
|
||||||
handleFieldSet(agent, method, location);
|
handleFieldSet(agent, method);
|
||||||
} else if (method.getReference().equals(newInstance)) {
|
} else if (method.getReference().equals(newInstance)) {
|
||||||
handleNewInstance(agent, method, location);
|
handleNewInstance(agent, method);
|
||||||
} else if (method.getReference().equals(invokeMethod)) {
|
} else if (method.getReference().equals(invokeMethod)) {
|
||||||
handleInvoke(agent, method, location);
|
handleInvoke(agent, method);
|
||||||
} else if (method.getReference().equals(getFields)) {
|
} else if (method.getReference().equals(getFields)) {
|
||||||
method.getVariable(0).getClassValueNode().addConsumer(type -> {
|
method.getVariable(0).getClassValueNode().addConsumer(type -> {
|
||||||
if (!type.getName().startsWith("[")) {
|
if (!type.getName().startsWith("[")) {
|
||||||
|
@ -156,13 +152,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFieldGet(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
private void handleFieldGet(DependencyAgent agent, MethodDependency method) {
|
||||||
if (fieldGetHandled) {
|
CallLocation location = new CallLocation(method.getReference());
|
||||||
return;
|
DependencyNode classValueNode = agent.linkMethod(getFields)
|
||||||
}
|
.addLocation(location)
|
||||||
fieldGetHandled = true;
|
.getVariable(0).getClassValueNode();
|
||||||
|
|
||||||
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
|
|
||||||
classValueNode.addConsumer(reflectedType -> {
|
classValueNode.addConsumer(reflectedType -> {
|
||||||
if (reflectedType.getName().startsWith("[")) {
|
if (reflectedType.getName().startsWith("[")) {
|
||||||
return;
|
return;
|
||||||
|
@ -171,20 +165,19 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||||
for (String fieldName : accessibleFields) {
|
for (String fieldName : accessibleFields) {
|
||||||
FieldReader field = cls.getField(fieldName);
|
FieldReader field = cls.getField(fieldName);
|
||||||
FieldDependency fieldDep = agent.linkField(field.getReference(), location);
|
FieldDependency fieldDep = agent.linkField(field.getReference())
|
||||||
|
.addLocation(location);
|
||||||
propagateGet(agent, field.getType(), fieldDep.getValue(), method.getResult(), location);
|
propagateGet(agent, field.getType(), fieldDep.getValue(), method.getResult(), location);
|
||||||
linkClassIfNecessary(agent, field, location);
|
linkClassIfNecessary(agent, field, location);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFieldSet(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
private void handleFieldSet(DependencyAgent agent, MethodDependency method) {
|
||||||
if (fieldSetHandled) {
|
CallLocation location = new CallLocation(method.getReference());
|
||||||
return;
|
DependencyNode classValueNode = agent.linkMethod(getFields)
|
||||||
}
|
.addLocation(location)
|
||||||
fieldSetHandled = true;
|
.getVariable(0).getClassValueNode();
|
||||||
|
|
||||||
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
|
|
||||||
classValueNode.addConsumer(reflectedType -> {
|
classValueNode.addConsumer(reflectedType -> {
|
||||||
if (reflectedType.getName().startsWith("[")) {
|
if (reflectedType.getName().startsWith("[")) {
|
||||||
return;
|
return;
|
||||||
|
@ -194,20 +187,19 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||||
for (String fieldName : accessibleFields) {
|
for (String fieldName : accessibleFields) {
|
||||||
FieldReader field = cls.getField(fieldName);
|
FieldReader field = cls.getField(fieldName);
|
||||||
FieldDependency fieldDep = agent.linkField(field.getReference(), location);
|
FieldDependency fieldDep = agent.linkField(field.getReference()).addLocation(location);
|
||||||
propagateSet(agent, field.getType(), method.getVariable(2), fieldDep.getValue(), location);
|
propagateSet(agent, field.getType(), method.getVariable(2), fieldDep.getValue(), location);
|
||||||
linkClassIfNecessary(agent, field, location);
|
linkClassIfNecessary(agent, field, location);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNewInstance(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
private void handleNewInstance(DependencyAgent agent, MethodDependency method) {
|
||||||
if (newInstanceHandled) {
|
CallLocation location = new CallLocation(method.getReference());
|
||||||
return;
|
|
||||||
}
|
|
||||||
newInstanceHandled = true;
|
|
||||||
|
|
||||||
DependencyNode classValueNode = agent.linkMethod(getConstructors, location).getVariable(0).getClassValueNode();
|
DependencyNode classValueNode = agent.linkMethod(getConstructors)
|
||||||
|
.addLocation(location)
|
||||||
|
.getVariable(0).getClassValueNode();
|
||||||
classValueNode.addConsumer(reflectedType -> {
|
classValueNode.addConsumer(reflectedType -> {
|
||||||
if (reflectedType.getName().startsWith("[")) {
|
if (reflectedType.getName().startsWith("[")) {
|
||||||
return;
|
return;
|
||||||
|
@ -217,7 +209,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||||
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
||||||
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
||||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference(), location);
|
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
|
||||||
calledMethodDep.use();
|
calledMethodDep.use();
|
||||||
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
||||||
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(1).getArrayItem(),
|
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(1).getArrayItem(),
|
||||||
|
@ -230,13 +222,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
classValueNode.connect(method.getResult());
|
classValueNode.connect(method.getResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInvoke(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
private void handleInvoke(DependencyAgent agent, MethodDependency method) {
|
||||||
if (invokeHandled) {
|
CallLocation location = new CallLocation(method.getReference());
|
||||||
return;
|
DependencyNode classValueNode = agent.linkMethod(getMethods)
|
||||||
}
|
.addLocation(location)
|
||||||
invokeHandled = true;
|
.getVariable(0).getClassValueNode();
|
||||||
|
|
||||||
DependencyNode classValueNode = agent.linkMethod(getMethods, location).getVariable(0).getClassValueNode();
|
|
||||||
classValueNode.addConsumer(reflectedType -> {
|
classValueNode.addConsumer(reflectedType -> {
|
||||||
if (reflectedType.getName().startsWith("[")) {
|
if (reflectedType.getName().startsWith("[")) {
|
||||||
return;
|
return;
|
||||||
|
@ -246,7 +236,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||||
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
||||||
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
||||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference(), location);
|
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
|
||||||
calledMethodDep.use();
|
calledMethodDep.use();
|
||||||
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
||||||
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(2).getArrayItem(),
|
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(2).getArrayItem(),
|
||||||
|
@ -267,14 +257,14 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
if (type instanceof ValueType.Object) {
|
if (type instanceof ValueType.Object) {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
agent.linkClass(className, null);
|
agent.linkClass(className);
|
||||||
typesInReflectableSignaturesNode.propagate(agent.getType(className));
|
typesInReflectableSignaturesNode.propagate(agent.getType(className));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) {
|
private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) {
|
||||||
if (member.hasModifier(ElementModifier.STATIC)) {
|
if (member.hasModifier(ElementModifier.STATIC)) {
|
||||||
agent.linkClass(member.getOwnerName(), location).initClass(location);
|
agent.linkClass(member.getOwnerName()).initClass(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +323,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
default:
|
default:
|
||||||
throw new AssertionError(type.toString());
|
throw new AssertionError(type.toString());
|
||||||
}
|
}
|
||||||
MethodDependency boxMethodDep = agent.linkMethod(boxMethod, location);
|
MethodDependency boxMethodDep = agent.linkMethod(boxMethod).addLocation(location);
|
||||||
boxMethodDep.use();
|
boxMethodDep.use();
|
||||||
boxMethodDep.getResult().connect(targetNode);
|
boxMethodDep.getResult().connect(targetNode);
|
||||||
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
||||||
|
@ -370,7 +360,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
default:
|
default:
|
||||||
throw new AssertionError(type.toString());
|
throw new AssertionError(type.toString());
|
||||||
}
|
}
|
||||||
MethodDependency unboxMethodDep = agent.linkMethod(unboxMethod, location);
|
MethodDependency unboxMethodDep = agent.linkMethod(unboxMethod).addLocation(location);
|
||||||
unboxMethodDep.use();
|
unboxMethodDep.use();
|
||||||
sourceNode.connect(unboxMethodDep.getResult());
|
sourceNode.connect(unboxMethodDep.getResult());
|
||||||
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -33,10 +33,10 @@ import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
public class ScalaHacks implements ClassHolderTransformer {
|
public class ScalaHacks implements ClassHolderTransformer {
|
||||||
private static final String ATTR_NAME_CLASS = "java.util.jar.Attributes$Name";
|
private static final String ATTR_NAME_CLASS = "java.util.jar.Attributes$Name";
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
switch (cls.getName()) {
|
switch (cls.getName()) {
|
||||||
case "scala.util.PropertiesTrait$class":
|
case "scala.util.PropertiesTrait$class":
|
||||||
transformPropertiesTrait(cls, innerSource);
|
transformPropertiesTrait(cls, context.getHierarchy());
|
||||||
break;
|
break;
|
||||||
case "scala.util.Properties$":
|
case "scala.util.Properties$":
|
||||||
transformProperties(cls);
|
transformProperties(cls);
|
||||||
|
@ -44,10 +44,10 @@ public class ScalaHacks implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformPropertiesTrait(ClassHolder cls, ClassReaderSource innerSource) {
|
private void transformPropertiesTrait(ClassHolder cls, ClassHierarchy hierarchy) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getName().equals("scalaProps")) {
|
if (method.getName().equals("scalaProps")) {
|
||||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||||
pe.construct(Properties.class).returnValue();
|
pe.construct(Properties.class).returnValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,9 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
|
@ -42,9 +40,9 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class ServiceLoaderSupport extends AbstractDependencyListener implements Generator {
|
public class ServiceLoaderSupport extends AbstractDependencyListener implements Generator {
|
||||||
private Set<String> reachedClasses = new HashSet<>();
|
private static final MethodReference LOAD_METHOD = new MethodReference(ServiceLoader.class, "load", Class.class,
|
||||||
|
ServiceLoader.class);
|
||||||
private Map<String, List<String>> serviceMap = new HashMap<>();
|
private Map<String, List<String>> serviceMap = new HashMap<>();
|
||||||
private DependencyNode allClassesNode;
|
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
public ServiceLoaderSupport(ClassLoader classLoader) {
|
public ServiceLoaderSupport(ClassLoader classLoader) {
|
||||||
|
@ -87,30 +85,8 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
||||||
writer.append("return result;").softNewLine();
|
writer.append("return result;").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void parseServiceFile(DependencyAgent agent, DependencyNode targetNode, String service,
|
||||||
public void started(DependencyAgent agent) {
|
InputStream input, CallLocation location) throws IOException {
|
||||||
allClassesNode = agent.createNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
|
||||||
if (!reachedClasses.add(className)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + className);
|
|
||||||
while (resources.hasMoreElements()) {
|
|
||||||
URL resource = resources.nextElement();
|
|
||||||
try (InputStream stream = resource.openStream()) {
|
|
||||||
parseServiceFile(agent, className, stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseServiceFile(DependencyAgent agent, String service, InputStream input) throws IOException {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||||
while (true) {
|
while (true) {
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
|
@ -122,24 +98,37 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
serviceMap.computeIfAbsent(service, k -> new ArrayList<>()).add(line);
|
serviceMap.computeIfAbsent(service, k -> new ArrayList<>()).add(line);
|
||||||
allClassesNode.propagate(agent.getType(line));
|
|
||||||
|
MethodReference ctor = new MethodReference(line, new MethodDescriptor("<init>", ValueType.VOID));
|
||||||
|
agent.linkMethod(ctor).addLocation(location).use();
|
||||||
|
targetNode.propagate(agent.getType(line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
MethodReference ref = method.getReference();
|
MethodReference ref = method.getReference();
|
||||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||||
DependencyNode sourceNode = agent.linkMethod(new MethodReference(ServiceLoader.class, "load", Class.class,
|
DependencyNode sourceNode = agent.linkMethod(LOAD_METHOD).getVariable(1).getClassValueNode();
|
||||||
ServiceLoader.class), null).getVariable(1).getClassValueNode();
|
|
||||||
sourceNode.connect(method.getResult().getArrayItem());
|
sourceNode.connect(method.getResult().getArrayItem());
|
||||||
sourceNode.addConsumer(type -> initConstructor(agent, type.getName(), location));
|
sourceNode.addConsumer(type -> initConstructor(agent, method.getResult().getArrayItem(),
|
||||||
|
type.getName(), new CallLocation(LOAD_METHOD)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initConstructor(DependencyAgent agent, String type, CallLocation location) {
|
private void initConstructor(DependencyAgent agent, DependencyNode targetNode, String type,
|
||||||
MethodReference ctor = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
|
CallLocation location) {
|
||||||
agent.linkMethod(ctor, location).use();
|
try {
|
||||||
|
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + type);
|
||||||
|
while (resources.hasMoreElements()) {
|
||||||
|
URL resource = resources.nextElement();
|
||||||
|
try (InputStream stream = resource.openStream()) {
|
||||||
|
parseServiceFile(agent, targetNode, type, stream, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -31,7 +30,7 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
|
||||||
public class SystemClassTransformer implements ClassHolderTransformer {
|
public class SystemClassTransformer implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getProgram() != null) {
|
if (method.getProgram() != null) {
|
||||||
transformProgram(method.getProgram());
|
transformProgram(method.getProgram());
|
||||||
|
|
|
@ -15,14 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl.lambda;
|
package org.teavm.classlib.impl.lambda;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.teavm.cache.NoCache;
|
|
||||||
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||||
import org.teavm.dependency.DynamicCallSite;
|
import org.teavm.dependency.DynamicCallSite;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
@ -31,6 +32,8 @@ import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.MethodHandle;
|
import org.teavm.model.MethodHandle;
|
||||||
import org.teavm.model.MethodHandleType;
|
import org.teavm.model.MethodHandleType;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.PrimitiveType;
|
import org.teavm.model.PrimitiveType;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -42,7 +45,7 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
private static final int FLAG_SERIALIZABLE = 1;
|
private static final int FLAG_SERIALIZABLE = 1;
|
||||||
private static final int FLAG_MARKERS = 2;
|
private static final int FLAG_MARKERS = 2;
|
||||||
private static final int FLAG_BRIDGES = 4;
|
private static final int FLAG_BRIDGES = 4;
|
||||||
private Map<String, Integer> lambdaIdsByMethod = new HashMap<>();
|
private Map<MethodReference, Integer> lambdaIdsByMethod = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter callerPe) {
|
public ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter callerPe) {
|
||||||
|
@ -52,14 +55,24 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
||||||
|
|
||||||
String samName = ((ValueType.Object) callSite.getCalledMethod().getResultType()).getClassName();
|
String samName = ((ValueType.Object) callSite.getCalledMethod().getResultType()).getClassName();
|
||||||
ClassReaderSource classSource = callSite.getAgent().getClassSource();
|
ClassHierarchy hierarchy = callSite.getAgent().getClassHierarchy();
|
||||||
ClassReader samClass = classSource.get(samName);
|
ClassReader samClass = hierarchy.getClassSource().get(samName);
|
||||||
|
|
||||||
String key = callSite.getCaller().getClassName() + "$" + callSite.getCaller().getName();
|
String key = callSite.getCaller().getClassName() + "$" + callSite.getCaller().getName();
|
||||||
int id = lambdaIdsByMethod.getOrDefault(key, 0);
|
ClassReaderSource classSource = callSite.getAgent().getClassSource();
|
||||||
lambdaIdsByMethod.put(key, id + 1);
|
ClassReader callerClass = classSource.get(callSite.getCaller().getClassName());
|
||||||
|
int id = 0;
|
||||||
|
for (MethodReader callerMethod : callerClass.getMethods()) {
|
||||||
|
if (callerMethod.getDescriptor().equals(callSite.getCaller().getDescriptor())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int subId = lambdaIdsByMethod.getOrDefault(callSite.getCaller(), 0);
|
||||||
|
ClassHolder implementor = new ClassHolder(key + "$lambda$_" + id + "_" + subId);
|
||||||
|
lambdaIdsByMethod.put(callSite.getCaller(), subId + 1);
|
||||||
|
|
||||||
ClassHolder implementor = new ClassHolder(key + "$lambda$_" + id);
|
|
||||||
implementor.setLevel(AccessLevel.PUBLIC);
|
implementor.setLevel(AccessLevel.PUBLIC);
|
||||||
if (samClass != null && samClass.hasModifier(ElementModifier.INTERFACE)) {
|
if (samClass != null && samClass.hasModifier(ElementModifier.INTERFACE)) {
|
||||||
implementor.setParent("java.lang.Object");
|
implementor.setParent("java.lang.Object");
|
||||||
|
@ -69,16 +82,14 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
}
|
}
|
||||||
|
|
||||||
int capturedVarCount = callSite.getCalledMethod().parameterCount();
|
int capturedVarCount = callSite.getCalledMethod().parameterCount();
|
||||||
MethodHolder ctor = createConstructor(classSource, implementor,
|
MethodHolder ctor = createConstructor(hierarchy, implementor,
|
||||||
Arrays.copyOfRange(invokedType, 0, capturedVarCount), callerPe.getCurrentLocation());
|
Arrays.copyOfRange(invokedType, 0, capturedVarCount), callerPe.getCurrentLocation());
|
||||||
ctor.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
createBridge(hierarchy, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||||
createBridge(classSource, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
|
||||||
samMethodType, callerPe.getCurrentLocation());
|
samMethodType, callerPe.getCurrentLocation());
|
||||||
|
|
||||||
MethodHolder worker = new MethodHolder(callSite.getCalledMethod().getName(), instantiatedMethodType);
|
MethodHolder worker = new MethodHolder(callSite.getCalledMethod().getName(), instantiatedMethodType);
|
||||||
worker.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
|
||||||
worker.setLevel(AccessLevel.PUBLIC);
|
worker.setLevel(AccessLevel.PUBLIC);
|
||||||
ProgramEmitter pe = ProgramEmitter.create(worker, callSite.getAgent().getClassSource());
|
ProgramEmitter pe = ProgramEmitter.create(worker, callSite.getAgent().getClassHierarchy());
|
||||||
pe.setCurrentLocation(callerPe.getCurrentLocation());
|
pe.setCurrentLocation(callerPe.getCurrentLocation());
|
||||||
ValueEmitter thisVar = pe.var(0, implementor);
|
ValueEmitter thisVar = pe.var(0, implementor);
|
||||||
ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1];
|
ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1];
|
||||||
|
@ -128,13 +139,23 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
int bridgeCount = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getInt();
|
int bridgeCount = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getInt();
|
||||||
for (int i = 0; i < bridgeCount; ++i) {
|
for (int i = 0; i < bridgeCount; ++i) {
|
||||||
ValueType[] bridgeType = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getMethodType();
|
ValueType[] bridgeType = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getMethodType();
|
||||||
createBridge(classSource, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
createBridge(hierarchy, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||||
bridgeType, callerPe.getCurrentLocation());
|
bridgeType, callerPe.getCurrentLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> dependencies = new ArrayList<>();
|
||||||
|
dependencies.add(callSite.getCaller().getClassName());
|
||||||
|
dependencies.addAll(implementor.getInterfaces());
|
||||||
|
if (!implementor.getParent().equals("java.lang.Object")) {
|
||||||
|
dependencies.add(implementor.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
callSite.getAgent().submitClass(implementor);
|
callSite.getAgent().submitClass(implementor);
|
||||||
|
callSite.getAgent().getIncrementalCache().addDependencies(implementor.getName(),
|
||||||
|
dependencies.toArray(new String[0]));
|
||||||
|
|
||||||
return callerPe.construct(ctor.getOwnerName(), callSite.getArguments().toArray(new ValueEmitter[0]));
|
return callerPe.construct(ctor.getOwnerName(), callSite.getArguments().toArray(new ValueEmitter[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,14 +294,14 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodHolder createConstructor(ClassReaderSource classSource, ClassHolder implementor, ValueType[] types,
|
private MethodHolder createConstructor(ClassHierarchy hierarchy, ClassHolder implementor, ValueType[] types,
|
||||||
TextLocation location) {
|
TextLocation location) {
|
||||||
ValueType[] signature = Arrays.copyOf(types, types.length + 1);
|
ValueType[] signature = Arrays.copyOf(types, types.length + 1);
|
||||||
signature[types.length] = ValueType.VOID;
|
signature[types.length] = ValueType.VOID;
|
||||||
MethodHolder ctor = new MethodHolder("<init>", signature);
|
MethodHolder ctor = new MethodHolder("<init>", signature);
|
||||||
ctor.setLevel(AccessLevel.PUBLIC);
|
ctor.setLevel(AccessLevel.PUBLIC);
|
||||||
|
|
||||||
ProgramEmitter pe = ProgramEmitter.create(ctor, classSource);
|
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
|
||||||
pe.setCurrentLocation(location);
|
pe.setCurrentLocation(location);
|
||||||
ValueEmitter thisVar = pe.var(0, implementor);
|
ValueEmitter thisVar = pe.var(0, implementor);
|
||||||
thisVar.invokeSpecial(implementor.getParent(), "<init>");
|
thisVar.invokeSpecial(implementor.getParent(), "<init>");
|
||||||
|
@ -298,17 +319,16 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
return ctor;
|
return ctor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBridge(ClassReaderSource classSource, ClassHolder implementor, String name, ValueType[] types,
|
private void createBridge(ClassHierarchy hierarchy, ClassHolder implementor, String name, ValueType[] types,
|
||||||
ValueType[] bridgeTypes, TextLocation location) {
|
ValueType[] bridgeTypes, TextLocation location) {
|
||||||
if (Arrays.equals(types, bridgeTypes)) {
|
if (Arrays.equals(types, bridgeTypes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodHolder bridge = new MethodHolder(name, bridgeTypes);
|
MethodHolder bridge = new MethodHolder(name, bridgeTypes);
|
||||||
bridge.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
|
||||||
bridge.setLevel(AccessLevel.PUBLIC);
|
bridge.setLevel(AccessLevel.PUBLIC);
|
||||||
bridge.getModifiers().add(ElementModifier.BRIDGE);
|
bridge.getModifiers().add(ElementModifier.BRIDGE);
|
||||||
ProgramEmitter pe = ProgramEmitter.create(bridge, classSource);
|
ProgramEmitter pe = ProgramEmitter.create(bridge, hierarchy);
|
||||||
pe.setCurrentLocation(location);
|
pe.setCurrentLocation(location);
|
||||||
ValueEmitter thisVar = pe.var(0, implementor);
|
ValueEmitter thisVar = pe.var(0, implementor);
|
||||||
ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1];
|
ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1];
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
package org.teavm.classlib.impl.tz;
|
package org.teavm.classlib.impl.tz;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.teavm.classlib.impl.Base46;
|
import org.teavm.classlib.impl.Base46;
|
||||||
import org.teavm.classlib.impl.CharFlow;
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
|
||||||
public class TimeZoneCache {
|
public class TimeZoneCache {
|
||||||
public void write(OutputStream output, Collection<StorableDateTimeZone> timeZones) throws IOException {
|
public void write(OutputStream output, Collection<StorableDateTimeZone> timeZones) throws IOException {
|
||||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, "UTF-8"));
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (StorableDateTimeZone timeZone : timeZones) {
|
for (StorableDateTimeZone timeZone : timeZones) {
|
||||||
writer.append(timeZone.getID()).append(' ');
|
writer.append(timeZone.getID()).append(' ');
|
||||||
|
@ -36,7 +37,7 @@ public class TimeZoneCache {
|
||||||
|
|
||||||
public Map<String, StorableDateTimeZone> read(InputStream input) throws IOException {
|
public Map<String, StorableDateTimeZone> read(InputStream input) throws IOException {
|
||||||
Map<String, StorableDateTimeZone> result = new HashMap<>();
|
Map<String, StorableDateTimeZone> result = new HashMap<>();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||||
List<String> aliasLines = new ArrayList<>();
|
List<String> aliasLines = new ArrayList<>();
|
||||||
while (true) {
|
while (true) {
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
@ -50,11 +51,13 @@ public class TimeZoneGenerator implements MetadataGenerator {
|
||||||
case "northamerica":
|
case "northamerica":
|
||||||
case "pacificnew":
|
case "pacificnew":
|
||||||
case "southamerica":
|
case "southamerica":
|
||||||
compiler.parseDataFile(new BufferedReader(new InputStreamReader(zip, "UTF-8")), false);
|
compiler.parseDataFile(new BufferedReader(
|
||||||
|
new InputStreamReader(zip, StandardCharsets.UTF_8)), false);
|
||||||
break;
|
break;
|
||||||
case "backward":
|
case "backward":
|
||||||
case "backzone":
|
case "backzone":
|
||||||
compiler.parseDataFile(new BufferedReader(new InputStreamReader(zip, "UTF-8")), true);
|
compiler.parseDataFile(new BufferedReader(
|
||||||
|
new InputStreamReader(zip, StandardCharsets.UTF_8)), true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,13 @@ package org.teavm.classlib.java.lang;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
|
|
||||||
public class ClassDependencyListener implements DependencyPlugin {
|
public class ClassDependencyListener implements DependencyPlugin {
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
switch (method.getMethod().getName()) {
|
switch (method.getMethod().getName()) {
|
||||||
case "initialize":
|
case "initialize":
|
||||||
method.getVariable(0).getClassValueNode().addConsumer(type -> agent
|
method.getVariable(0).getClassValueNode().addConsumer(type -> agent.linkClass(type.getName()));
|
||||||
.linkClass(type.getName(), location)
|
|
||||||
.initClass(location));
|
|
||||||
break;
|
break;
|
||||||
case "getSimpleNameCacheLowLevel":
|
case "getSimpleNameCacheLowLevel":
|
||||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.teavm.classlib.impl.ReflectionDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
|
@ -65,7 +64,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
switch (method.getReference().getName()) {
|
switch (method.getReference().getName()) {
|
||||||
case "newEmptyInstance":
|
case "newEmptyInstance":
|
||||||
method.getVariable(0).getClassValueNode().connect(method.getResult());
|
method.getVariable(0).getClassValueNode().connect(method.getResult());
|
||||||
|
|
|
@ -18,11 +18,10 @@ package org.teavm.classlib.java.lang;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
|
|
||||||
public class ObjectDependencyPlugin implements DependencyPlugin {
|
public class ObjectDependencyPlugin implements DependencyPlugin {
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
switch (method.getMethod().getName()) {
|
switch (method.getMethod().getName()) {
|
||||||
case "clone":
|
case "clone":
|
||||||
method.getVariable(0).connect(method.getResult());
|
method.getVariable(0).connect(method.getResult());
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyNode;
|
import org.teavm.dependency.DependencyNode;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
||||||
|
@ -40,7 +39,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
switch (method.getReference().getName()) {
|
switch (method.getReference().getName()) {
|
||||||
case "doArrayCopy":
|
case "doArrayCopy":
|
||||||
reachArrayCopy(method);
|
reachArrayCopy(method);
|
||||||
|
|
|
@ -18,7 +18,9 @@ package org.teavm.classlib.java.lang.reflect;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyNode;
|
import org.teavm.dependency.DependencyNode;
|
||||||
|
@ -26,10 +28,9 @@ import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -41,16 +42,18 @@ import org.teavm.model.emit.ValueEmitter;
|
||||||
import org.teavm.platform.PlatformAnnotationProvider;
|
import org.teavm.platform.PlatformAnnotationProvider;
|
||||||
|
|
||||||
public class AnnotationDependencyListener extends AbstractDependencyListener {
|
public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
|
private Set<MethodReference> reachedMethods = new HashSet<>();
|
||||||
|
|
||||||
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
|
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
|
||||||
String implementorName = annotationType + "$$_impl";
|
String implementorName = annotationType + "$$_impl";
|
||||||
if (agent.getClassSource().get(implementorName) == null) {
|
if (agent.getClassSource().get(implementorName) == null) {
|
||||||
ClassHolder implementor = createImplementor(agent.getClassSource(), annotationType, implementorName);
|
ClassHolder implementor = createImplementor(agent.getClassHierarchy(), annotationType, implementorName);
|
||||||
agent.submitClass(implementor);
|
agent.submitClass(implementor);
|
||||||
}
|
}
|
||||||
return implementorName;
|
return implementorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassHolder createImplementor(ClassReaderSource classSource, String annotationType,
|
private ClassHolder createImplementor(ClassHierarchy hierarchy, String annotationType,
|
||||||
String implementorName) {
|
String implementorName) {
|
||||||
ClassHolder implementor = new ClassHolder(implementorName);
|
ClassHolder implementor = new ClassHolder(implementorName);
|
||||||
implementor.setParent("java.lang.Object");
|
implementor.setParent("java.lang.Object");
|
||||||
|
@ -58,7 +61,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
implementor.getModifiers().add(ElementModifier.FINAL);
|
implementor.getModifiers().add(ElementModifier.FINAL);
|
||||||
implementor.setLevel(AccessLevel.PUBLIC);
|
implementor.setLevel(AccessLevel.PUBLIC);
|
||||||
|
|
||||||
ClassReader annotation = classSource.get(annotationType);
|
ClassReader annotation = hierarchy.getClassSource().get(annotationType);
|
||||||
if (annotation == null) {
|
if (annotation == null) {
|
||||||
return implementor;
|
return implementor;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +77,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
implementor.addField(field);
|
implementor.addField(field);
|
||||||
|
|
||||||
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
|
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
|
||||||
ProgramEmitter pe = ProgramEmitter.create(accessor, classSource);
|
ProgramEmitter pe = ProgramEmitter.create(accessor, hierarchy);
|
||||||
ValueEmitter thisVal = pe.var(0, implementor);
|
ValueEmitter thisVal = pe.var(0, implementor);
|
||||||
ValueEmitter result = thisVal.getField(field.getName(), field.getType());
|
ValueEmitter result = thisVal.getField(field.getName(), field.getType());
|
||||||
if (field.getType() instanceof ValueType.Array) {
|
if (field.getType() instanceof ValueType.Array) {
|
||||||
|
@ -87,8 +90,8 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
ctorSignature.add(ValueType.VOID);
|
ctorSignature.add(ValueType.VOID);
|
||||||
|
|
||||||
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[ctorSignature.size()]));
|
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[0]));
|
||||||
ProgramEmitter pe = ProgramEmitter.create(ctor, classSource);
|
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
|
||||||
ValueEmitter thisVar = pe.var(0, implementor);
|
ValueEmitter thisVar = pe.var(0, implementor);
|
||||||
thisVar.invokeSpecial(Object.class, "<init>");
|
thisVar.invokeSpecial(Object.class, "<init>");
|
||||||
int index = 1;
|
int index = 1;
|
||||||
|
@ -103,7 +106,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
implementor.addMethod(ctor);
|
implementor.addMethod(ctor);
|
||||||
|
|
||||||
MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class));
|
MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class));
|
||||||
pe = ProgramEmitter.create(annotTypeMethod, classSource);
|
pe = ProgramEmitter.create(annotTypeMethod, hierarchy);
|
||||||
pe.constant(ValueType.object(annotationType)).returnValue();
|
pe.constant(ValueType.object(annotationType)).returnValue();
|
||||||
implementor.addMethod(annotTypeMethod);
|
implementor.addMethod(annotTypeMethod);
|
||||||
|
|
||||||
|
@ -111,7 +114,11 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (!reachedMethods.add(method.getReference())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ValueType type = method.getMethod().getResultType();
|
ValueType type = method.getMethod().getResultType();
|
||||||
while (type instanceof ValueType.Array) {
|
while (type instanceof ValueType.Array) {
|
||||||
type = ((ValueType.Array) type).getItemType();
|
type = ((ValueType.Array) type).getItemType();
|
||||||
|
@ -120,7 +127,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
ClassReader cls = agent.getClassSource().get(className);
|
ClassReader cls = agent.getClassSource().get(className);
|
||||||
if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) {
|
if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) {
|
||||||
agent.linkClass(className, location);
|
agent.linkClass(className);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,18 +136,18 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
ClassReader cls = agent.getClassSource().get(method.getReference().getClassName());
|
ClassReader cls = agent.getClassSource().get(method.getReference().getClassName());
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
||||||
agent.linkClass(annotation.getType(), location);
|
agent.linkClass(annotation.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodReference methodRef = method.getMethod().getReference();
|
MethodReference methodRef = method.getMethod().getReference();
|
||||||
if (methodRef.getClassName().equals("java.lang.Class") && methodRef.getName().equals("getAnnotations")) {
|
if (methodRef.getClassName().equals("java.lang.Class") && methodRef.getName().equals("getAnnotations")) {
|
||||||
reachGetAnnotations(agent, location, method.getVariable(0));
|
reachGetAnnotations(agent, method.getVariable(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reachGetAnnotations(DependencyAgent agent, CallLocation location, DependencyNode node) {
|
private void reachGetAnnotations(DependencyAgent agent, DependencyNode node) {
|
||||||
node.getClassValueNode().addConsumer(type -> {
|
node.getClassValueNode().addConsumer(type -> {
|
||||||
String className = type.getName();
|
String className = type.getName();
|
||||||
|
|
||||||
|
@ -150,7 +157,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
||||||
agent.linkClass(annotation.getType(), location);
|
agent.linkClass(annotation.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
createAnnotationClass(agent, className);
|
createAnnotationClass(agent, className);
|
||||||
|
@ -170,7 +177,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
|
|
||||||
MethodHolder ctor = new MethodHolder("<init>", ValueType.VOID);
|
MethodHolder ctor = new MethodHolder("<init>", ValueType.VOID);
|
||||||
ctor.setLevel(AccessLevel.PUBLIC);
|
ctor.setLevel(AccessLevel.PUBLIC);
|
||||||
ProgramEmitter pe = ProgramEmitter.create(ctor, agent.getClassSource());
|
ProgramEmitter pe = ProgramEmitter.create(ctor, agent.getClassHierarchy());
|
||||||
ValueEmitter thisVar = pe.var(0, cls);
|
ValueEmitter thisVar = pe.var(0, cls);
|
||||||
thisVar.invokeSpecial(Object.class, "<init>").exit();
|
thisVar.invokeSpecial(Object.class, "<init>").exit();
|
||||||
|
|
||||||
|
@ -184,7 +191,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
private MethodHolder addReader(DependencyAgent agent, ClassReader cls) {
|
private MethodHolder addReader(DependencyAgent agent, ClassReader cls) {
|
||||||
MethodHolder readerMethod = new MethodHolder("getAnnotations", ValueType.parse(Annotation[].class));
|
MethodHolder readerMethod = new MethodHolder("getAnnotations", ValueType.parse(Annotation[].class));
|
||||||
readerMethod.setLevel(AccessLevel.PUBLIC);
|
readerMethod.setLevel(AccessLevel.PUBLIC);
|
||||||
ProgramEmitter pe = ProgramEmitter.create(readerMethod, agent.getClassSource());
|
ProgramEmitter pe = ProgramEmitter.create(readerMethod, agent.getClassHierarchy());
|
||||||
|
|
||||||
List<AnnotationReader> annotations = new ArrayList<>();
|
List<AnnotationReader> annotations = new ArrayList<>();
|
||||||
for (AnnotationReader annot : cls.getAnnotations().all()) {
|
for (AnnotationReader annot : cls.getAnnotations().all()) {
|
||||||
|
@ -230,7 +237,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||||
.cast(methodDecl.getResultType()));
|
.cast(methodDecl.getResultType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return pe.construct(className, params.toArray(new ValueEmitter[params.size()]));
|
return pe.construct(className, params.toArray(new ValueEmitter[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueEmitter generateAnnotationValue(DependencyAgent agent, ProgramEmitter pe, ValueType type,
|
private ValueEmitter generateAnnotationValue(DependencyAgent agent, ProgramEmitter pe, ValueType type,
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
package org.teavm.classlib.java.lang.reflect;
|
package org.teavm.classlib.java.lang.reflect;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -35,8 +36,13 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
private static final ValueType[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER,
|
private static final ValueType[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER,
|
||||||
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
|
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
|
||||||
|
|
||||||
|
private Set<MethodReference> reachedMethods = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (!reachedMethods.add(method.getReference())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (method.getReference().getName()) {
|
switch (method.getReference().getName()) {
|
||||||
case "getLength":
|
case "getLength":
|
||||||
reachGetLength(agent, method);
|
reachGetLength(agent, method);
|
||||||
|
@ -51,7 +57,9 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
} else {
|
} else {
|
||||||
arrayTypeName = ValueType.object(t.getName()).toString();
|
arrayTypeName = ValueType.object(t.getName()).toString();
|
||||||
}
|
}
|
||||||
method.getResult().propagate(agent.getType("[" + arrayTypeName));
|
if (!arrayTypeName.startsWith("[[[")) {
|
||||||
|
method.getResult().propagate(agent.getType("[" + arrayTypeName));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "getImpl":
|
case "getImpl":
|
||||||
|
@ -91,11 +99,11 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
writer.append("return " + array + ".data.length;").softNewLine();
|
writer.append("return " + array + ".data.length;").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reachGetLength(final DependencyAgent agent, final MethodDependency method) {
|
private void reachGetLength(DependencyAgent agent, MethodDependency method) {
|
||||||
method.getVariable(1).addConsumer(type -> {
|
method.getVariable(1).addConsumer(type -> {
|
||||||
if (!type.getName().startsWith("[")) {
|
if (!type.getName().startsWith("[")) {
|
||||||
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class);
|
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class);
|
||||||
agent.linkMethod(cons, null).use();
|
agent.linkMethod(cons).use();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,7 +178,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
String wrapper = "java.lang." + primitiveWrappers[i];
|
String wrapper = "java.lang." + primitiveWrappers[i];
|
||||||
MethodReference methodRef = new MethodReference(wrapper, "valueOf",
|
MethodReference methodRef = new MethodReference(wrapper, "valueOf",
|
||||||
primitiveTypes[i], ValueType.object(wrapper));
|
primitiveTypes[i], ValueType.object(wrapper));
|
||||||
agent.linkMethod(methodRef, null).use();
|
agent.linkMethod(methodRef).use();
|
||||||
method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i]));
|
method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +196,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
String wrapper = "java.lang." + primitiveWrappers[i];
|
String wrapper = "java.lang." + primitiveWrappers[i];
|
||||||
MethodReference methodRef = new MethodReference(wrapper,
|
MethodReference methodRef = new MethodReference(wrapper,
|
||||||
primitives[i].toLowerCase() + "Value", primitiveTypes[i]);
|
primitives[i].toLowerCase() + "Value", primitiveTypes[i]);
|
||||||
agent.linkMethod(methodRef, null).use();
|
agent.linkMethod(methodRef).use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.ast.cache;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
|
||||||
import org.teavm.ast.RegularMethodNode;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
|
|
||||||
public class InMemoryRegularMethodNodeCache implements MethodNodeCache {
|
|
||||||
private Map<MethodReference, RegularMethodNode> cache = new HashMap<>();
|
|
||||||
private Map<MethodReference, AsyncMethodNode> asyncCache = new HashMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RegularMethodNode get(MethodReference methodReference) {
|
|
||||||
return cache.get(methodReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void store(MethodReference methodReference, RegularMethodNode node) {
|
|
||||||
cache.put(methodReference, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncMethodNode getAsync(MethodReference methodReference) {
|
|
||||||
return asyncCache.get(methodReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node) {
|
|
||||||
asyncCache.put(methodReference, node);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.ast.decompilation;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.ast.AssignmentStatement;
|
|
||||||
import org.teavm.ast.InvocationExpr;
|
|
||||||
import org.teavm.ast.RecursiveVisitor;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
|
|
||||||
class AsyncCallsFinder extends RecursiveVisitor {
|
|
||||||
final Set<MethodReference> asyncCalls = new HashSet<>();
|
|
||||||
final Set<MethodReference> allCalls = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(AssignmentStatement statement) {
|
|
||||||
InvocationExpr invocation = (InvocationExpr) statement.getRightValue();
|
|
||||||
asyncCalls.add(invocation.getMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InvocationExpr expr) {
|
|
||||||
super.visit(expr);
|
|
||||||
allCalls.add(expr.getMethod());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,12 +42,13 @@ import org.teavm.ast.Statement;
|
||||||
import org.teavm.ast.TryCatchStatement;
|
import org.teavm.ast.TryCatchStatement;
|
||||||
import org.teavm.ast.VariableNode;
|
import org.teavm.ast.VariableNode;
|
||||||
import org.teavm.ast.WhileStatement;
|
import org.teavm.ast.WhileStatement;
|
||||||
import org.teavm.ast.cache.MethodNodeCache;
|
|
||||||
import org.teavm.ast.optimization.Optimizer;
|
import org.teavm.ast.optimization.Optimizer;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.cache.NoCache;
|
import org.teavm.cache.AstDependencyExtractor;
|
||||||
|
import org.teavm.cache.CacheStatus;
|
||||||
|
import org.teavm.cache.MethodNodeCache;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphIndexer;
|
import org.teavm.common.GraphIndexer;
|
||||||
import org.teavm.common.Loop;
|
import org.teavm.common.Loop;
|
||||||
|
@ -74,6 +75,7 @@ import org.teavm.model.util.TypeInferer;
|
||||||
public class Decompiler {
|
public class Decompiler {
|
||||||
private ClassHolderSource classSource;
|
private ClassHolderSource classSource;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
private CacheStatus cacheStatus;
|
||||||
private Graph graph;
|
private Graph graph;
|
||||||
private LoopGraph loopGraph;
|
private LoopGraph loopGraph;
|
||||||
private GraphIndexer indexer;
|
private GraphIndexer indexer;
|
||||||
|
@ -90,15 +92,18 @@ public class Decompiler {
|
||||||
private Set<MethodReference> asyncMethods;
|
private Set<MethodReference> asyncMethods;
|
||||||
private Set<MethodReference> splitMethods = new HashSet<>();
|
private Set<MethodReference> splitMethods = new HashSet<>();
|
||||||
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
|
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
|
||||||
|
private final AstDependencyExtractor astDependencyExtractor = new AstDependencyExtractor();
|
||||||
private Deque<Block> stack;
|
private Deque<Block> stack;
|
||||||
private Program program;
|
private Program program;
|
||||||
private boolean friendlyToDebugger;
|
private boolean friendlyToDebugger;
|
||||||
private boolean moveConstants;
|
private boolean moveConstants;
|
||||||
|
|
||||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods,
|
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader,
|
||||||
Set<MethodReference> asyncFamilyMethods, boolean friendlyToDebugger, boolean moveConstants) {
|
CacheStatus cacheStatus, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
|
||||||
|
boolean friendlyToDebugger, boolean moveConstants) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
|
this.cacheStatus = cacheStatus;
|
||||||
this.asyncMethods = asyncMethods;
|
this.asyncMethods = asyncMethods;
|
||||||
splitMethods.addAll(asyncMethods);
|
splitMethods.addAll(asyncMethods);
|
||||||
splitMethods.addAll(asyncFamilyMethods);
|
splitMethods.addAll(asyncFamilyMethods);
|
||||||
|
@ -239,8 +244,7 @@ public class Decompiler {
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeMethodNode methodNode = new NativeMethodNode(new MethodReference(method.getOwnerName(),
|
NativeMethodNode methodNode = new NativeMethodNode(method.getReference());
|
||||||
method.getDescriptor()));
|
|
||||||
methodNode.getModifiers().addAll(method.getModifiers());
|
methodNode.getModifiers().addAll(method.getModifiers());
|
||||||
methodNode.setGenerator(generator);
|
methodNode.setGenerator(generator);
|
||||||
methodNode.setAsync(asyncMethods.contains(method.getReference()));
|
methodNode.setAsync(asyncMethods.contains(method.getReference()));
|
||||||
|
@ -253,13 +257,16 @@ public class Decompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegularMethodNode decompileRegular(MethodHolder method) {
|
public RegularMethodNode decompileRegular(MethodHolder method) {
|
||||||
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
if (regularMethodCache == null) {
|
||||||
return decompileRegularCacheMiss(method);
|
return decompileRegularCacheMiss(method);
|
||||||
}
|
}
|
||||||
RegularMethodNode node = regularMethodCache.get(method.getReference());
|
RegularMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
||||||
|
? regularMethodCache.get(method.getReference(), cacheStatus)
|
||||||
|
: null;
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
node = decompileRegularCacheMiss(method);
|
node = decompileRegularCacheMiss(method);
|
||||||
regularMethodCache.store(method.getReference(), node);
|
RegularMethodNode finalNode = node;
|
||||||
|
regularMethodCache.store(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -293,36 +300,20 @@ public class Decompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncMethodNode decompileAsync(MethodHolder method) {
|
public AsyncMethodNode decompileAsync(MethodHolder method) {
|
||||||
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
if (regularMethodCache == null) {
|
||||||
return decompileAsyncCacheMiss(method);
|
return decompileAsyncCacheMiss(method);
|
||||||
}
|
}
|
||||||
AsyncMethodNode node = regularMethodCache.getAsync(method.getReference());
|
AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
||||||
if (node == null || !checkAsyncRelevant(node)) {
|
? regularMethodCache.getAsync(method.getReference(), cacheStatus)
|
||||||
|
: null;
|
||||||
|
if (node == null) {
|
||||||
node = decompileAsyncCacheMiss(method);
|
node = decompileAsyncCacheMiss(method);
|
||||||
regularMethodCache.storeAsync(method.getReference(), node);
|
AsyncMethodNode finalNode = node;
|
||||||
|
regularMethodCache.storeAsync(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAsyncRelevant(AsyncMethodNode node) {
|
|
||||||
AsyncCallsFinder asyncCallsFinder = new AsyncCallsFinder();
|
|
||||||
for (AsyncMethodPart part : node.getBody()) {
|
|
||||||
part.getStatement().acceptVisitor(asyncCallsFinder);
|
|
||||||
}
|
|
||||||
for (MethodReference asyncCall : asyncCallsFinder.asyncCalls) {
|
|
||||||
if (!splitMethods.contains(asyncCall)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asyncCallsFinder.allCalls.removeAll(asyncCallsFinder.asyncCalls);
|
|
||||||
for (MethodReference asyncCall : asyncCallsFinder.allCalls) {
|
|
||||||
if (splitMethods.contains(asyncCall)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AsyncMethodNode decompileAsyncCacheMiss(MethodHolder method) {
|
private AsyncMethodNode decompileAsyncCacheMiss(MethodHolder method) {
|
||||||
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
|
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
|
||||||
AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods);
|
AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods);
|
||||||
|
|
|
@ -44,11 +44,9 @@ import org.teavm.common.GraphIndexer;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.InvokeDynamicInstruction;
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
import org.teavm.model.MethodDescriptor;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.ArrayElementType;
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
@ -93,6 +91,7 @@ import org.teavm.model.instructions.SwitchTableEntry;
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
class StatementGenerator implements InstructionVisitor {
|
class StatementGenerator implements InstructionVisitor {
|
||||||
|
private static final MethodReference CLONE_METHOD = new MethodReference(Object.class, "clone", Object.class);
|
||||||
private int lastSwitchId;
|
private int lastSwitchId;
|
||||||
final List<Statement> statements = new ArrayList<>();
|
final List<Statement> statements = new ArrayList<>();
|
||||||
GraphIndexer indexer;
|
GraphIndexer indexer;
|
||||||
|
@ -432,9 +431,7 @@ class StatementGenerator implements InstructionVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(CloneArrayInstruction insn) {
|
public void visit(CloneArrayInstruction insn) {
|
||||||
MethodDescriptor cloneMethodDesc = new MethodDescriptor("clone", ValueType.object("java.lang.Object"));
|
assign(Expr.invoke(CLONE_METHOD, Expr.var(insn.getArray().getIndex()), new Expr[0]), insn.getReceiver());
|
||||||
MethodReference cloneMethod = new MethodReference("java.lang.Object", cloneMethodDesc);
|
|
||||||
assign(Expr.invoke(cloneMethod, Expr.var(insn.getArray().getIndex()), new Expr[0]), insn.getReceiver());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -59,6 +59,7 @@ import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
|
import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
||||||
|
import org.teavm.cache.AlwaysStaleCacheStatus;
|
||||||
import org.teavm.dependency.ClassDependency;
|
import org.teavm.dependency.ClassDependency;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
@ -164,34 +165,34 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
@Override
|
@Override
|
||||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
RuntimeClass.class, Address.class), null).use();
|
RuntimeClass.class, Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||||
RuntimeClass.class, int.class, Address.class), null).use();
|
RuntimeClass.class, int.class, Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
||||||
RuntimeClass.class, Address.class, int.class, RuntimeArray.class), null).use();
|
RuntimeClass.class, Address.class, int.class, RuntimeArray.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||||
Throwable.class, void.class), null).use();
|
Throwable.class, void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
||||||
void.class), null).use();
|
void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||||
void.class), null).use();
|
void.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||||
Throwable.class), null).use();
|
Throwable.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkClass("java.lang.String", null);
|
dependencyAnalyzer.linkClass("java.lang.String");
|
||||||
dependencyAnalyzer.linkClass("java.lang.Class", null);
|
dependencyAnalyzer.linkClass("java.lang.Class");
|
||||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"), null);
|
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"));
|
||||||
|
|
||||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
|
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName());
|
||||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName());
|
||||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||||
for (FieldReader field : classDep.getClassReader().getFields()) {
|
for (FieldReader field : classDep.getClassReader().getFields()) {
|
||||||
dependencyAnalyzer.linkField(field.getReference(), null);
|
dependencyAnalyzer.linkField(field.getReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,8 +217,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
TagRegistry tagRegistry = new TagRegistry(classes);
|
TagRegistry tagRegistry = new TagRegistry(classes);
|
||||||
StringPool stringPool = new StringPool();
|
StringPool stringPool = new StringPool();
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
|
||||||
new HashSet<>(), false, true);
|
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
|
||||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||||
|
|
||||||
NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource());
|
NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource());
|
||||||
|
|
|
@ -18,21 +18,19 @@ package org.teavm.backend.c.analyze;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
|
||||||
public class CDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
public class CDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
|
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
|
||||||
if (delegateAnnot != null) {
|
if (delegateAnnot != null) {
|
||||||
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
||||||
|
@ -40,7 +38,9 @@ public class CDependencyListener extends AbstractDependencyListener implements C
|
||||||
for (MethodReader delegate : cls.getMethods()) {
|
for (MethodReader delegate : cls.getMethods()) {
|
||||||
if (delegate.getName().equals(delegateMethodName)) {
|
if (delegate.getName().equals(delegateMethodName)) {
|
||||||
if (delegate != method.getMethod()) {
|
if (delegate != method.getMethod()) {
|
||||||
agent.linkMethod(delegate.getReference(), location).use();
|
MethodDependency delegateDep = agent.linkMethod(delegate.getReference());
|
||||||
|
method.addLocationListener(delegateDep::addLocation);
|
||||||
|
delegateDep.use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class CDependencyListener extends AbstractDependencyListener implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||||
if (delegateAnnot != null) {
|
if (delegateAnnot != null) {
|
||||||
|
|
|
@ -35,8 +35,6 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.teavm.ast.ClassNode;
|
import org.teavm.ast.ClassNode;
|
||||||
import org.teavm.ast.cache.EmptyRegularMethodNodeCache;
|
|
||||||
import org.teavm.ast.cache.MethodNodeCache;
|
|
||||||
import org.teavm.ast.decompilation.Decompiler;
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.javascript.codegen.AliasProvider;
|
import org.teavm.backend.javascript.codegen.AliasProvider;
|
||||||
import org.teavm.backend.javascript.codegen.DefaultAliasProvider;
|
import org.teavm.backend.javascript.codegen.DefaultAliasProvider;
|
||||||
|
@ -53,6 +51,8 @@ import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||||
|
import org.teavm.cache.EmptyMethodNodeCache;
|
||||||
|
import org.teavm.cache.MethodNodeCache;
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||||
import org.teavm.debugging.information.SourceLocation;
|
import org.teavm.debugging.information.SourceLocation;
|
||||||
|
@ -107,7 +107,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private final List<Function<ProviderContext, Injector>> injectorProviders = new ArrayList<>();
|
private final List<Function<ProviderContext, Injector>> injectorProviders = new ArrayList<>();
|
||||||
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = new EmptyRegularMethodNodeCache();
|
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||||
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
||||||
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
|
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
|
||||||
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
|
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
|
||||||
|
@ -211,42 +211,41 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
DependencyType stringType = dependencyAnalyzer.getType("java.lang.String");
|
DependencyType stringType = dependencyAnalyzer.getType("java.lang.String");
|
||||||
|
|
||||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(Class.class.getName(), "getClass",
|
dep = dependencyAnalyzer.linkMethod(new MethodReference(Class.class.getName(), "getClass",
|
||||||
ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)), null);
|
ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)));
|
||||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("org.teavm.platform.PlatformClass"));
|
dep.getVariable(0).propagate(dependencyAnalyzer.getType("org.teavm.platform.PlatformClass"));
|
||||||
dep.getResult().propagate(dependencyAnalyzer.getType("java.lang.Class"));
|
dep.getResult().propagate(dependencyAnalyzer.getType("java.lang.Class"));
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
|
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class));
|
||||||
null);
|
|
||||||
dep.getVariable(0).propagate(stringType);
|
dep.getVariable(0).propagate(stringType);
|
||||||
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C"));
|
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C"));
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"), null);
|
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"));
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class), null);
|
dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class));
|
||||||
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||||
NoClassDefFoundError.class, "<init>", String.class, void.class), null);
|
NoClassDefFoundError.class, "<init>", String.class, void.class));
|
||||||
|
|
||||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "toString", String.class), null);
|
dep = dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "toString", String.class));
|
||||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.Object"));
|
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.Object"));
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
|
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
|
||||||
exceptionCons.getVariable(1).propagate(stringType);
|
exceptionCons.getVariable(1).propagate(stringType);
|
||||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchFieldError.class, "<init>",
|
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchFieldError.class, "<init>",
|
||||||
String.class, void.class), null);
|
String.class, void.class));
|
||||||
exceptionCons.use();
|
exceptionCons.use();
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchFieldError.class.getName()));
|
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchFieldError.class.getName()));
|
||||||
exceptionCons.getVariable(1).propagate(stringType);
|
exceptionCons.getVariable(1).propagate(stringType);
|
||||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchMethodError.class, "<init>",
|
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchMethodError.class, "<init>",
|
||||||
String.class, void.class), null);
|
String.class, void.class));
|
||||||
exceptionCons.use();
|
exceptionCons.use();
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName()));
|
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName()));
|
||||||
exceptionCons.getVariable(1).propagate(stringType);
|
exceptionCons.getVariable(1).propagate(stringType);
|
||||||
|
|
||||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||||
RuntimeException.class, "<init>", String.class, void.class), null);
|
RuntimeException.class, "<init>", String.class, void.class));
|
||||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
||||||
exceptionCons.getVariable(1).propagate(stringType);
|
exceptionCons.getVariable(1).propagate(stringType);
|
||||||
exceptionCons.use();
|
exceptionCons.use();
|
||||||
|
@ -263,7 +262,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
|
|
||||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||||
StackTraceElement.class, "<init>", String.class, String.class, String.class,
|
StackTraceElement.class, "<init>", String.class, String.class, String.class,
|
||||||
int.class, void.class), null);
|
int.class, void.class));
|
||||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
dep.getVariable(0).propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
||||||
dep.getVariable(1).propagate(stringType);
|
dep.getVariable(1).propagate(stringType);
|
||||||
dep.getVariable(2).propagate(stringType);
|
dep.getVariable(2).propagate(stringType);
|
||||||
|
@ -271,7 +270,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
dep.use();
|
dep.use();
|
||||||
|
|
||||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||||
Throwable.class, "setStackTrace", StackTraceElement[].class, void.class), null);
|
Throwable.class, "setStackTrace", StackTraceElement[].class, void.class));
|
||||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType(Throwable.class.getName()));
|
dep.getVariable(0).propagate(dependencyAnalyzer.getType(Throwable.class.getName()));
|
||||||
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/StackTraceElement;"));
|
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/StackTraceElement;"));
|
||||||
dep.getVariable(1).getArrayItem().propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
dep.getVariable(1).getArrayItem().propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
||||||
|
@ -440,9 +439,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
||||||
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), asyncMethods, asyncFamilyMethods,
|
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), controller.getCacheStatus(),
|
||||||
controller.isFriendlyToDebugger(), false);
|
asyncMethods, asyncFamilyMethods, controller.isFriendlyToDebugger(), false);
|
||||||
decompiler.setRegularMethodCache(controller.isIncremental() ? astCache : null);
|
decompiler.setRegularMethodCache(astCache);
|
||||||
|
|
||||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||||
decompiler.addGenerator(entry.getKey(), entry.getValue());
|
decompiler.addGenerator(entry.getKey(), entry.getValue());
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.backend.javascript;
|
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.*;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
|
||||||
import org.teavm.model.instructions.NullCheckInstruction;
|
|
||||||
|
|
||||||
public class NullPointerExceptionTransformer implements ClassHolderTransformer {
|
|
||||||
@Override
|
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
|
||||||
Program program = method.getProgram();
|
|
||||||
if (program == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
transformBlock(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void transformBlock(BasicBlock block) {
|
|
||||||
for (Instruction insn : block) {
|
|
||||||
if (insn instanceof InvokeInstruction) {
|
|
||||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
|
||||||
if (invoke.getType() != InvocationType.VIRTUAL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NullCheckInstruction nullCheck = new NullCheckInstruction();
|
|
||||||
nullCheck.setValue(invoke.getInstance());
|
|
||||||
Variable var = block.getProgram().createVariable();
|
|
||||||
nullCheck.setReceiver(var);
|
|
||||||
invoke.setInstance(var);
|
|
||||||
insn.insertPrevious(nullCheck);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript.codegen;
|
package org.teavm.backend.javascript.codegen;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -25,12 +27,15 @@ import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class DefaultAliasProvider implements AliasProvider {
|
public class DefaultAliasProvider implements AliasProvider {
|
||||||
private final Map<String, String> classAliases = new HashMap<>();
|
private final Map<String, String> classAliases = new HashMap<>();
|
||||||
private final Set<String> knownAliases = new HashSet<>();
|
private final Set<String> knownAliases = new HashSet<>(200, 0.5f);
|
||||||
private final Set<String> knownVirtualAliases = new HashSet<>();
|
private final ObjectIntMap<String> knowAliasesCounter = new ObjectIntHashMap<>();
|
||||||
|
private final Set<String> knownVirtualAliases = new HashSet<>(200, 0.5f);
|
||||||
|
private final ObjectIntMap<String> knowVirtualAliasesCounter = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClassAlias(String cls) {
|
public String getClassAlias(String cls) {
|
||||||
return classAliases.computeIfAbsent(cls, key -> makeUnique(knownAliases, suggestAliasForClass(key)));
|
return classAliases.computeIfAbsent(cls, key -> makeUnique(knownAliases, knowAliasesCounter,
|
||||||
|
suggestAliasForClass(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String suggestAliasForClass(String cls) {
|
private static String suggestAliasForClass(String cls) {
|
||||||
|
@ -80,7 +85,7 @@ public class DefaultAliasProvider implements AliasProvider {
|
||||||
alias = "$" + alias;
|
alias = "$" + alias;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return makeUnique(knownVirtualAliases, alias);
|
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,17 +100,18 @@ public class DefaultAliasProvider implements AliasProvider {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeUnique(knownAliases, getClassAlias(method.getClassName()) + "_" + alias);
|
return makeUnique(knownAliases, knowAliasesCounter, getClassAlias(method.getClassName()) + "_" + alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFieldAlias(FieldReference field) {
|
public String getFieldAlias(FieldReference field) {
|
||||||
return makeUnique(knownVirtualAliases, "$" + field.getFieldName());
|
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, "$" + field.getFieldName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStaticFieldAlias(FieldReference field) {
|
public String getStaticFieldAlias(FieldReference field) {
|
||||||
return makeUnique(knownAliases, getClassAlias(field.getClassName()) + "_" + field.getFieldName());
|
return makeUnique(knownAliases, knowAliasesCounter,
|
||||||
|
getClassAlias(field.getClassName()) + "_" + field.getFieldName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,15 +121,19 @@ public class DefaultAliasProvider implements AliasProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClassInitAlias(String className) {
|
public String getClassInitAlias(String className) {
|
||||||
return makeUnique(knownAliases, suggestAliasForClass(className) + "_$callClinit");
|
return makeUnique(knownAliases, knowAliasesCounter, suggestAliasForClass(className) + "_$callClinit");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String makeUnique(Set<String> knowAliases, String alias) {
|
private String makeUnique(Set<String> knowAliases, ObjectIntMap<String> indexMap, String alias) {
|
||||||
String uniqueAlias = alias;
|
String uniqueAlias = alias;
|
||||||
int index = 1;
|
int index = indexMap.get(alias);
|
||||||
|
if (index > 0) {
|
||||||
|
uniqueAlias = alias + index++;
|
||||||
|
}
|
||||||
while (!knowAliases.add(uniqueAlias)) {
|
while (!knowAliases.add(uniqueAlias)) {
|
||||||
uniqueAlias = alias + index++;
|
uniqueAlias = alias + index++;
|
||||||
}
|
}
|
||||||
|
indexMap.put(alias, index);
|
||||||
return uniqueAlias;
|
return uniqueAlias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
if (method.getLevel() == AccessLevel.PRIVATE && !className.equals(methodRef.getClassName())) {
|
if (method.getLevel() == AccessLevel.PRIVATE && !className.equals(methodRef.getClassName())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new MethodReference(className, method.getDescriptor());
|
return method.getReference();
|
||||||
}
|
}
|
||||||
className = cls.getParent();
|
className = cls.getParent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,15 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisitor {
|
class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisitor {
|
||||||
|
static final MethodReference MONITOR_ENTER_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorEnter", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_ENTER_SYNC_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorEnterSync", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_EXIT_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorExit", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_EXIT_SYNC_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorExitSync", Object.class, void.class);
|
||||||
|
|
||||||
private final NameFrequencyConsumer consumer;
|
private final NameFrequencyConsumer consumer;
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private boolean async;
|
private boolean async;
|
||||||
|
@ -167,14 +176,10 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
||||||
public void visit(MonitorEnterStatement statement) {
|
public void visit(MonitorEnterStatement statement) {
|
||||||
super.visit(statement);
|
super.visit(statement);
|
||||||
if (async) {
|
if (async) {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
consumer.consume(MONITOR_ENTER_METHOD);
|
||||||
Object.class, "monitorEnter", Object.class, void.class);
|
|
||||||
consumer.consume(monitorEnterRef);
|
|
||||||
consumer.consumeFunction("$rt_suspending");
|
consumer.consumeFunction("$rt_suspending");
|
||||||
} else {
|
} else {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
consumer.consume(MONITOR_ENTER_SYNC_METHOD);
|
||||||
Object.class, "monitorEnterSync", Object.class, void.class);
|
|
||||||
consumer.consume(monitorEnterRef);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,13 +187,9 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
||||||
public void visit(MonitorExitStatement statement) {
|
public void visit(MonitorExitStatement statement) {
|
||||||
super.visit(statement);
|
super.visit(statement);
|
||||||
if (async) {
|
if (async) {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
consumer.consume(MONITOR_EXIT_METHOD);
|
||||||
Object.class, "monitorExit", Object.class, void.class);
|
|
||||||
consumer.consume(monitorEnterRef);
|
|
||||||
} else {
|
} else {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
consumer.consume(MONITOR_EXIT_SYNC_METHOD);
|
||||||
Object.class, "monitorExitSync", Object.class, void.class);
|
|
||||||
consumer.consume(monitorEnterRef);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -836,7 +836,29 @@ public class Renderer implements RenderingManager {
|
||||||
|
|
||||||
statementRenderer.setEnd(true);
|
statementRenderer.setEnd(true);
|
||||||
statementRenderer.setCurrentPart(0);
|
statementRenderer.setCurrentPart(0);
|
||||||
|
|
||||||
|
if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
|
||||||
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD);
|
||||||
|
writer.append("(");
|
||||||
|
appendMonitor(statementRenderer, method);
|
||||||
|
writer.append(");").softNewLine();
|
||||||
|
|
||||||
|
writer.append("try").ws().append("{").softNewLine().indent();
|
||||||
|
}
|
||||||
|
|
||||||
method.getBody().acceptVisitor(statementRenderer);
|
method.getBody().acceptVisitor(statementRenderer);
|
||||||
|
|
||||||
|
if (method.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
|
||||||
|
writer.outdent().append("}").ws().append("finally").ws().append("{").indent().softNewLine();
|
||||||
|
|
||||||
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD);
|
||||||
|
writer.append("(");
|
||||||
|
appendMonitor(statementRenderer, method);
|
||||||
|
writer.append(");").softNewLine();
|
||||||
|
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RenderingException("IO error occurred", e);
|
throw new RenderingException("IO error occurred", e);
|
||||||
}
|
}
|
||||||
|
@ -913,8 +935,7 @@ public class Renderer implements RenderingManager {
|
||||||
for (int i = 0; i < methodNode.getBody().size(); ++i) {
|
for (int i = 0; i < methodNode.getBody().size(); ++i) {
|
||||||
writer.append("case ").append(i).append(":").indent().softNewLine();
|
writer.append("case ").append(i).append(":").indent().softNewLine();
|
||||||
if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
|
if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
|
||||||
writer.appendMethodBody(new MethodReference(Object.class, "monitorEnter",
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD);
|
||||||
Object.class, void.class));
|
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
appendMonitor(statementRenderer, methodNode);
|
appendMonitor(statementRenderer, methodNode);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
|
@ -932,8 +953,7 @@ public class Renderer implements RenderingManager {
|
||||||
writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();
|
writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();
|
||||||
writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())")
|
writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())")
|
||||||
.ws().append("{").indent().softNewLine();
|
.ws().append("{").indent().softNewLine();
|
||||||
writer.appendMethodBody(new MethodReference(Object.class, "monitorExit",
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD);
|
||||||
Object.class, void.class));
|
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
appendMonitor(statementRenderer, methodNode);
|
appendMonitor(statementRenderer, methodNode);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
|
|
|
@ -1459,6 +1459,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
|
if (defaultHandlerOccurred) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!defaultHandlerOccurred) {
|
if (!defaultHandlerOccurred) {
|
||||||
writer.ws().append("else").ws().append("{").indent().softNewLine();
|
writer.ws().append("else").ws().append("{").indent().softNewLine();
|
||||||
|
@ -1492,17 +1496,13 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
public void visit(MonitorEnterStatement statement) {
|
public void visit(MonitorEnterStatement statement) {
|
||||||
try {
|
try {
|
||||||
if (async) {
|
if (async) {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD).append("(");
|
||||||
Object.class, "monitorEnter", Object.class, void.class);
|
|
||||||
writer.appendMethodBody(monitorEnterRef).append("(");
|
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
statement.getObjectRef().acceptVisitor(this);
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
emitSuspendChecker();
|
emitSuspendChecker();
|
||||||
} else {
|
} else {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD).append('(');
|
||||||
Object.class, "monitorEnterSync", Object.class, void.class);
|
|
||||||
writer.appendMethodBody(monitorEnterRef).append('(');
|
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
statement.getObjectRef().acceptVisitor(this);
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
|
@ -1523,16 +1523,12 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
public void visit(MonitorExitStatement statement) {
|
public void visit(MonitorExitStatement statement) {
|
||||||
try {
|
try {
|
||||||
if (async) {
|
if (async) {
|
||||||
MethodReference monitorExitRef = new MethodReference(
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
|
||||||
Object.class, "monitorExit", Object.class, void.class);
|
|
||||||
writer.appendMethodBody(monitorExitRef).append("(");
|
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
statement.getObjectRef().acceptVisitor(this);
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
} else {
|
} else {
|
||||||
MethodReference monitorEnterRef = new MethodReference(
|
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD).append('(');
|
||||||
Object.class, "monitorExitSync", Object.class, void.class);
|
|
||||||
writer.appendMethodBody(monitorEnterRef).append('(');
|
|
||||||
precedence = Precedence.min();
|
precedence = Precedence.min();
|
||||||
statement.getObjectRef().acceptVisitor(this);
|
statement.getObjectRef().acceptVisitor(this);
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
|
|
|
@ -91,6 +91,7 @@ import org.teavm.backend.wasm.render.WasmCRenderer;
|
||||||
import org.teavm.backend.wasm.render.WasmRenderer;
|
import org.teavm.backend.wasm.render.WasmRenderer;
|
||||||
import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation;
|
import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation;
|
||||||
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
|
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
|
||||||
|
import org.teavm.cache.AlwaysStaleCacheStatus;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.dependency.ClassDependency;
|
import org.teavm.dependency.ClassDependency;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
@ -241,61 +242,60 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||||
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
||||||
MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
|
MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
|
||||||
dependencyAnalyzer.linkMethod(method, null).use();
|
dependencyAnalyzer.linkMethod(method).use();
|
||||||
}
|
}
|
||||||
for (Class<?> type : Arrays.asList(float.class, double.class)) {
|
for (Class<?> type : Arrays.asList(float.class, double.class)) {
|
||||||
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
||||||
dependencyAnalyzer.linkMethod(method, null).use();
|
dependencyAnalyzer.linkMethod(method).use();
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "align", Address.class, int.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "align", Address.class, int.class,
|
||||||
Address.class), null).use();
|
Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "fillZero", Address.class, int.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "fillZero", Address.class, int.class,
|
||||||
void.class), null).use();
|
void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class,
|
||||||
Address.class, int.class, void.class), null).use();
|
Address.class, int.class, void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "allocStack",
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "allocStack",
|
||||||
int.class, Address.class), null).use();
|
int.class, Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackTop", Address.class),
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackTop", Address.class)).use();
|
||||||
null) .use();
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getNextStackFrame", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getNextStackFrame", Address.class,
|
||||||
Address.class), null).use();
|
Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootCount", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootCount", Address.class,
|
||||||
int.class), null).use();
|
int.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootPointer", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootPointer", Address.class,
|
||||||
Address.class), null).use();
|
Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerId", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerId", Address.class,
|
||||||
int.class, void.class), null).use();
|
int.class, void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
|
||||||
int.class), null).use();
|
int.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "resourceMapKeys", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "resourceMapKeys", Address.class,
|
||||||
String[].class), null).use();
|
String[].class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
|
||||||
String.class, Address.class), null).use();
|
String.class, Address.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
RuntimeClass.class, Address.class), null).use();
|
RuntimeClass.class, Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||||
RuntimeClass.class, int.class, Address.class), null).use();
|
RuntimeClass.class, int.class, Address.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
||||||
RuntimeClass.class, Address.class, int.class, RuntimeArray.class), null).use();
|
RuntimeClass.class, Address.class, int.class, RuntimeArray.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||||
Throwable.class, void.class), null).use();
|
Throwable.class, void.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||||
Throwable.class), null).use();
|
Throwable.class)).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.Object", "monitor"), null);
|
dependencyAnalyzer.linkField(new FieldReference("java.lang.Object", "monitor"));
|
||||||
|
|
||||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
|
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName());
|
||||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName());
|
||||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||||
for (FieldReader field : classDep.getClassReader().getFields()) {
|
for (FieldReader field : classDep.getClassReader().getFields()) {
|
||||||
dependencyAnalyzer.linkField(field.getReference(), null);
|
dependencyAnalyzer.linkField(field.getReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,8 +325,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
|
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
|
||||||
vtableProvider, tagRegistry, binaryWriter, names);
|
vtableProvider, tagRegistry, binaryWriter, names);
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
|
||||||
new HashSet<>(), false, true);
|
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
|
||||||
WasmStringPool stringPool = classGenerator.getStringPool();
|
WasmStringPool stringPool = classGenerator.getStringPool();
|
||||||
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
||||||
vtableProvider, tagRegistry, stringPool, names);
|
vtableProvider, tagRegistry, stringPool, names);
|
||||||
|
|
|
@ -18,32 +18,30 @@ package org.teavm.backend.wasm.generate;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.interop.Export;
|
import org.teavm.interop.Export;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
|
||||||
public class WasmDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
public class WasmDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
public void classReached(DependencyAgent agent, String className) {
|
||||||
for (MethodReader reader : agent.getClassSource().get(className).getMethods()) {
|
for (MethodReader reader : agent.getClassSource().get(className).getMethods()) {
|
||||||
AnnotationReader annotation = reader.getAnnotations().get(Export.class.getName());
|
AnnotationReader annotation = reader.getAnnotations().get(Export.class.getName());
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
agent.linkMethod(reader.getReference(), null).use();
|
agent.linkMethod(reader.getReference()).use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
|
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
|
||||||
if (delegateAnnot != null) {
|
if (delegateAnnot != null) {
|
||||||
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
||||||
|
@ -51,7 +49,9 @@ public class WasmDependencyListener extends AbstractDependencyListener implement
|
||||||
for (MethodReader delegate : cls.getMethods()) {
|
for (MethodReader delegate : cls.getMethods()) {
|
||||||
if (delegate.getName().equals(delegateMethodName)) {
|
if (delegate.getName().equals(delegateMethodName)) {
|
||||||
if (delegate != method.getMethod()) {
|
if (delegate != method.getMethod()) {
|
||||||
agent.linkMethod(delegate.getReference(), location).use();
|
MethodDependency dep = agent.linkMethod(delegate.getReference());
|
||||||
|
dep.use();
|
||||||
|
method.addLocationListener(dep::addLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ public class WasmDependencyListener extends AbstractDependencyListener implement
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||||
if (delegateAnnot != null) {
|
if (delegateAnnot != null) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,23 +13,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.cache;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import org.teavm.model.MethodReference;
|
||||||
import java.util.Map;
|
|
||||||
import org.teavm.model.util.ProgramUtils;
|
|
||||||
|
|
||||||
public class InMemoryProgramCache implements ProgramCache {
|
public class AlwaysFreshCacheStatus implements CacheStatus {
|
||||||
private Map<MethodReference, Program> cache = new HashMap<>();
|
public static final AlwaysFreshCacheStatus INSTANCE = new AlwaysFreshCacheStatus();
|
||||||
|
|
||||||
@Override
|
private AlwaysFreshCacheStatus() {
|
||||||
public Program get(MethodReference method) {
|
|
||||||
Program program = cache.get(method);
|
|
||||||
return program != null ? ProgramUtils.copy(program) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference method, Program program) {
|
public boolean isStaleClass(String className) {
|
||||||
cache.put(method, ProgramUtils.copy(program));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleMethod(MethodReference method) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
35
core/src/main/java/org/teavm/cache/AlwaysStaleCacheStatus.java
vendored
Normal file
35
core/src/main/java/org/teavm/cache/AlwaysStaleCacheStatus.java
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class AlwaysStaleCacheStatus implements CacheStatus {
|
||||||
|
public static final AlwaysStaleCacheStatus INSTANCE = new AlwaysStaleCacheStatus();
|
||||||
|
|
||||||
|
private AlwaysStaleCacheStatus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleClass(String className) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleMethod(MethodReference method) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
98
core/src/main/java/org/teavm/cache/AnnotationAwareCacheStatus.java
vendored
Normal file
98
core/src/main/java/org/teavm/cache/AnnotationAwareCacheStatus.java
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public final class AnnotationAwareCacheStatus implements CacheStatus {
|
||||||
|
private static final byte UNKNOWN = 0;
|
||||||
|
private static final byte STALE = 1;
|
||||||
|
private static final byte UNDECIDED = 2;
|
||||||
|
private static final byte FRESH = 3;
|
||||||
|
|
||||||
|
private CacheStatus underlyingStatus;
|
||||||
|
private IncrementalDependencyProvider dependencyProvider;
|
||||||
|
private List<Predicate<String>> synthesizedClasses = new ArrayList<>();
|
||||||
|
private ObjectByteMap<String> classStatusCache = new ObjectByteHashMap<>();
|
||||||
|
|
||||||
|
public AnnotationAwareCacheStatus(CacheStatus underlyingStatus, IncrementalDependencyProvider dependencyProvider) {
|
||||||
|
this.underlyingStatus = underlyingStatus;
|
||||||
|
this.dependencyProvider = dependencyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSynthesizedClasses(Predicate<String> synthesizedClasses) {
|
||||||
|
this.synthesizedClasses.add(synthesizedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleClass(String className) {
|
||||||
|
return getClassStatus(className) == STALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte getClassStatus(String className) {
|
||||||
|
byte status = classStatusCache.getOrDefault(className, UNKNOWN);
|
||||||
|
if (status == UNKNOWN) {
|
||||||
|
classStatusCache.put(className, UNDECIDED);
|
||||||
|
status = computeClassStatus(className);
|
||||||
|
classStatusCache.put(className, status);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte computeClassStatus(String className) {
|
||||||
|
if (!isSynthesizedClass(className)) {
|
||||||
|
if (underlyingStatus.isStaleClass(className)) {
|
||||||
|
return STALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependencyProvider.isNoCache(className)) {
|
||||||
|
return STALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasStaleDependencies(dependencyProvider.getDependencies(className))) {
|
||||||
|
return STALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FRESH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleMethod(MethodReference method) {
|
||||||
|
return isStaleClass(method.getClassName()) || dependencyProvider.isNoCache(method)
|
||||||
|
|| hasStaleDependencies(dependencyProvider.getDependencies(method));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasStaleDependencies(String[] dependencies) {
|
||||||
|
for (String dependency : dependencies) {
|
||||||
|
byte dependencyStatus = getClassStatus(dependency);
|
||||||
|
if (dependencyStatus == STALE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSynthesizedClass(String className) {
|
||||||
|
return synthesizedClasses.stream().anyMatch(p -> p.test(className));
|
||||||
|
}
|
||||||
|
}
|
61
core/src/main/java/org/teavm/cache/AstDependencyExtractor.java
vendored
Normal file
61
core/src/main/java/org/teavm/cache/AstDependencyExtractor.java
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
|
import org.teavm.ast.AsyncMethodPart;
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.ast.QualificationExpr;
|
||||||
|
import org.teavm.ast.RecursiveVisitor;
|
||||||
|
import org.teavm.ast.RegularMethodNode;
|
||||||
|
|
||||||
|
public class AstDependencyExtractor extends RecursiveVisitor {
|
||||||
|
private final ExtractingVisitor visitor = new ExtractingVisitor();
|
||||||
|
|
||||||
|
public String[] extract(RegularMethodNode node) {
|
||||||
|
node.getBody().acceptVisitor(visitor);
|
||||||
|
String[] result = visitor.dependencies.toArray(new String[0]);
|
||||||
|
visitor.dependencies.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] extract(AsyncMethodNode node) {
|
||||||
|
for (AsyncMethodPart part : node.getBody()) {
|
||||||
|
part.getStatement().acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
String[] result = visitor.dependencies.toArray(new String[0]);
|
||||||
|
visitor.dependencies.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class ExtractingVisitor extends RecursiveVisitor {
|
||||||
|
final Set<String> dependencies = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvocationExpr expr) {
|
||||||
|
super.visit(expr);
|
||||||
|
dependencies.add(expr.getMethod().getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(QualificationExpr expr) {
|
||||||
|
super.visit(expr);
|
||||||
|
dependencies.add(expr.getField().getClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
core/src/main/java/org/teavm/cache/AstIO.java
vendored
10
core/src/main/java/org/teavm/cache/AstIO.java
vendored
|
@ -83,6 +83,8 @@ public class AstIO {
|
||||||
private final SymbolTable symbolTable;
|
private final SymbolTable symbolTable;
|
||||||
private final SymbolTable fileTable;
|
private final SymbolTable fileTable;
|
||||||
private final Map<String, IdentifiedStatement> statementMap = new HashMap<>();
|
private final Map<String, IdentifiedStatement> statementMap = new HashMap<>();
|
||||||
|
private Map<String, MethodDescriptor> methodDescriptorCache = new HashMap<>();
|
||||||
|
private Map<String, Map<MethodDescriptor, MethodReference>> methodReferenceCache = new HashMap<>();
|
||||||
|
|
||||||
public AstIO(SymbolTable symbolTable, SymbolTable fileTable) {
|
public AstIO(SymbolTable symbolTable, SymbolTable fileTable) {
|
||||||
this.symbolTable = symbolTable;
|
this.symbolTable = symbolTable;
|
||||||
|
@ -949,8 +951,12 @@ public class AstIO {
|
||||||
InvocationExpr expr = new InvocationExpr();
|
InvocationExpr expr = new InvocationExpr();
|
||||||
expr.setType(invocationType);
|
expr.setType(invocationType);
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
MethodDescriptor method = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
MethodDescriptor method = methodDescriptorCache.computeIfAbsent(symbolTable.at(input.readInt()),
|
||||||
expr.setMethod(new MethodReference(className, method));
|
MethodDescriptor::parse);
|
||||||
|
MethodReference methodRef = methodReferenceCache
|
||||||
|
.computeIfAbsent(className, k -> new HashMap<>())
|
||||||
|
.computeIfAbsent(method, k -> new MethodReference(className, k));
|
||||||
|
expr.setMethod(methodRef);
|
||||||
int argCount = input.readShort();
|
int argCount = input.readShort();
|
||||||
for (int i = 0; i < argCount; ++i) {
|
for (int i = 0; i < argCount; ++i) {
|
||||||
expr.getArguments().add(readExpr(input));
|
expr.getArguments().add(readExpr(input));
|
||||||
|
|
24
core/src/main/java/org/teavm/cache/CacheStatus.java
vendored
Normal file
24
core/src/main/java/org/teavm/cache/CacheStatus.java
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface CacheStatus {
|
||||||
|
boolean isStaleClass(String className);
|
||||||
|
|
||||||
|
boolean isStaleMethod(MethodReference method);
|
||||||
|
}
|
|
@ -15,12 +15,43 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.cache;
|
package org.teavm.cache;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedInputStream;
|
||||||
import java.util.*;
|
import java.io.BufferedOutputStream;
|
||||||
import org.teavm.model.*;
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.model.AccessLevel;
|
||||||
|
import org.teavm.model.AnnotationContainer;
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.AnnotationValue;
|
||||||
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
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.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.parsing.ClassDateProvider;
|
import org.teavm.parsing.ClassDateProvider;
|
||||||
|
|
||||||
public class DiskCachedClassHolderSource implements ClassHolderSource {
|
public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStatus {
|
||||||
private static AccessLevel[] accessLevels = AccessLevel.values();
|
private static AccessLevel[] accessLevels = AccessLevel.values();
|
||||||
private static ElementModifier[] elementModifiers = ElementModifier.values();
|
private static ElementModifier[] elementModifiers = ElementModifier.values();
|
||||||
private File directory;
|
private File directory;
|
||||||
|
@ -42,6 +73,20 @@ public class DiskCachedClassHolderSource implements ClassHolderSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassHolder get(String name) {
|
public ClassHolder get(String name) {
|
||||||
|
return getItemFromCache(name).cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleClass(String className) {
|
||||||
|
return getItemFromCache(className).dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStaleMethod(MethodReference method) {
|
||||||
|
return isStaleClass(method.getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item getItemFromCache(String name) {
|
||||||
Item item = cache.get(name);
|
Item item = cache.get(name);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = new Item();
|
item = new Item();
|
||||||
|
@ -59,15 +104,17 @@ public class DiskCachedClassHolderSource implements ClassHolderSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.cls == null) {
|
if (item.cls == null) {
|
||||||
|
item.dirty = true;
|
||||||
item.cls = innerSource.get(name);
|
item.cls = innerSource.get(name);
|
||||||
newClasses.add(name);
|
newClasses.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return item.cls;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Item {
|
private static class Item {
|
||||||
ClassHolder cls;
|
ClassHolder cls;
|
||||||
|
boolean dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -25,39 +25,30 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.ast.AsyncMethodPart;
|
|
||||||
import org.teavm.ast.InvocationExpr;
|
|
||||||
import org.teavm.ast.QualificationExpr;
|
|
||||||
import org.teavm.ast.RecursiveVisitor;
|
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.cache.MethodNodeCache;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.parsing.ClassDateProvider;
|
|
||||||
|
|
||||||
public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
public class DiskMethodNodeCache implements MethodNodeCache {
|
||||||
private final File directory;
|
private final File directory;
|
||||||
private final AstIO astIO;
|
private final AstIO astIO;
|
||||||
private final ClassDateProvider classDateProvider;
|
|
||||||
private final Map<MethodReference, Item> cache = new HashMap<>();
|
private final Map<MethodReference, Item> cache = new HashMap<>();
|
||||||
private final Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
private final Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
||||||
private final Set<MethodReference> newMethods = new HashSet<>();
|
private final Set<MethodReference> newMethods = new HashSet<>();
|
||||||
private final Set<MethodReference> newAsyncMethods = new HashSet<>();
|
private final Set<MethodReference> newAsyncMethods = new HashSet<>();
|
||||||
|
|
||||||
public DiskRegularMethodNodeCache(File directory, SymbolTable symbolTable, SymbolTable fileTable,
|
public DiskMethodNodeCache(File directory, SymbolTable symbolTable, SymbolTable fileTable) {
|
||||||
ClassDateProvider classDateProvider) {
|
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
astIO = new AstIO(symbolTable, fileTable);
|
astIO = new AstIO(symbolTable, fileTable);
|
||||||
this.classDateProvider = classDateProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegularMethodNode get(MethodReference methodReference) {
|
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
Item item = cache.get(methodReference);
|
Item item = cache.get(methodReference);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = new Item();
|
item = new Item();
|
||||||
|
@ -66,7 +57,7 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
DataInput input = new DataInputStream(stream);
|
DataInput input = new DataInputStream(stream);
|
||||||
if (!checkIfDependenciesChanged(input, file)) {
|
if (!checkIfDependenciesChanged(input, cacheStatus)) {
|
||||||
item.node = astIO.read(input, methodReference);
|
item.node = astIO.read(input, methodReference);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -78,15 +69,16 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference methodReference, RegularMethodNode node) {
|
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||||
Item item = new Item();
|
Item item = new Item();
|
||||||
item.node = node;
|
item.node = node;
|
||||||
|
item.dependencies = dependencies.get().clone();
|
||||||
cache.put(methodReference, item);
|
cache.put(methodReference, item);
|
||||||
newMethods.add(methodReference);
|
newMethods.add(methodReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsyncMethodNode getAsync(MethodReference methodReference) {
|
public AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
AsyncItem item = asyncCache.get(methodReference);
|
AsyncItem item = asyncCache.get(methodReference);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = new AsyncItem();
|
item = new AsyncItem();
|
||||||
|
@ -95,7 +87,7 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
DataInput input = new DataInputStream(stream);
|
DataInput input = new DataInputStream(stream);
|
||||||
if (!checkIfDependenciesChanged(input, file)) {
|
if (!checkIfDependenciesChanged(input, cacheStatus)) {
|
||||||
item.node = astIO.readAsync(input, methodReference);
|
item.node = astIO.readAsync(input, methodReference);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -106,12 +98,11 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
return item.node;
|
return item.node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfDependenciesChanged(DataInput input, File file) throws IOException {
|
private boolean checkIfDependenciesChanged(DataInput input, CacheStatus cacheStatus) throws IOException {
|
||||||
int depCount = input.readShort();
|
int depCount = input.readShort();
|
||||||
for (int i = 0; i < depCount; ++i) {
|
for (int i = 0; i < depCount; ++i) {
|
||||||
String depClass = input.readUTF();
|
String depClass = input.readUTF();
|
||||||
Date depDate = classDateProvider.getModificationDate(depClass);
|
if (cacheStatus.isStaleClass(depClass)) {
|
||||||
if (depDate == null || depDate.after(new Date(file.lastModified()))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,9 +110,10 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node) {
|
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> depenencies) {
|
||||||
AsyncItem item = new AsyncItem();
|
AsyncItem item = new AsyncItem();
|
||||||
item.node = node;
|
item.node = node;
|
||||||
|
item.dependencies = depenencies.get().clone();
|
||||||
asyncCache.put(methodReference, item);
|
asyncCache.put(methodReference, item);
|
||||||
newAsyncMethods.add(methodReference);
|
newAsyncMethods.add(methodReference);
|
||||||
}
|
}
|
||||||
|
@ -129,32 +121,24 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
for (MethodReference method : newMethods) {
|
for (MethodReference method : newMethods) {
|
||||||
File file = getMethodFile(method, true);
|
File file = getMethodFile(method, true);
|
||||||
AstDependencyAnalyzer analyzer = new AstDependencyAnalyzer();
|
Item item = cache.get(method);
|
||||||
RegularMethodNode node = cache.get(method).node;
|
|
||||||
node.getBody().acceptVisitor(analyzer);
|
|
||||||
analyzer.dependencies.add(method.getClassName());
|
|
||||||
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
||||||
output.writeShort(analyzer.dependencies.size());
|
output.writeShort(item.dependencies.length);
|
||||||
for (String dependency : analyzer.dependencies) {
|
for (String dependency : item.dependencies) {
|
||||||
output.writeUTF(dependency);
|
output.writeUTF(dependency);
|
||||||
}
|
}
|
||||||
astIO.write(output, node);
|
astIO.write(output, item.node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (MethodReference method : newAsyncMethods) {
|
for (MethodReference method : newAsyncMethods) {
|
||||||
File file = getMethodFile(method, true);
|
File file = getMethodFile(method, true);
|
||||||
AstDependencyAnalyzer analyzer = new AstDependencyAnalyzer();
|
AsyncItem item = asyncCache.get(method);
|
||||||
AsyncMethodNode node = asyncCache.get(method).node;
|
|
||||||
for (AsyncMethodPart part : node.getBody()) {
|
|
||||||
part.getStatement().acceptVisitor(analyzer);
|
|
||||||
}
|
|
||||||
analyzer.dependencies.add(method.getClassName());
|
|
||||||
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
||||||
output.writeShort(analyzer.dependencies.size());
|
output.writeShort(item.dependencies.length);
|
||||||
for (String dependency : analyzer.dependencies) {
|
for (String dependency : item.dependencies) {
|
||||||
output.writeUTF(dependency);
|
output.writeUTF(dependency);
|
||||||
}
|
}
|
||||||
astIO.writeAsync(output, node);
|
astIO.writeAsync(output, item.node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,27 +149,13 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
||||||
+ (async ? "-async" : ""));
|
+ (async ? "-async" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AstDependencyAnalyzer extends RecursiveVisitor {
|
|
||||||
final Set<String> dependencies = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InvocationExpr expr) {
|
|
||||||
super.visit(expr);
|
|
||||||
dependencies.add(expr.getMethod().getClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(QualificationExpr expr) {
|
|
||||||
super.visit(expr);
|
|
||||||
dependencies.add(expr.getField().getClassName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Item {
|
private static class Item {
|
||||||
RegularMethodNode node;
|
RegularMethodNode node;
|
||||||
|
String[] dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AsyncItem {
|
private static class AsyncItem {
|
||||||
AsyncMethodNode node;
|
AsyncMethodNode node;
|
||||||
|
String[] dependencies;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,28 +15,42 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.cache;
|
package org.teavm.cache;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedInputStream;
|
||||||
import java.util.*;
|
import java.io.BufferedOutputStream;
|
||||||
import org.teavm.model.*;
|
import java.io.DataInput;
|
||||||
import org.teavm.model.instructions.*;
|
import java.io.DataInputStream;
|
||||||
import org.teavm.parsing.ClassDateProvider;
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ProgramCache;
|
||||||
|
|
||||||
public class DiskProgramCache implements ProgramCache {
|
public class DiskProgramCache implements ProgramCache {
|
||||||
private File directory;
|
private File directory;
|
||||||
private ProgramIO programIO;
|
private ProgramIO programIO;
|
||||||
private Map<MethodReference, Item> cache = new HashMap<>();
|
private Map<MethodReference, Item> cache = new HashMap<>();
|
||||||
private Set<MethodReference> newMethods = new HashSet<>();
|
private Set<MethodReference> newMethods = new HashSet<>();
|
||||||
private ClassDateProvider classDateProvider;
|
|
||||||
|
|
||||||
public DiskProgramCache(File directory, SymbolTable symbolTable, SymbolTable fileTable,
|
public DiskProgramCache(File directory, SymbolTable symbolTable, SymbolTable fileTable) {
|
||||||
ClassDateProvider classDateProvider) {
|
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
programIO = new ProgramIO(symbolTable, fileTable);
|
programIO = new ProgramIO(symbolTable, fileTable);
|
||||||
this.classDateProvider = classDateProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Program get(MethodReference method) {
|
public Program get(MethodReference method, CacheStatus cacheStatus) {
|
||||||
Item item = cache.get(method);
|
Item item = cache.get(method);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = new Item();
|
item = new Item();
|
||||||
|
@ -49,8 +63,7 @@ public class DiskProgramCache implements ProgramCache {
|
||||||
boolean dependenciesChanged = false;
|
boolean dependenciesChanged = false;
|
||||||
for (int i = 0; i < depCount; ++i) {
|
for (int i = 0; i < depCount; ++i) {
|
||||||
String depClass = input.readUTF();
|
String depClass = input.readUTF();
|
||||||
Date depDate = classDateProvider.getModificationDate(depClass);
|
if (cacheStatus.isStaleClass(depClass)) {
|
||||||
if (depDate == null || depDate.after(new Date(file.lastModified()))) {
|
|
||||||
dependenciesChanged = true;
|
dependenciesChanged = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -67,33 +80,29 @@ public class DiskProgramCache implements ProgramCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference method, Program program) {
|
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
||||||
Item item = new Item();
|
Item item = new Item();
|
||||||
cache.put(method, item);
|
cache.put(method, item);
|
||||||
item.program = program;
|
item.program = program;
|
||||||
|
item.dependencies = dependencies.get().clone();
|
||||||
newMethods.add(method);
|
newMethods.add(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() throws IOException {
|
public void flush(ClassReaderSource classSource) throws IOException {
|
||||||
|
Date currentTime = new Date();
|
||||||
for (MethodReference method : newMethods) {
|
for (MethodReference method : newMethods) {
|
||||||
|
Item item = cache.get(method);
|
||||||
File file = getMethodFile(method);
|
File file = getMethodFile(method);
|
||||||
ProgramDependencyAnalyzer analyzer = new ProgramDependencyAnalyzer();
|
|
||||||
analyzer.dependencies.add(method.getClassName());
|
|
||||||
Program program = cache.get(method).program;
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
for (Instruction insn : block) {
|
|
||||||
insn.acceptVisitor(analyzer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
|
|
||||||
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
|
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||||
DataOutput output = new DataOutputStream(stream);
|
DataOutput output = new DataOutputStream(stream);
|
||||||
output.writeShort(analyzer.dependencies.size());
|
|
||||||
for (String dep : analyzer.dependencies) {
|
output.writeShort(item.dependencies.length);
|
||||||
|
for (String dep : item.dependencies) {
|
||||||
output.writeUTF(dep);
|
output.writeUTF(dep);
|
||||||
}
|
}
|
||||||
programIO.write(program, stream);
|
programIO.write(item.program, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,60 +114,6 @@ public class DiskProgramCache implements ProgramCache {
|
||||||
|
|
||||||
static class Item {
|
static class Item {
|
||||||
Program program;
|
Program program;
|
||||||
}
|
String[] dependencies;
|
||||||
|
|
||||||
static class ProgramDependencyAnalyzer implements InstructionVisitor {
|
|
||||||
Set<String> dependencies = new HashSet<>();
|
|
||||||
@Override public void visit(GetFieldInstruction insn) {
|
|
||||||
dependencies.add(insn.getField().getClassName());
|
|
||||||
}
|
|
||||||
@Override public void visit(PutFieldInstruction insn) {
|
|
||||||
dependencies.add(insn.getField().getClassName());
|
|
||||||
}
|
|
||||||
@Override public void visit(InvokeInstruction insn) {
|
|
||||||
dependencies.add(insn.getMethod().getClassName());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void visit(InvokeDynamicInstruction insn) {
|
|
||||||
for (RuntimeConstant cst : insn.getBootstrapArguments()) {
|
|
||||||
if (cst.getKind() == RuntimeConstant.METHOD_HANDLE) {
|
|
||||||
MethodHandle handle = cst.getMethodHandle();
|
|
||||||
dependencies.add(handle.getClassName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override public void visit(EmptyInstruction insn) { }
|
|
||||||
@Override public void visit(ClassConstantInstruction insn) { }
|
|
||||||
@Override public void visit(NullConstantInstruction insn) { }
|
|
||||||
@Override public void visit(IntegerConstantInstruction insn) { }
|
|
||||||
@Override public void visit(LongConstantInstruction insn) { }
|
|
||||||
@Override public void visit(FloatConstantInstruction insn) { }
|
|
||||||
@Override public void visit(DoubleConstantInstruction insn) { }
|
|
||||||
@Override public void visit(StringConstantInstruction insn) { }
|
|
||||||
@Override public void visit(BinaryInstruction insn) { }
|
|
||||||
@Override public void visit(NegateInstruction insn) { }
|
|
||||||
@Override public void visit(AssignInstruction insn) { }
|
|
||||||
@Override public void visit(CastInstruction insn) { }
|
|
||||||
@Override public void visit(CastNumberInstruction insn) { }
|
|
||||||
@Override public void visit(CastIntegerInstruction insn) { }
|
|
||||||
@Override public void visit(BranchingInstruction insn) { }
|
|
||||||
@Override public void visit(BinaryBranchingInstruction insn) { }
|
|
||||||
@Override public void visit(JumpInstruction insn) { }
|
|
||||||
@Override public void visit(SwitchInstruction insn) { }
|
|
||||||
@Override public void visit(ExitInstruction insn) { }
|
|
||||||
@Override public void visit(RaiseInstruction insn) { }
|
|
||||||
@Override public void visit(ConstructArrayInstruction insn) { }
|
|
||||||
@Override public void visit(ConstructInstruction insn) { }
|
|
||||||
@Override public void visit(ConstructMultiArrayInstruction insn) { }
|
|
||||||
@Override public void visit(ArrayLengthInstruction insn) { }
|
|
||||||
@Override public void visit(CloneArrayInstruction insn) { }
|
|
||||||
@Override public void visit(UnwrapArrayInstruction insn) { }
|
|
||||||
@Override public void visit(GetElementInstruction insn) { }
|
|
||||||
@Override public void visit(PutElementInstruction insn) { }
|
|
||||||
@Override public void visit(IsInstanceInstruction insn) { }
|
|
||||||
@Override public void visit(InitClassInstruction insn) { }
|
|
||||||
@Override public void visit(NullCheckInstruction insn) { }
|
|
||||||
@Override public void visit(MonitorEnterInstruction insn) { }
|
|
||||||
@Override public void visit(MonitorExitInstruction insn) { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,28 +13,34 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.ast.cache;
|
package org.teavm.cache;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class EmptyRegularMethodNodeCache implements MethodNodeCache {
|
public class EmptyMethodNodeCache implements MethodNodeCache {
|
||||||
|
public static final EmptyMethodNodeCache INSTANCE = new EmptyMethodNodeCache();
|
||||||
|
|
||||||
|
private EmptyMethodNodeCache() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegularMethodNode get(MethodReference methodReference) {
|
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference methodReference, RegularMethodNode node) {
|
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsyncMethodNode getAsync(MethodReference methodReference) {
|
public AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node) {
|
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> dependencies) {
|
||||||
}
|
}
|
||||||
}
|
}
|
37
core/src/main/java/org/teavm/cache/EmptyProgramCache.java
vendored
Normal file
37
core/src/main/java/org/teavm/cache/EmptyProgramCache.java
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ProgramCache;
|
||||||
|
|
||||||
|
public class EmptyProgramCache implements ProgramCache {
|
||||||
|
public static final EmptyProgramCache INSTANCE = new EmptyProgramCache();
|
||||||
|
|
||||||
|
private EmptyProgramCache() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Program get(MethodReference method, CacheStatus status) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
||||||
|
}
|
||||||
|
}
|
87
core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java
vendored
Normal file
87
core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
|
import org.teavm.ast.RegularMethodNode;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class InMemoryMethodNodeCache implements MethodNodeCache {
|
||||||
|
private Map<MethodReference, RegularItem> cache = new HashMap<>();
|
||||||
|
private Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
|
RegularItem item = cache.get(methodReference);
|
||||||
|
if (item == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arrays.stream(item.dependencies).anyMatch(cacheStatus::isStaleClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||||
|
cache.put(methodReference, new RegularItem(node, dependencies.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
|
AsyncItem item = asyncCache.get(methodReference);
|
||||||
|
if (item == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arrays.stream(item.dependencies).anyMatch(cacheStatus::isStaleClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> dependencies) {
|
||||||
|
asyncCache.put(methodReference, new AsyncItem(node, dependencies.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class RegularItem {
|
||||||
|
final RegularMethodNode node;
|
||||||
|
final String[] dependencies;
|
||||||
|
|
||||||
|
RegularItem(RegularMethodNode node, String[] dependencies) {
|
||||||
|
this.node = node;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class AsyncItem {
|
||||||
|
final AsyncMethodNode node;
|
||||||
|
final String[] dependencies;
|
||||||
|
|
||||||
|
AsyncItem(AsyncMethodNode node, String[] dependencies) {
|
||||||
|
this.node = node;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
core/src/main/java/org/teavm/cache/InMemoryProgramCache.java
vendored
Normal file
57
core/src/main/java/org/teavm/cache/InMemoryProgramCache.java
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ProgramCache;
|
||||||
|
|
||||||
|
public class InMemoryProgramCache implements ProgramCache {
|
||||||
|
private Map<MethodReference, Item> cache = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Program get(MethodReference method, CacheStatus cacheStatus) {
|
||||||
|
Item item = cache.get(method);
|
||||||
|
if (item == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arrays.stream(item.dependencies).anyMatch(cacheStatus::isStaleClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.program;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
||||||
|
cache.put(method, new Item(program, dependencies.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Item {
|
||||||
|
final Program program;
|
||||||
|
final String[] dependencies;
|
||||||
|
|
||||||
|
Item(Program program, String[] dependencies) {
|
||||||
|
this.program = program;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
core/src/main/java/org/teavm/cache/IncrementalDependencyProvider.java
vendored
Normal file
28
core/src/main/java/org/teavm/cache/IncrementalDependencyProvider.java
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface IncrementalDependencyProvider {
|
||||||
|
boolean isNoCache(String className);
|
||||||
|
|
||||||
|
boolean isNoCache(MethodReference method);
|
||||||
|
|
||||||
|
String[] getDependencies(String className);
|
||||||
|
|
||||||
|
String[] getDependencies(MethodReference method);
|
||||||
|
}
|
28
core/src/main/java/org/teavm/cache/IncrementalDependencyRegistration.java
vendored
Normal file
28
core/src/main/java/org/teavm/cache/IncrementalDependencyRegistration.java
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface IncrementalDependencyRegistration {
|
||||||
|
void setNoCache(String className);
|
||||||
|
|
||||||
|
void setNoCache(MethodReference method);
|
||||||
|
|
||||||
|
void addDependencies(String className, String... dependencies);
|
||||||
|
|
||||||
|
void addDependencies(MethodReference method, String... dependencies);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,18 +13,19 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.ast.cache;
|
package org.teavm.cache;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public interface MethodNodeCache {
|
public interface MethodNodeCache {
|
||||||
RegularMethodNode get(MethodReference methodReference);
|
RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus);
|
||||||
|
|
||||||
void store(MethodReference methodReference, RegularMethodNode node);
|
void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies);
|
||||||
|
|
||||||
AsyncMethodNode getAsync(MethodReference methodReference);
|
AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus);
|
||||||
|
|
||||||
void storeAsync(MethodReference methodReference, AsyncMethodNode node);
|
void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> dependencies);
|
||||||
}
|
}
|
66
core/src/main/java/org/teavm/cache/ProgramDependencyExtractor.java
vendored
Normal file
66
core/src/main/java/org/teavm/cache/ProgramDependencyExtractor.java
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.cache;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.RuntimeConstant;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
|
||||||
|
public class ProgramDependencyExtractor extends AbstractInstructionVisitor {
|
||||||
|
private final AnalyzingVisitor visitor = new AnalyzingVisitor();
|
||||||
|
|
||||||
|
public String[] extractDependencies(Program program) {
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] result = visitor.dependencies.toArray(new String[0]);
|
||||||
|
visitor.dependencies.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnalyzingVisitor extends AbstractInstructionVisitor {
|
||||||
|
Set<String> dependencies = new LinkedHashSet<>();
|
||||||
|
@Override public void visit(GetFieldInstruction insn) {
|
||||||
|
dependencies.add(insn.getField().getClassName());
|
||||||
|
}
|
||||||
|
@Override public void visit(PutFieldInstruction insn) {
|
||||||
|
dependencies.add(insn.getField().getClassName());
|
||||||
|
}
|
||||||
|
@Override public void visit(InvokeInstruction insn) {
|
||||||
|
dependencies.add(insn.getMethod().getClassName());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeDynamicInstruction insn) {
|
||||||
|
for (RuntimeConstant cst : insn.getBootstrapArguments()) {
|
||||||
|
if (cst.getKind() == RuntimeConstant.METHOD_HANDLE) {
|
||||||
|
MethodHandle handle = cst.getMethodHandle();
|
||||||
|
dependencies.add(handle.getClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
core/src/main/java/org/teavm/cache/ProgramIO.java
vendored
137
core/src/main/java/org/teavm/cache/ProgramIO.java
vendored
|
@ -15,14 +15,81 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.cache;
|
package org.teavm.cache;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.model.RuntimeConstant;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryOperation;
|
||||||
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.CastIntegerDirection;
|
||||||
|
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||||
|
import org.teavm.model.instructions.CastNumberInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.EmptyInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.InstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerSubtype;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||||
|
import org.teavm.model.instructions.NegateInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchTableEntry;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
public class ProgramIO {
|
public class ProgramIO {
|
||||||
private SymbolTable symbolTable;
|
private SymbolTable symbolTable;
|
||||||
private SymbolTable fileTable;
|
private SymbolTable fileTable;
|
||||||
|
private ReferenceCache referenceCache = new ReferenceCache();
|
||||||
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
||||||
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
||||||
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
||||||
|
@ -733,7 +800,7 @@ public class ProgramIO {
|
||||||
case 1: {
|
case 1: {
|
||||||
ClassConstantInstruction insn = new ClassConstantInstruction();
|
ClassConstantInstruction insn = new ClassConstantInstruction();
|
||||||
insn.setReceiver(program.variableAt(input.readShort()));
|
insn.setReceiver(program.variableAt(input.readShort()));
|
||||||
insn.setConstant(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setConstant(parseValueType(symbolTable.at(input.readInt())));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
@ -798,7 +865,7 @@ public class ProgramIO {
|
||||||
case 11: {
|
case 11: {
|
||||||
CastInstruction insn = new CastInstruction();
|
CastInstruction insn = new CastInstruction();
|
||||||
insn.setReceiver(program.variableAt(input.readShort()));
|
insn.setReceiver(program.variableAt(input.readShort()));
|
||||||
insn.setTargetType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setTargetType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
insn.setValue(program.variableAt(input.readShort()));
|
insn.setValue(program.variableAt(input.readShort()));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
|
@ -870,7 +937,7 @@ public class ProgramIO {
|
||||||
case 21: {
|
case 21: {
|
||||||
ConstructArrayInstruction insn = new ConstructArrayInstruction();
|
ConstructArrayInstruction insn = new ConstructArrayInstruction();
|
||||||
insn.setReceiver(program.variableAt(input.readShort()));
|
insn.setReceiver(program.variableAt(input.readShort()));
|
||||||
insn.setItemType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setItemType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
insn.setSize(program.variableAt(input.readShort()));
|
insn.setSize(program.variableAt(input.readShort()));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
|
@ -883,7 +950,7 @@ public class ProgramIO {
|
||||||
case 23: {
|
case 23: {
|
||||||
ConstructMultiArrayInstruction insn = new ConstructMultiArrayInstruction();
|
ConstructMultiArrayInstruction insn = new ConstructMultiArrayInstruction();
|
||||||
insn.setReceiver(program.variableAt(input.readShort()));
|
insn.setReceiver(program.variableAt(input.readShort()));
|
||||||
insn.setItemType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setItemType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
int dimensionCount = input.readByte();
|
int dimensionCount = input.readByte();
|
||||||
for (int i = 0; i < dimensionCount; ++i) {
|
for (int i = 0; i < dimensionCount; ++i) {
|
||||||
insn.getDimensions().add(program.variableAt(input.readShort()));
|
insn.getDimensions().add(program.variableAt(input.readShort()));
|
||||||
|
@ -897,7 +964,7 @@ public class ProgramIO {
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
String fieldName = symbolTable.at(input.readInt());
|
String fieldName = symbolTable.at(input.readInt());
|
||||||
insn.setField(new FieldReference(className, fieldName));
|
insn.setField(new FieldReference(className, fieldName));
|
||||||
insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setFieldType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
case 25: {
|
case 25: {
|
||||||
|
@ -906,7 +973,7 @@ public class ProgramIO {
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
String fieldName = symbolTable.at(input.readInt());
|
String fieldName = symbolTable.at(input.readInt());
|
||||||
insn.setField(new FieldReference(className, fieldName));
|
insn.setField(new FieldReference(className, fieldName));
|
||||||
insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setFieldType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
case 26: {
|
case 26: {
|
||||||
|
@ -914,7 +981,7 @@ public class ProgramIO {
|
||||||
insn.setInstance(program.variableAt(input.readShort()));
|
insn.setInstance(program.variableAt(input.readShort()));
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
String fieldName = symbolTable.at(input.readInt());
|
String fieldName = symbolTable.at(input.readInt());
|
||||||
ValueType type = ValueType.parse(symbolTable.at(input.readInt()));
|
ValueType type = parseValueType(symbolTable.at(input.readInt()));
|
||||||
insn.setField(new FieldReference(className, fieldName));
|
insn.setField(new FieldReference(className, fieldName));
|
||||||
insn.setValue(program.variableAt(input.readShort()));
|
insn.setValue(program.variableAt(input.readShort()));
|
||||||
insn.setFieldType(type);
|
insn.setFieldType(type);
|
||||||
|
@ -924,7 +991,7 @@ public class ProgramIO {
|
||||||
PutFieldInstruction insn = new PutFieldInstruction();
|
PutFieldInstruction insn = new PutFieldInstruction();
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
String fieldName = symbolTable.at(input.readInt());
|
String fieldName = symbolTable.at(input.readInt());
|
||||||
ValueType type = ValueType.parse(symbolTable.at(input.readInt()));
|
ValueType type = parseValueType(symbolTable.at(input.readInt()));
|
||||||
insn.setField(new FieldReference(className, fieldName));
|
insn.setField(new FieldReference(className, fieldName));
|
||||||
insn.setValue(program.variableAt(input.readShort()));
|
insn.setValue(program.variableAt(input.readShort()));
|
||||||
insn.setFieldType(type);
|
insn.setFieldType(type);
|
||||||
|
@ -969,8 +1036,8 @@ public class ProgramIO {
|
||||||
int receiverIndex = input.readShort();
|
int receiverIndex = input.readShort();
|
||||||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||||
insn.setMethod(new MethodReference(className, methodDesc));
|
insn.setMethod(createMethodReference(className, methodDesc));
|
||||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||||
for (int i = 0; i < paramCount; ++i) {
|
for (int i = 0; i < paramCount; ++i) {
|
||||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||||
|
@ -984,8 +1051,8 @@ public class ProgramIO {
|
||||||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||||
insn.setInstance(program.variableAt(input.readShort()));
|
insn.setInstance(program.variableAt(input.readShort()));
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||||
insn.setMethod(new MethodReference(className, methodDesc));
|
insn.setMethod(createMethodReference(className, methodDesc));
|
||||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||||
for (int i = 0; i < paramCount; ++i) {
|
for (int i = 0; i < paramCount; ++i) {
|
||||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||||
|
@ -999,8 +1066,8 @@ public class ProgramIO {
|
||||||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||||
insn.setInstance(program.variableAt(input.readShort()));
|
insn.setInstance(program.variableAt(input.readShort()));
|
||||||
String className = symbolTable.at(input.readInt());
|
String className = symbolTable.at(input.readInt());
|
||||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||||
insn.setMethod(new MethodReference(className, methodDesc));
|
insn.setMethod(createMethodReference(className, methodDesc));
|
||||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||||
for (int i = 0; i < paramCount; ++i) {
|
for (int i = 0; i < paramCount; ++i) {
|
||||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||||
|
@ -1010,7 +1077,7 @@ public class ProgramIO {
|
||||||
case 36: {
|
case 36: {
|
||||||
IsInstanceInstruction insn = new IsInstanceInstruction();
|
IsInstanceInstruction insn = new IsInstanceInstruction();
|
||||||
insn.setReceiver(program.variableAt(input.readShort()));
|
insn.setReceiver(program.variableAt(input.readShort()));
|
||||||
insn.setType(ValueType.parse(symbolTable.at(input.readInt())));
|
insn.setType(parseValueType(symbolTable.at(input.readInt())));
|
||||||
insn.setValue(program.variableAt(input.readShort()));
|
insn.setValue(program.variableAt(input.readShort()));
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
|
@ -1041,7 +1108,7 @@ public class ProgramIO {
|
||||||
short instance = input.readShort();
|
short instance = input.readShort();
|
||||||
insn.setReceiver(receiver >= 0 ? program.variableAt(receiver) : null);
|
insn.setReceiver(receiver >= 0 ? program.variableAt(receiver) : null);
|
||||||
insn.setInstance(instance >= 0 ? program.variableAt(instance) : null);
|
insn.setInstance(instance >= 0 ? program.variableAt(instance) : null);
|
||||||
insn.setMethod(MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
insn.setMethod(parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
int argsCount = insn.getMethod().parameterCount();
|
int argsCount = insn.getMethod().parameterCount();
|
||||||
for (int i = 0; i < argsCount; ++i) {
|
for (int i = 0; i < argsCount; ++i) {
|
||||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||||
|
@ -1063,31 +1130,31 @@ public class ProgramIO {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case 0:
|
case 0:
|
||||||
return MethodHandle.fieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
return MethodHandle.fieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||||
ValueType.parse(symbolTable.at(input.readInt())));
|
parseValueType(symbolTable.at(input.readInt())));
|
||||||
case 1:
|
case 1:
|
||||||
return MethodHandle.staticFieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
return MethodHandle.staticFieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||||
ValueType.parse(symbolTable.at(input.readInt())));
|
parseValueType(symbolTable.at(input.readInt())));
|
||||||
case 2:
|
case 2:
|
||||||
return MethodHandle.fieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
return MethodHandle.fieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||||
ValueType.parse(symbolTable.at(input.readInt())));
|
parseValueType(symbolTable.at(input.readInt())));
|
||||||
case 3:
|
case 3:
|
||||||
return MethodHandle.staticFieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
return MethodHandle.staticFieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||||
ValueType.parse(symbolTable.at(input.readInt())));
|
parseValueType(symbolTable.at(input.readInt())));
|
||||||
case 4:
|
case 4:
|
||||||
return MethodHandle.virtualCaller(symbolTable.at(input.readInt()),
|
return MethodHandle.virtualCaller(symbolTable.at(input.readInt()),
|
||||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
case 5:
|
case 5:
|
||||||
return MethodHandle.staticCaller(symbolTable.at(input.readInt()),
|
return MethodHandle.staticCaller(symbolTable.at(input.readInt()),
|
||||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
case 6:
|
case 6:
|
||||||
return MethodHandle.specialCaller(symbolTable.at(input.readInt()),
|
return MethodHandle.specialCaller(symbolTable.at(input.readInt()),
|
||||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
case 7:
|
case 7:
|
||||||
return MethodHandle.constructorCaller(symbolTable.at(input.readInt()),
|
return MethodHandle.constructorCaller(symbolTable.at(input.readInt()),
|
||||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
case 8:
|
case 8:
|
||||||
return MethodHandle.interfaceCaller(symbolTable.at(input.readInt()),
|
return MethodHandle.interfaceCaller(symbolTable.at(input.readInt()),
|
||||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected method handle type: " + kind);
|
throw new IllegalArgumentException("Unexpected method handle type: " + kind);
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1174,7 @@ public class ProgramIO {
|
||||||
case 4:
|
case 4:
|
||||||
return new RuntimeConstant(input.readUTF());
|
return new RuntimeConstant(input.readUTF());
|
||||||
case 5:
|
case 5:
|
||||||
return new RuntimeConstant(ValueType.parse(symbolTable.at(input.readInt())));
|
return new RuntimeConstant(parseValueType(symbolTable.at(input.readInt())));
|
||||||
case 6:
|
case 6:
|
||||||
return new RuntimeConstant(MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
return new RuntimeConstant(MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||||
case 7:
|
case 7:
|
||||||
|
@ -1116,4 +1183,16 @@ public class ProgramIO {
|
||||||
throw new IllegalArgumentException("Unexpected runtime constant type: " + kind);
|
throw new IllegalArgumentException("Unexpected runtime constant type: " + kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodDescriptor parseMethodDescriptor(String key) {
|
||||||
|
return referenceCache.parseDescriptorCached(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueType parseValueType(String key) {
|
||||||
|
return referenceCache.parseValueTypeCached(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodReference createMethodReference(String className, MethodDescriptor method) {
|
||||||
|
return referenceCache.getCached(className, method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class DefaultCallGraph implements CallGraph, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultCallGraphNode getNode(MethodReference method) {
|
public DefaultCallGraphNode getNode(MethodReference method) {
|
||||||
return nodes.computeIfAbsent(method, k -> new DefaultCallGraphNode(this, method));
|
return nodes.computeIfAbsent(method, k -> new DefaultCallGraphNode(this, k));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.teavm.model.TextLocation;
|
||||||
public class DefaultCallGraphNode implements CallGraphNode {
|
public class DefaultCallGraphNode implements CallGraphNode {
|
||||||
private DefaultCallGraph graph;
|
private DefaultCallGraph graph;
|
||||||
private MethodReference method;
|
private MethodReference method;
|
||||||
private Set<DefaultCallSite> callSites = new LinkedHashSet<>();
|
private Set<DefaultCallSite> callSites = new LinkedHashSet<>(10, 0.5f);
|
||||||
private Set<DefaultCallSite> safeCallSites;
|
private Set<DefaultCallSite> safeCallSites;
|
||||||
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
||||||
private List<DefaultCallSite> safeCallersCallSites;
|
private List<DefaultCallSite> safeCallersCallSites;
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(location, callee, field);
|
return Objects.hash(location, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,6 @@ public class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DefaultFieldAccessSite other = (DefaultFieldAccessSite) obj;
|
DefaultFieldAccessSite other = (DefaultFieldAccessSite) obj;
|
||||||
return Objects.equals(location, other.location) && Objects.equals(callee, other.callee)
|
return Objects.equals(location, other.location) && Objects.equals(field, other.field);
|
||||||
&& Objects.equals(field, other.field);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
core/src/main/java/org/teavm/common/OptionalPredicate.java
Normal file
20
core/src/main/java/org/teavm/common/OptionalPredicate.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
public interface OptionalPredicate<T> {
|
||||||
|
boolean test(T value, boolean defaultResult);
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import org.teavm.common.RecordArray;
|
||||||
import org.teavm.common.RecordArrayBuilder;
|
import org.teavm.common.RecordArrayBuilder;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
|
||||||
public class DebugInformation {
|
public class DebugInformation {
|
||||||
String[] fileNames;
|
String[] fileNames;
|
||||||
|
@ -50,6 +51,7 @@ public class DebugInformation {
|
||||||
Map<String, ClassMetadata> classMetadataByJsName;
|
Map<String, ClassMetadata> classMetadataByJsName;
|
||||||
RecordArray methodEntrances;
|
RecordArray methodEntrances;
|
||||||
MethodTree methodTree;
|
MethodTree methodTree;
|
||||||
|
ReferenceCache referenceCache = new ReferenceCache();
|
||||||
|
|
||||||
public String[] getFilesNames() {
|
public String[] getFilesNames() {
|
||||||
return fileNames.clone();
|
return fileNames.clone();
|
||||||
|
@ -82,13 +84,13 @@ public class DebugInformation {
|
||||||
public MethodDescriptor[] getMethods() {
|
public MethodDescriptor[] getMethods() {
|
||||||
MethodDescriptor[] descriptors = new MethodDescriptor[methods.length];
|
MethodDescriptor[] descriptors = new MethodDescriptor[methods.length];
|
||||||
for (int i = 0; i < descriptors.length; ++i) {
|
for (int i = 0; i < descriptors.length; ++i) {
|
||||||
descriptors[i] = MethodDescriptor.parse(methods[i]);
|
descriptors[i] = referenceCache.parseDescriptorCached(methods[i]);
|
||||||
}
|
}
|
||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodDescriptor getMethod(int methodId) {
|
public MethodDescriptor getMethod(int methodId) {
|
||||||
return MethodDescriptor.parse(methods[methodId]);
|
return referenceCache.parseDescriptorCached(methods[methodId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference[] getExactMethods() {
|
public MethodReference[] getExactMethods() {
|
||||||
|
@ -115,7 +117,8 @@ public class DebugInformation {
|
||||||
long item = exactMethods[index];
|
long item = exactMethods[index];
|
||||||
int classIndex = (int) (item >>> 32);
|
int classIndex = (int) (item >>> 32);
|
||||||
int methodIndex = (int) item;
|
int methodIndex = (int) item;
|
||||||
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
|
return referenceCache.getCached(classNames[classIndex], referenceCache.parseDescriptorCached(
|
||||||
|
methods[methodIndex]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getExactMethodId(int classNameId, int methodId) {
|
public int getExactMethodId(int classNameId, int methodId) {
|
||||||
|
@ -186,7 +189,7 @@ public class DebugInformation {
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new MethodReference(className, MethodDescriptor.parse(method));
|
return referenceCache.getCached(className, referenceCache.parseDescriptorCached(method));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference getMethodAt(int line, int column) {
|
public MethodReference getMethodAt(int line, int column) {
|
||||||
|
@ -678,8 +681,8 @@ public class DebugInformation {
|
||||||
long item = exactMethods[data[start + i]];
|
long item = exactMethods[data[start + i]];
|
||||||
int classIndex = (int) (item >>> 32);
|
int classIndex = (int) (item >>> 32);
|
||||||
int methodIndex = (int) item;
|
int methodIndex = (int) item;
|
||||||
references[i] = new MethodReference(classNames[classIndex],
|
references[i] = referenceCache.getCached(classNames[classIndex],
|
||||||
MethodDescriptor.parse(methods[methodIndex]));
|
referenceCache.parseDescriptorCached(methods[methodIndex]));
|
||||||
}
|
}
|
||||||
return references;
|
return references;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
|
|
||||||
public abstract class AbstractDependencyListener implements DependencyListener {
|
public abstract class AbstractDependencyListener implements DependencyListener {
|
||||||
@Override
|
@Override
|
||||||
public void started(DependencyAgent agent) {
|
public void started(DependencyAgent agent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
public void classReached(DependencyAgent agent, String className) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location) {
|
public void fieldReached(DependencyAgent agent, FieldDependency field) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.RuntimeConstant;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.VariableReader;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
|
||||||
|
abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
|
||||||
|
private static final MethodReference STRING_INIT_FROM_CHARS_METHOD = new MethodReference(String.class,
|
||||||
|
"<init>", char[].class, void.class);
|
||||||
|
static final MethodReference CLONE_METHOD = new MethodReference(Object.class, "clone", Object.class);
|
||||||
|
private static final MethodReference NPE_INIT_METHOD = new MethodReference(NullPointerException.class,
|
||||||
|
"<init>", void.class);
|
||||||
|
static final MethodReference MONITOR_ENTER_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorEnter", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_ENTER_SYNC_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorEnterSync", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_EXIT_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorExit", Object.class, void.class);
|
||||||
|
static final MethodReference MONITOR_EXIT_SYNC_METHOD = new MethodReference(Object.class,
|
||||||
|
"monitorExitSync", Object.class, void.class);
|
||||||
|
|
||||||
|
protected TextLocation location;
|
||||||
|
protected MethodReference caller;
|
||||||
|
protected CallLocation callLocation;
|
||||||
|
|
||||||
|
public void setCaller(MethodReference caller) {
|
||||||
|
this.caller = caller;
|
||||||
|
callLocation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void location(TextLocation location) {
|
||||||
|
if (!Objects.equals(this.location, location)) {
|
||||||
|
this.location = location;
|
||||||
|
callLocation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void classConstant(VariableReader receiver, ValueType cst) {
|
||||||
|
DependencyNode node = getNode(receiver);
|
||||||
|
if (node != null) {
|
||||||
|
node.propagate(getAnalyzer().getType("java.lang.Class"));
|
||||||
|
if (!(cst instanceof ValueType.Primitive)) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (cst instanceof ValueType.Object) {
|
||||||
|
sb.append(((ValueType.Object) cst).getClassName());
|
||||||
|
} else {
|
||||||
|
sb.append(cst.toString());
|
||||||
|
}
|
||||||
|
node.getClassValueNode().propagate(getAnalyzer().getType(sb.toString()));
|
||||||
|
} else {
|
||||||
|
node.getClassValueNode().propagate(getAnalyzer().getType("~" + cst.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (cst instanceof ValueType.Array) {
|
||||||
|
cst = ((ValueType.Array) cst).getItemType();
|
||||||
|
}
|
||||||
|
if (cst instanceof ValueType.Object) {
|
||||||
|
String className = ((ValueType.Object) cst).getClassName();
|
||||||
|
getAnalyzer().linkClass(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stringConstant(VariableReader receiver, String cst) {
|
||||||
|
DependencyNode node = getNode(receiver);
|
||||||
|
if (node != null) {
|
||||||
|
node.propagate(getAnalyzer().getType("java.lang.String"));
|
||||||
|
}
|
||||||
|
MethodDependency method = getAnalyzer().linkMethod(STRING_INIT_FROM_CHARS_METHOD);
|
||||||
|
method.addLocation(getCallLocation());
|
||||||
|
method.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
|
||||||
|
DependencyNode node = getNode(receiver);
|
||||||
|
if (node != null) {
|
||||||
|
node.propagate(getAnalyzer().getType("[" + itemType));
|
||||||
|
}
|
||||||
|
String className = extractClassName(itemType);
|
||||||
|
if (className != null) {
|
||||||
|
getAnalyzer().linkClass(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createArray(VariableReader receiver, ValueType itemType, List<? extends VariableReader> dimensions) {
|
||||||
|
DependencyNode node = getNode(receiver);
|
||||||
|
for (int i = 0; i < dimensions.size(); ++i) {
|
||||||
|
if (node == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String itemTypeStr;
|
||||||
|
if (itemType instanceof ValueType.Object) {
|
||||||
|
itemTypeStr = ((ValueType.Object) itemType).getClassName();
|
||||||
|
} else {
|
||||||
|
itemTypeStr = itemType.toString();
|
||||||
|
}
|
||||||
|
node.propagate(getAnalyzer().getType(itemTypeStr));
|
||||||
|
node = node.getArrayItem();
|
||||||
|
itemType = ((ValueType.Array) itemType).getItemType();
|
||||||
|
}
|
||||||
|
String className = extractClassName(itemType);
|
||||||
|
if (className != null) {
|
||||||
|
getAnalyzer().linkClass(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final String extractClassName(ValueType itemType) {
|
||||||
|
while (itemType instanceof ValueType.Array) {
|
||||||
|
itemType = ((ValueType.Array) itemType).getItemType();
|
||||||
|
}
|
||||||
|
return itemType instanceof ValueType.Object ? ((ValueType.Object) itemType).getClassName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(VariableReader receiver, String type) {
|
||||||
|
getAnalyzer().linkClass(type);
|
||||||
|
DependencyNode node = getNode(receiver);
|
||||||
|
if (node != null) {
|
||||||
|
node.propagate(getAnalyzer().getType(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
|
||||||
|
ValueType fieldType) {
|
||||||
|
FieldDependency fieldDep = getAnalyzer().linkField(field);
|
||||||
|
fieldDep.addLocation(getCallLocation());
|
||||||
|
if (!(fieldType instanceof ValueType.Primitive)) {
|
||||||
|
DependencyNode receiverNode = getNode(receiver);
|
||||||
|
if (receiverNode != null) {
|
||||||
|
fieldDep.getValue().connect(receiverNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initClass(field.getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putField(VariableReader instance, FieldReference field, VariableReader value,
|
||||||
|
ValueType fieldType) {
|
||||||
|
FieldDependency fieldDep = getAnalyzer().linkField(field);
|
||||||
|
fieldDep.addLocation(getCallLocation());
|
||||||
|
if (!(fieldType instanceof ValueType.Primitive)) {
|
||||||
|
DependencyNode valueNode = getNode(value);
|
||||||
|
if (valueNode != null) {
|
||||||
|
valueNode.connect(fieldDep.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initClass(field.getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments, InvocationType type) {
|
||||||
|
if (instance == null) {
|
||||||
|
invokeSpecial(receiver, null, method, arguments);
|
||||||
|
} else {
|
||||||
|
switch (type) {
|
||||||
|
case SPECIAL:
|
||||||
|
invokeSpecial(receiver, instance, method, arguments);
|
||||||
|
break;
|
||||||
|
case VIRTUAL:
|
||||||
|
invokeVirtual(receiver, instance, method, arguments);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (method.getName().equals("getClass") && method.parameterCount() == 0
|
||||||
|
&& method.getReturnType().isObject(Class.class) && receiver != null) {
|
||||||
|
getNode(instance).connect(getNode(receiver).getClassValueNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments);
|
||||||
|
|
||||||
|
protected abstract void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||||
|
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||||
|
List<RuntimeConstant> bootstrapArguments) {
|
||||||
|
// Should be eliminated by processInvokeDynamic method
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||||
|
String className = extractClassName(type);
|
||||||
|
if (className != null) {
|
||||||
|
getAnalyzer().linkClass(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initClass(String className) {
|
||||||
|
getAnalyzer().linkClass(className).initClass(getCallLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nullCheck(VariableReader receiver, VariableReader value) {
|
||||||
|
DependencyNode valueNode = getNode(value);
|
||||||
|
DependencyNode receiverNode = getNode(receiver);
|
||||||
|
if (valueNode != null) {
|
||||||
|
valueNode.connect(receiverNode);
|
||||||
|
}
|
||||||
|
MethodDependency npeMethod = getAnalyzer().linkMethod(NPE_INIT_METHOD);
|
||||||
|
npeMethod.addLocation(getCallLocation());
|
||||||
|
npeMethod.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void monitorEnter(VariableReader objectRef) {
|
||||||
|
if (getAnalyzer().asyncSupported) {
|
||||||
|
MethodDependency methodDep = getAnalyzer().linkMethod(MONITOR_ENTER_METHOD);
|
||||||
|
methodDep.addLocation(getCallLocation());
|
||||||
|
getNode(objectRef).connect(methodDep.getVariable(1));
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDependency methodDep = getAnalyzer().linkMethod(MONITOR_ENTER_SYNC_METHOD);
|
||||||
|
methodDep.addLocation(getCallLocation());
|
||||||
|
getNode(objectRef).connect(methodDep.getVariable(1));
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void monitorExit(VariableReader objectRef) {
|
||||||
|
if (getAnalyzer().asyncSupported) {
|
||||||
|
MethodDependency methodDep = getAnalyzer().linkMethod(MONITOR_EXIT_METHOD);
|
||||||
|
methodDep.addLocation(getCallLocation());
|
||||||
|
getNode(objectRef).connect(methodDep.getVariable(1));
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDependency methodDep = getAnalyzer().linkMethod(MONITOR_EXIT_SYNC_METHOD);
|
||||||
|
methodDep.addLocation(getCallLocation());
|
||||||
|
getNode(objectRef).connect(methodDep.getVariable(1));
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DependencyNode getNode(VariableReader variable);
|
||||||
|
|
||||||
|
protected abstract DependencyAnalyzer getAnalyzer();
|
||||||
|
|
||||||
|
protected CallLocation getCallLocation() {
|
||||||
|
if (callLocation == null) {
|
||||||
|
callLocation = new CallLocation(caller, location);
|
||||||
|
}
|
||||||
|
return callLocation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ public class ClassDependency implements ClassDependencyInfo {
|
||||||
private DependencyAnalyzer analyzer;
|
private DependencyAnalyzer analyzer;
|
||||||
private String className;
|
private String className;
|
||||||
private ClassReader classReader;
|
private ClassReader classReader;
|
||||||
|
boolean activated;
|
||||||
|
|
||||||
ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) {
|
ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) {
|
||||||
this.analyzer = analyzer;
|
this.analyzer = analyzer;
|
||||||
|
@ -43,9 +44,9 @@ public class ClassDependency implements ClassDependencyInfo {
|
||||||
return classReader;
|
return classReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initClass(CallLocation callLocation) {
|
public void initClass(CallLocation location) {
|
||||||
if (!isMissing()) {
|
if (!isMissing()) {
|
||||||
analyzer.initClass(this, callLocation);
|
analyzer.initClass(this, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
@ -52,16 +53,20 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
||||||
analyzer.submitMethod(method, program);
|
analyzer.submitMethod(method, program);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
|
public MethodDependency linkMethod(MethodReference methodRef) {
|
||||||
return analyzer.linkMethod(methodRef, callLocation);
|
return analyzer.linkMethod(methodRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
public MethodDependency linkMethod(String className, MethodDescriptor descriptor) {
|
||||||
return analyzer.linkClass(className, callLocation);
|
return analyzer.linkMethod(className, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldDependency linkField(FieldReference fieldRef, CallLocation callLocation) {
|
public ClassDependency linkClass(String className) {
|
||||||
return analyzer.linkField(fieldRef, callLocation);
|
return analyzer.linkClass(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldDependency linkField(FieldReference fieldRef) {
|
||||||
|
return analyzer.linkField(fieldRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Diagnostics getDiagnostics() {
|
public Diagnostics getDiagnostics() {
|
||||||
|
@ -83,6 +88,11 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
||||||
return analyzer.getClassLoader();
|
return analyzer.getClassLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassHierarchy getClassHierarchy() {
|
||||||
|
return analyzer.getClassHierarchy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MethodReference> getReachableMethods() {
|
public Collection<MethodReference> getReachableMethods() {
|
||||||
return analyzer.getReachableMethods();
|
return analyzer.getReachableMethods();
|
||||||
|
@ -122,4 +132,8 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
||||||
public CallGraph getCallGraph() {
|
public CallGraph getCallGraph() {
|
||||||
return analyzer.getCallGraph();
|
return analyzer.getCallGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IncrementalDependencyRegistration getIncrementalCache() {
|
||||||
|
return analyzer.incrementalCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,17 +20,24 @@ import com.carrotsearch.hppc.IntSet;
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.teavm.cache.IncrementalDependencyProvider;
|
||||||
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
|
import org.teavm.callgraph.CallGraphNode;
|
||||||
import org.teavm.callgraph.DefaultCallGraph;
|
import org.teavm.callgraph.DefaultCallGraph;
|
||||||
import org.teavm.common.CachedMapper;
|
import org.teavm.common.CachedMapper;
|
||||||
import org.teavm.common.Mapper;
|
import org.teavm.common.Mapper;
|
||||||
|
@ -38,7 +45,9 @@ import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.PlatformMarker;
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -47,6 +56,8 @@ import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
@ -54,23 +65,32 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.ReferenceCache;
|
import org.teavm.model.ReferenceCache;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.emit.ProgramEmitter;
|
||||||
|
import org.teavm.model.emit.ValueEmitter;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
import org.teavm.model.util.BasicBlockSplitter;
|
||||||
import org.teavm.model.util.ModelUtils;
|
import org.teavm.model.util.ModelUtils;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
import org.teavm.parsing.Parser;
|
import org.teavm.parsing.Parser;
|
||||||
|
|
||||||
public class DependencyAnalyzer implements DependencyInfo {
|
public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
private static final int PROPAGATION_STACK_THRESHOLD = 50;
|
private static final int PROPAGATION_STACK_THRESHOLD = 50;
|
||||||
|
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", void.class);
|
||||||
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
|
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
|
||||||
static final boolean shouldTag = System.getProperty("org.teavm.tagDependencies", "false").equals("true")
|
static final boolean shouldTag = System.getProperty("org.teavm.tagDependencies", "false").equals("true")
|
||||||
|| shouldLog;
|
|| shouldLog;
|
||||||
static final boolean dependencyReport = System.getProperty("org.teavm.dependencyReport", "false").equals("true");
|
static final boolean dependencyReport = System.getProperty("org.teavm.dependencyReport", "false").equals("true");
|
||||||
private int classNameSuffix;
|
private int classNameSuffix;
|
||||||
|
private ClassReaderSource unprocessedClassSource;
|
||||||
private DependencyClassSource classSource;
|
private DependencyClassSource classSource;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private Mapper<MethodReference, MethodHolder> methodReaderCache;
|
private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f);
|
||||||
private Mapper<FieldReference, FieldHolder> fieldReaderCache;
|
private Mapper<FieldReference, FieldHolder> fieldReaderCache;
|
||||||
private CachedMapper<MethodReference, MethodDependency> methodCache;
|
private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>();
|
||||||
|
private Set<MethodReference> reachedMethods = new LinkedHashSet<>();
|
||||||
|
private Set<MethodReference> readonlyReachedMethods = Collections.unmodifiableSet(reachedMethods);
|
||||||
private CachedMapper<FieldReference, FieldDependency> fieldCache;
|
private CachedMapper<FieldReference, FieldDependency> fieldCache;
|
||||||
private CachedMapper<String, ClassDependency> classCache;
|
private CachedMapper<String, ClassDependency> classCache;
|
||||||
private List<DependencyListener> listeners = new ArrayList<>();
|
private List<DependencyListener> listeners = new ArrayList<>();
|
||||||
|
@ -90,23 +110,19 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
private boolean completing;
|
private boolean completing;
|
||||||
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
||||||
private List<DependencyNode> allNodes = new ArrayList<>();
|
private List<DependencyNode> allNodes = new ArrayList<>();
|
||||||
|
private ClassHierarchy classHierarchy;
|
||||||
|
IncrementalCache incrementalCache = new IncrementalCache();
|
||||||
boolean asyncSupported;
|
boolean asyncSupported;
|
||||||
|
|
||||||
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||||
Diagnostics diagnostics) {
|
Diagnostics diagnostics) {
|
||||||
|
unprocessedClassSource = classSource;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
this.classSource = new DependencyClassSource(classSource, diagnostics);
|
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache);
|
||||||
|
classHierarchy = new ClassHierarchy(this.classSource);
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.services = services;
|
this.services = services;
|
||||||
methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutableImplementation(preimage));
|
|
||||||
fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage));
|
fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage));
|
||||||
methodCache = new CachedMapper<>(preimage -> {
|
|
||||||
MethodHolder method = methodReaderCache.map(preimage);
|
|
||||||
if (method != null && !method.getReference().equals(preimage)) {
|
|
||||||
return methodCache.map(method.getReference());
|
|
||||||
}
|
|
||||||
return createMethodDep(preimage, method);
|
|
||||||
});
|
|
||||||
fieldCache = new CachedMapper<>(preimage -> {
|
fieldCache = new CachedMapper<>(preimage -> {
|
||||||
FieldReader field = fieldReaderCache.map(preimage);
|
FieldReader field = fieldReaderCache.map(preimage);
|
||||||
if (field != null && !field.getReference().equals(preimage)) {
|
if (field != null && !field.getReference().equals(preimage)) {
|
||||||
|
@ -150,10 +166,32 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
type = new DependencyType(this, name, types.size());
|
type = new DependencyType(this, name, types.size());
|
||||||
types.add(type);
|
types.add(type);
|
||||||
typeMap.put(name, type);
|
typeMap.put(name, type);
|
||||||
|
|
||||||
|
if (!name.startsWith("[") && !name.startsWith("~")) {
|
||||||
|
markSupertypesAsHavingSubtypes(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void markSupertypesAsHavingSubtypes(String name) {
|
||||||
|
ClassReader cls = unprocessedClassSource.get(name);
|
||||||
|
if (cls == null) {
|
||||||
|
cls = classSource.get(name);
|
||||||
|
if (cls == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
getType(cls.getParent()).subtypeExists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
getType(itf).subtypeExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DependencyNode createNode() {
|
public DependencyNode createNode() {
|
||||||
return createNode(null);
|
return createNode(null);
|
||||||
}
|
}
|
||||||
|
@ -172,6 +210,15 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
return classSource;
|
return classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSynthesizedClass(String className) {
|
||||||
|
return classSource.isGeneratedClass(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassHierarchy getClassHierarchy() {
|
||||||
|
return classHierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassLoader getClassLoader() {
|
public ClassLoader getClassLoader() {
|
||||||
return classLoader;
|
return classLoader;
|
||||||
|
@ -231,8 +278,8 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
dep.used = false;
|
dep.used = false;
|
||||||
lock(dep, false);
|
lock(dep, false);
|
||||||
deferredTasks.add(() -> {
|
deferredTasks.add(() -> {
|
||||||
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyAnalyzer.this);
|
processInvokeDynamic(dep);
|
||||||
graphBuilder.buildGraph(dep);
|
processMethod(dep);
|
||||||
dep.used = true;
|
dep.used = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -240,6 +287,8 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void processMethod(MethodDependency methodDep);
|
||||||
|
|
||||||
public void addDependencyListener(DependencyListener listener) {
|
public void addDependencyListener(DependencyListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
listener.started(agent);
|
listener.started(agent);
|
||||||
|
@ -254,7 +303,7 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
if (parameters.length + 1 != argumentTypes.length) {
|
if (parameters.length + 1 != argumentTypes.length) {
|
||||||
throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments");
|
throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments");
|
||||||
}
|
}
|
||||||
MethodDependency method = linkMethod(methodRef, null);
|
MethodDependency method = linkMethod(methodRef);
|
||||||
method.use();
|
method.use();
|
||||||
DependencyNode[] varNodes = method.getVariables();
|
DependencyNode[] varNodes = method.getVariables();
|
||||||
varNodes[0].propagate(getType(methodRef.getClassName()));
|
varNodes[0].propagate(getType(methodRef.getClassName()));
|
||||||
|
@ -344,34 +393,33 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> classesAddedByRoot = new HashSet<>();
|
|
||||||
|
|
||||||
public void defer(Runnable task) {
|
public void defer(Runnable task) {
|
||||||
deferredTasks.add(task);
|
deferredTasks.add(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
public ClassDependency linkClass(String className) {
|
||||||
if (completing && getClass(className) == null) {
|
if (completing && getClass(className) == null) {
|
||||||
throw new IllegalStateException("Can't link class during completion phase");
|
throw new IllegalStateException("Can't link class during completion phase");
|
||||||
}
|
}
|
||||||
ClassDependency dep = classCache.map(className);
|
ClassDependency dep = classCache.map(className);
|
||||||
boolean added = true;
|
if (!dep.activated) {
|
||||||
if (callLocation == null || callLocation.getMethod() == null) {
|
dep.activated = true;
|
||||||
added = classesAddedByRoot.add(className);
|
if (!dep.isMissing()) {
|
||||||
}
|
deferredTasks.add(() -> {
|
||||||
if (!dep.isMissing() && added) {
|
for (DependencyListener listener : listeners) {
|
||||||
deferredTasks.add(() -> {
|
listener.classReached(agent, className);
|
||||||
for (DependencyListener listener : listeners) {
|
}
|
||||||
listener.classReached(agent, className, callLocation);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ClassReader cls = dep.getClassReader();
|
ClassReader cls = dep.getClassReader();
|
||||||
if (cls.getParent() != null) {
|
if (cls.getParent() != null && !classCache.caches(cls.getParent())) {
|
||||||
linkClass(cls.getParent(), callLocation);
|
linkClass(cls.getParent());
|
||||||
}
|
}
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
linkClass(iface, callLocation);
|
if (!classCache.caches(iface)) {
|
||||||
|
linkClass(iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,54 +429,39 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
private ClassDependency createClassDependency(String className) {
|
private ClassDependency createClassDependency(String className) {
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
ClassDependency dependency = new ClassDependency(this, className, cls);
|
ClassDependency dependency = new ClassDependency(this, className, cls);
|
||||||
if (!dependency.isMissing()) {
|
|
||||||
if (cls.getParent() != null) {
|
|
||||||
linkClass(cls.getParent(), null);
|
|
||||||
}
|
|
||||||
for (String ifaceName : cls.getInterfaces()) {
|
|
||||||
linkClass(ifaceName, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dependency;
|
return dependency;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> methodsAddedByRoot = new HashSet<>();
|
public MethodDependency linkMethod(String className, MethodDescriptor descriptor) {
|
||||||
|
MethodDependency dep = getMethodDependency(className, descriptor);
|
||||||
|
|
||||||
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
|
if (!dep.activated) {
|
||||||
if (methodRef == null) {
|
dep.activated = true;
|
||||||
throw new IllegalArgumentException();
|
if (!dep.isMissing()) {
|
||||||
}
|
for (DependencyListener listener : listeners) {
|
||||||
MethodReader methodReader = methodReaderCache.map(methodRef);
|
listener.methodReached(agent, dep);
|
||||||
if (methodReader != null) {
|
}
|
||||||
methodRef = methodReader.getReference();
|
activateDependencyPlugin(dep);
|
||||||
}
|
|
||||||
|
|
||||||
if (completing && getMethod(methodRef) == null) {
|
|
||||||
throw new IllegalStateException("Can't submit class during completion phase");
|
|
||||||
}
|
|
||||||
callGraph.getNode(methodRef);
|
|
||||||
boolean added;
|
|
||||||
if (callLocation != null && callLocation.getMethod() != null) {
|
|
||||||
added = callGraph.getNode(callLocation.getMethod()).addCallSite(methodRef,
|
|
||||||
callLocation.getSourceLocation());
|
|
||||||
} else {
|
|
||||||
added = methodsAddedByRoot.add(methodRef);
|
|
||||||
}
|
|
||||||
MethodDependency graph = methodCache.map(methodRef);
|
|
||||||
if (!graph.isMissing() && added) {
|
|
||||||
for (DependencyListener listener : listeners) {
|
|
||||||
listener.methodReached(agent, graph, callLocation);
|
|
||||||
}
|
}
|
||||||
activateDependencyPlugin(graph, callLocation);
|
|
||||||
}
|
}
|
||||||
return graph;
|
return dep;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initClass(ClassDependency cls, CallLocation callLocation) {
|
public MethodDependency linkMethod(MethodReference method) {
|
||||||
|
return linkMethod(method.getClassName(), method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
void initClass(ClassDependency cls, CallLocation location) {
|
||||||
ClassReader reader = cls.getClassReader();
|
ClassReader reader = cls.getClassReader();
|
||||||
MethodReader method = reader.getMethod(new MethodDescriptor("<clinit>", void.class));
|
MethodReader method = reader.getMethod(CLINIT_METHOD);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
deferredTasks.add(() -> linkMethod(method.getReference(), callLocation).use());
|
deferredTasks.add(() -> {
|
||||||
|
MethodDependency initMethod = linkMethod(method.getReference());
|
||||||
|
if (location != null) {
|
||||||
|
initMethod.addLocation(location);
|
||||||
|
}
|
||||||
|
initMethod.use();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,55 +470,49 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
int paramCount = arguments.length + 1;
|
int paramCount = arguments.length + 1;
|
||||||
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
|
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
|
||||||
|
|
||||||
parameterNodes[0] = createNode(ValueType.object(methodRef.getClassName()));
|
parameterNodes[0] = createParameterNode(methodRef, ValueType.object(methodRef.getClassName()), 0);
|
||||||
parameterNodes[0].method = methodRef;
|
|
||||||
if (shouldTag) {
|
|
||||||
parameterNodes[0].setTag(methodRef + ":0");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < arguments.length; ++i) {
|
for (int i = 0; i < arguments.length; ++i) {
|
||||||
parameterNodes[i + 1] = createNode(arguments[i]);
|
parameterNodes[i + 1] = createParameterNode(methodRef, arguments[i], i + 1);
|
||||||
parameterNodes[i + 1].method = methodRef;
|
|
||||||
if (shouldTag) {
|
|
||||||
parameterNodes[i + 1].setTag(methodRef + ":" + (i + 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DependencyNode resultNode;
|
DependencyNode resultNode;
|
||||||
if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
|
if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
|
||||||
resultNode = null;
|
resultNode = null;
|
||||||
} else {
|
} else {
|
||||||
resultNode = createNode(methodRef.getDescriptor().getResultType());
|
resultNode = createResultNode(methodRef);
|
||||||
resultNode.method = methodRef;
|
|
||||||
if (shouldTag) {
|
|
||||||
resultNode.setTag(methodRef + ":RESULT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DependencyNode thrown = createNode();
|
|
||||||
thrown.method = methodRef;
|
|
||||||
if (shouldTag) {
|
|
||||||
thrown.setTag(methodRef + ":THROWN");
|
|
||||||
}
|
}
|
||||||
|
DependencyNode thrown = createThrownNode(methodRef);
|
||||||
MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
|
MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
|
||||||
method, methodRef);
|
method, methodRef);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
deferredTasks.add(() -> {
|
deferredTasks.add(() -> linkClass(dep.getMethod().getOwnerName())
|
||||||
CallLocation caller = new CallLocation(dep.getMethod().getReference());
|
.initClass(new CallLocation(dep.getMethod().getReference())));
|
||||||
linkClass(dep.getMethod().getOwnerName(), caller).initClass(caller);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return dep;
|
return dep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract DependencyNode createParameterNode(MethodReference method, ValueType type, int index);
|
||||||
|
|
||||||
|
abstract DependencyNode createResultNode(MethodReference method);
|
||||||
|
|
||||||
|
abstract DependencyNode createThrownNode(MethodReference method);
|
||||||
|
|
||||||
|
abstract DependencyNode createFieldNode(FieldReference field, ValueType type);
|
||||||
|
|
||||||
|
abstract DependencyNode createArrayItemNode(DependencyNode parent);
|
||||||
|
|
||||||
|
abstract DependencyNode createClassValueNode(int degree, DependencyNode parent);
|
||||||
|
|
||||||
void scheduleMethodAnalysis(MethodDependency dep) {
|
void scheduleMethodAnalysis(MethodDependency dep) {
|
||||||
deferredTasks.add(() -> {
|
deferredTasks.add(() -> {
|
||||||
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyAnalyzer.this);
|
processInvokeDynamic(dep);
|
||||||
graphBuilder.buildGraph(dep);
|
processMethod(dep);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MethodReference> getReachableMethods() {
|
public Collection<MethodReference> getReachableMethods() {
|
||||||
return methodCache.getCachedPreimages();
|
return readonlyReachedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -500,23 +527,14 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
|
|
||||||
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
|
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
|
||||||
|
|
||||||
public FieldDependency linkField(FieldReference fieldRef, CallLocation location) {
|
public FieldDependency linkField(FieldReference fieldRef) {
|
||||||
if (completing) {
|
|
||||||
throw new IllegalStateException("Can't submit class during completion phase");
|
|
||||||
}
|
|
||||||
boolean added;
|
|
||||||
if (location != null) {
|
|
||||||
added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation());
|
|
||||||
} else {
|
|
||||||
added = fieldsAddedByRoot.add(fieldRef);
|
|
||||||
}
|
|
||||||
FieldDependency dep = fieldCache.map(fieldRef);
|
FieldDependency dep = fieldCache.map(fieldRef);
|
||||||
if (!dep.isMissing()) {
|
if (!dep.activated) {
|
||||||
deferredTasks.add(() -> linkClass(fieldRef.getClassName(), location).initClass(location));
|
dep.activated = true;
|
||||||
}
|
if (!dep.isMissing()) {
|
||||||
if (!dep.isMissing() && added) {
|
for (DependencyListener listener : listeners) {
|
||||||
for (DependencyListener listener : listeners) {
|
listener.fieldReached(agent, dep);
|
||||||
listener.fieldReached(agent, dep, location);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dep;
|
return dep;
|
||||||
|
@ -533,21 +551,14 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field) {
|
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field) {
|
||||||
DependencyNode node = createNode(field != null ? field.getType() : null);
|
DependencyNode node = createFieldNode(fieldRef, field != null ? field.getType() : null);
|
||||||
if (shouldTag) {
|
return new FieldDependency(node, field, fieldRef);
|
||||||
node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName());
|
|
||||||
}
|
|
||||||
FieldDependency dep = new FieldDependency(node, field, fieldRef);
|
|
||||||
if (!dep.isMissing()) {
|
|
||||||
deferredTasks.add(() -> linkClass(fieldRef.getClassName(), null).initClass(null));
|
|
||||||
}
|
|
||||||
return dep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activateDependencyPlugin(MethodDependency methodDep, CallLocation location) {
|
private void activateDependencyPlugin(MethodDependency methodDep) {
|
||||||
attachDependencyPlugin(methodDep);
|
attachDependencyPlugin(methodDep);
|
||||||
if (methodDep.dependencyPlugin != null) {
|
if (methodDep.dependencyPlugin != null) {
|
||||||
methodDep.dependencyPlugin.methodReached(agent, methodDep, location);
|
methodDep.dependencyPlugin.methodReached(agent, methodDep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,13 +599,48 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodDependency getMethod(MethodReference methodRef) {
|
public MethodDependency getMethod(MethodReference methodRef) {
|
||||||
return methodCache.getKnown(methodRef);
|
return getMethod(methodRef.getClassName(), methodRef.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodDependency getMethod(String className, MethodDescriptor descriptor) {
|
||||||
|
Map<MethodDescriptor, MethodDependency> map = methodCache.get(className);
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return map.get(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodDependency getMethodImplementation(MethodReference methodRef) {
|
public MethodDependency getMethodImplementation(MethodReference methodRef) {
|
||||||
MethodReader method = methodReaderCache.map(methodRef);
|
MethodReader method = getMethodHolder(methodRef.getClassName(), methodRef.getDescriptor());
|
||||||
return method != null ? methodCache.getKnown(method.getReference()) : null;
|
return method != null ? getMethod(method.getReference()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHolder getMethodHolder(String className, MethodDescriptor descriptor) {
|
||||||
|
return methodReaderCache
|
||||||
|
.computeIfAbsent(className, k -> new HashMap<>(100, 0.5f))
|
||||||
|
.computeIfAbsent(descriptor, k -> Optional.ofNullable(
|
||||||
|
classSource.resolveMutableImplementation(className, k)))
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDependency getMethodDependency(String className, MethodDescriptor descriptor) {
|
||||||
|
Map<MethodDescriptor, MethodDependency> map = methodCache.computeIfAbsent(className, k -> new HashMap<>());
|
||||||
|
MethodDependency result = map.get(descriptor);
|
||||||
|
if (result == null) {
|
||||||
|
MethodHolder method = getMethodHolder(className, descriptor);
|
||||||
|
if (method != null && !(method.getDescriptor().equals(descriptor)
|
||||||
|
&& method.getOwnerName().equals(className))) {
|
||||||
|
result = getMethodDependency(method.getOwnerName(), method.getDescriptor());
|
||||||
|
} else {
|
||||||
|
MethodReference reference = method != null
|
||||||
|
? method.getReference()
|
||||||
|
: new MethodReference(className, descriptor);
|
||||||
|
result = createMethodDep(reference, method);
|
||||||
|
}
|
||||||
|
map.put(descriptor, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processQueue() {
|
private void processQueue() {
|
||||||
|
@ -763,6 +809,10 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
dependencyPlugins.put(method, dependencyPlugin);
|
dependencyPlugins.put(method, dependencyPlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IncrementalDependencyProvider getIncrementalDependencies() {
|
||||||
|
return incrementalCache;
|
||||||
|
}
|
||||||
|
|
||||||
DependencyTypeFilter getSuperClassFilter(String superClass) {
|
DependencyTypeFilter getSuperClassFilter(String superClass) {
|
||||||
DependencyTypeFilter result = superClassFilters.get(superClass);
|
DependencyTypeFilter result = superClassFilters.get(superClass);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
@ -777,10 +827,148 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
result = new ExactTypeFilter(superClass);
|
result = new ExactTypeFilter(superClass);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = new SuperClassFilter(classSource, superClass);
|
if (superClass.equals("java.lang.Object")) {
|
||||||
|
result = t -> true;
|
||||||
|
} else {
|
||||||
|
result = new SuperClassFilter(this, getType(superClass));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
superClassFilters.put(superClass, result);
|
superClassFilters.put(superClass, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processInvokeDynamic(MethodDependency methodDep) {
|
||||||
|
if (methodDep.method == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program program = methodDep.method.getProgram();
|
||||||
|
if (program == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallGraphNode caller = callGraph.getNode(methodDep.getReference());
|
||||||
|
|
||||||
|
ProgramEmitter pe = ProgramEmitter.create(program, classHierarchy);
|
||||||
|
boolean hasIndy = false;
|
||||||
|
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
for (Instruction insn : block) {
|
||||||
|
if (!(insn instanceof InvokeDynamicInstruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
block = insn.getBasicBlock();
|
||||||
|
|
||||||
|
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
|
||||||
|
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
|
||||||
|
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
|
||||||
|
BootstrapMethodSubstitutor substitutor = bootstrapMethodSubstitutors.get(bootstrapMethod);
|
||||||
|
if (substitutor == null) {
|
||||||
|
NullConstantInstruction nullInsn = new NullConstantInstruction();
|
||||||
|
nullInsn.setReceiver(indy.getReceiver());
|
||||||
|
nullInsn.setLocation(indy.getLocation());
|
||||||
|
insn.replace(nullInsn);
|
||||||
|
CallLocation location = new CallLocation(methodDep.getReference(), insn.getLocation());
|
||||||
|
diagnostics.error(location, "Substitutor for bootstrap method {{m0}} was not found",
|
||||||
|
bootstrapMethod);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasIndy = true;
|
||||||
|
BasicBlock splitBlock = splitter.split(block, insn);
|
||||||
|
|
||||||
|
pe.enter(block);
|
||||||
|
pe.setCurrentLocation(indy.getLocation());
|
||||||
|
insn.delete();
|
||||||
|
|
||||||
|
List<ValueEmitter> arguments = new ArrayList<>();
|
||||||
|
for (int k = 0; k < indy.getArguments().size(); ++k) {
|
||||||
|
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
|
||||||
|
}
|
||||||
|
DynamicCallSite callSite = new DynamicCallSite(
|
||||||
|
methodDep.getReference(), indy.getMethod(),
|
||||||
|
indy.getInstance() != null ? pe.var(indy.getInstance(),
|
||||||
|
ValueType.object(methodDep.getMethod().getOwnerName())) : null,
|
||||||
|
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
|
||||||
|
agent);
|
||||||
|
ValueEmitter result = substitutor.substitute(callSite, pe);
|
||||||
|
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()) {
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setAssignee(result.getVariable());
|
||||||
|
assign.setReceiver(indy.getReceiver());
|
||||||
|
pe.addInstruction(assign);
|
||||||
|
}
|
||||||
|
pe.jump(splitBlock);
|
||||||
|
block = splitBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
splitter.fixProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
class IncrementalCache implements IncrementalDependencyProvider, IncrementalDependencyRegistration {
|
||||||
|
private final String[] emptyArray = new String[0];
|
||||||
|
private Map<String, IncrementalItem> classes = new HashMap<>();
|
||||||
|
private Map<MethodReference, IncrementalItem> methods = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNoCache(String className) {
|
||||||
|
IncrementalItem item = classes.get(className);
|
||||||
|
return item != null && item.noCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNoCache(MethodReference method) {
|
||||||
|
IncrementalItem item = methods.get(method);
|
||||||
|
return item != null && item.noCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDependencies(String className) {
|
||||||
|
IncrementalItem item = classes.get(className);
|
||||||
|
return item != null && item.dependencies != null ? item.dependencies.toArray(new String[0]) : emptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDependencies(MethodReference method) {
|
||||||
|
IncrementalItem item = methods.get(method);
|
||||||
|
return item != null && item.dependencies != null ? item.dependencies.toArray(new String[0]) : emptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNoCache(String className) {
|
||||||
|
classes.computeIfAbsent(className, k -> new IncrementalItem()).noCache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNoCache(MethodReference method) {
|
||||||
|
methods.computeIfAbsent(method, k -> new IncrementalItem()).noCache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDependencies(String className, String... dependencies) {
|
||||||
|
IncrementalItem item = classes.computeIfAbsent(className, k -> new IncrementalItem());
|
||||||
|
if (item.dependencies == null) {
|
||||||
|
item.dependencies = new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
item.dependencies.addAll(Arrays.asList(dependencies));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDependencies(MethodReference method, String... dependencies) {
|
||||||
|
IncrementalItem item = this.methods.computeIfAbsent(method, k -> new IncrementalItem());
|
||||||
|
if (item.dependencies == null) {
|
||||||
|
item.dependencies = new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
item.dependencies.addAll(Arrays.asList(dependencies));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class IncrementalItem {
|
||||||
|
boolean noCache;
|
||||||
|
Set<String> dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract boolean domainOptimizationEnabled();
|
||||||
}
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import org.teavm.common.ServiceRepository;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
|
||||||
|
public interface DependencyAnalyzerFactory {
|
||||||
|
DependencyAnalyzer create(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||||
|
Diagnostics diagnostics);
|
||||||
|
}
|
|
@ -20,10 +20,14 @@ import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -32,19 +36,24 @@ import org.teavm.model.util.ModelUtils;
|
||||||
|
|
||||||
class DependencyClassSource implements ClassHolderSource {
|
class DependencyClassSource implements ClassHolderSource {
|
||||||
private ClassReaderSource innerSource;
|
private ClassReaderSource innerSource;
|
||||||
|
private ClassHierarchy innerHierarchy;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
|
private IncrementalDependencyRegistration dependencyRegistration;
|
||||||
private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>();
|
private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>();
|
||||||
private List<ClassHolderTransformer> transformers = new ArrayList<>();
|
private List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||||
private Map<String, ClassHolder> cache = new LinkedHashMap<>();
|
private Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
|
||||||
|
|
||||||
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) {
|
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
|
||||||
|
IncrementalDependencyRegistration dependencyRegistration) {
|
||||||
this.innerSource = innerSource;
|
this.innerSource = innerSource;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
|
innerHierarchy = new ClassHierarchy(innerSource);
|
||||||
|
this.dependencyRegistration = dependencyRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassHolder get(String name) {
|
public ClassHolder get(String name) {
|
||||||
return cache.computeIfAbsent(name, this::findAndTransformClass);
|
return cache.computeIfAbsent(name, n -> Optional.ofNullable(findAndTransformClass(n))).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(ClassHolder cls) {
|
public void submit(ClassHolder cls) {
|
||||||
|
@ -53,7 +62,7 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
}
|
}
|
||||||
if (!transformers.isEmpty()) {
|
if (!transformers.isEmpty()) {
|
||||||
for (ClassHolderTransformer transformer : transformers) {
|
for (ClassHolderTransformer transformer : transformers) {
|
||||||
transformer.transformClass(cls, innerSource, diagnostics);
|
transformer.transformClass(cls, transformContext);
|
||||||
}
|
}
|
||||||
cls = ModelUtils.copyClass(cls);
|
cls = ModelUtils.copyClass(cls);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +79,7 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
ClassHolder cls = findClass(name);
|
ClassHolder cls = findClass(name);
|
||||||
if (cls != null && !transformers.isEmpty()) {
|
if (cls != null && !transformers.isEmpty()) {
|
||||||
for (ClassHolderTransformer transformer : transformers) {
|
for (ClassHolderTransformer transformer : transformers) {
|
||||||
transformer.transformClass(cls, innerSource, diagnostics);
|
transformer.transformClass(cls, transformContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cls;
|
return cls;
|
||||||
|
@ -88,7 +97,28 @@ class DependencyClassSource implements ClassHolderSource {
|
||||||
return generatedClasses.values();
|
return generatedClasses.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGeneratedClass(String className) {
|
||||||
|
return generatedClasses.containsKey(className);
|
||||||
|
}
|
||||||
|
|
||||||
public void addTransformer(ClassHolderTransformer transformer) {
|
public void addTransformer(ClassHolderTransformer transformer) {
|
||||||
transformers.add(transformer);
|
transformers.add(transformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ClassHolderTransformerContext transformContext = new ClassHolderTransformerContext() {
|
||||||
|
@Override
|
||||||
|
public ClassHierarchy getHierarchy() {
|
||||||
|
return innerHierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Diagnostics getDiagnostics() {
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IncrementalDependencyRegistration getIncrementalCache() {
|
||||||
|
return dependencyRegistration;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,46 +15,30 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_ENTER_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_ENTER_SYNC_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_SYNC_METHOD;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.cache.NoCache;
|
|
||||||
import org.teavm.callgraph.DefaultCallGraphNode;
|
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||||
import org.teavm.model.AnnotationHolder;
|
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.BasicBlockReader;
|
import org.teavm.model.BasicBlockReader;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
|
||||||
import org.teavm.model.IncomingReader;
|
import org.teavm.model.IncomingReader;
|
||||||
import org.teavm.model.Instruction;
|
|
||||||
import org.teavm.model.InvokeDynamicInstruction;
|
|
||||||
import org.teavm.model.MethodDescriptor;
|
|
||||||
import org.teavm.model.MethodHandle;
|
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.PhiReader;
|
import org.teavm.model.PhiReader;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.RuntimeConstant;
|
|
||||||
import org.teavm.model.TextLocation;
|
|
||||||
import org.teavm.model.TryCatchBlockReader;
|
import org.teavm.model.TryCatchBlockReader;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.VariableReader;
|
import org.teavm.model.VariableReader;
|
||||||
import org.teavm.model.emit.ProgramEmitter;
|
|
||||||
import org.teavm.model.emit.ValueEmitter;
|
|
||||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
|
||||||
import org.teavm.model.instructions.ArrayElementType;
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
|
||||||
import org.teavm.model.instructions.InstructionReader;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
|
||||||
import org.teavm.model.text.ListingBuilder;
|
import org.teavm.model.text.ListingBuilder;
|
||||||
import org.teavm.model.util.BasicBlockSplitter;
|
|
||||||
|
|
||||||
class DependencyGraphBuilder {
|
class DependencyGraphBuilder {
|
||||||
private DependencyAnalyzer dependencyAnalyzer;
|
private DependencyAnalyzer dependencyAnalyzer;
|
||||||
|
@ -62,7 +46,6 @@ class DependencyGraphBuilder {
|
||||||
private DependencyNode resultNode;
|
private DependencyNode resultNode;
|
||||||
private Program program;
|
private Program program;
|
||||||
private DefaultCallGraphNode caller;
|
private DefaultCallGraphNode caller;
|
||||||
private TextLocation currentLocation;
|
|
||||||
private ExceptionConsumer currentExceptionConsumer;
|
private ExceptionConsumer currentExceptionConsumer;
|
||||||
|
|
||||||
DependencyGraphBuilder(DependencyAnalyzer dependencyAnalyzer) {
|
DependencyGraphBuilder(DependencyAnalyzer dependencyAnalyzer) {
|
||||||
|
@ -78,8 +61,6 @@ class DependencyGraphBuilder {
|
||||||
program = method.getProgram();
|
program = method.getProgram();
|
||||||
resultNode = dep.getResult();
|
resultNode = dep.getResult();
|
||||||
|
|
||||||
processInvokeDynamic(dep);
|
|
||||||
|
|
||||||
DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder();
|
DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder();
|
||||||
boolean[] significantParams = new boolean[dep.getParameterCount()];
|
boolean[] significantParams = new boolean[dep.getParameterCount()];
|
||||||
significantParams[0] = true;
|
significantParams[0] = true;
|
||||||
|
@ -122,6 +103,7 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
dep.setVariables(nodes);
|
dep.setVariables(nodes);
|
||||||
|
|
||||||
|
reader.setCaller(caller.getMethod());
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlockReader block = program.basicBlockAt(i);
|
BasicBlockReader block = program.basicBlockAt(i);
|
||||||
currentExceptionConsumer = createExceptionConsumer(dep, block);
|
currentExceptionConsumer = createExceptionConsumer(dep, block);
|
||||||
|
@ -139,7 +121,7 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
||||||
if (tryCatch.getExceptionType() != null) {
|
if (tryCatch.getExceptionType() != null) {
|
||||||
dependencyAnalyzer.linkClass(tryCatch.getExceptionType(), new CallLocation(caller.getMethod()));
|
dependencyAnalyzer.linkClass(tryCatch.getExceptionType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,26 +131,22 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
MethodDependency methodDep;
|
MethodDependency methodDep;
|
||||||
if (dependencyAnalyzer.asyncSupported) {
|
if (dependencyAnalyzer.asyncSupported) {
|
||||||
methodDep = dependencyAnalyzer.linkMethod(
|
methodDep = dependencyAnalyzer.linkMethod(MONITOR_ENTER_METHOD);
|
||||||
new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null);
|
|
||||||
syncNodes.add(methodDep.getVariable(1));
|
syncNodes.add(methodDep.getVariable(1));
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
}
|
}
|
||||||
|
|
||||||
methodDep = dependencyAnalyzer.linkMethod(
|
methodDep = dependencyAnalyzer.linkMethod(MONITOR_ENTER_SYNC_METHOD);
|
||||||
new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null);
|
|
||||||
syncNodes.add(methodDep.getVariable(1));
|
syncNodes.add(methodDep.getVariable(1));
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
|
|
||||||
if (dependencyAnalyzer.asyncSupported) {
|
if (dependencyAnalyzer.asyncSupported) {
|
||||||
methodDep = dependencyAnalyzer.linkMethod(
|
methodDep = dependencyAnalyzer.linkMethod(MONITOR_EXIT_METHOD);
|
||||||
new MethodReference(Object.class, "monitorExit", Object.class, void.class), null);
|
|
||||||
syncNodes.add(methodDep.getVariable(1));
|
syncNodes.add(methodDep.getVariable(1));
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
}
|
}
|
||||||
|
|
||||||
methodDep = dependencyAnalyzer.linkMethod(
|
methodDep = dependencyAnalyzer.linkMethod(MONITOR_EXIT_SYNC_METHOD);
|
||||||
new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null);
|
|
||||||
syncNodes.add(methodDep.getVariable(1));
|
syncNodes.add(methodDep.getVariable(1));
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
|
|
||||||
|
@ -184,72 +162,6 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processInvokeDynamic(MethodDependency methodDep) {
|
|
||||||
if (program == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ProgramEmitter pe = ProgramEmitter.create(program, dependencyAnalyzer.getClassSource());
|
|
||||||
boolean hasIndy = false;
|
|
||||||
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
for (Instruction insn : block) {
|
|
||||||
if (!(insn instanceof InvokeDynamicInstruction)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
block = insn.getBasicBlock();
|
|
||||||
|
|
||||||
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
|
|
||||||
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
|
|
||||||
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
|
|
||||||
BootstrapMethodSubstitutor substitutor = dependencyAnalyzer.bootstrapMethodSubstitutors
|
|
||||||
.get(bootstrapMethod);
|
|
||||||
if (substitutor == null) {
|
|
||||||
NullConstantInstruction nullInsn = new NullConstantInstruction();
|
|
||||||
nullInsn.setReceiver(indy.getReceiver());
|
|
||||||
nullInsn.setLocation(indy.getLocation());
|
|
||||||
insn.replace(nullInsn);
|
|
||||||
CallLocation location = new CallLocation(caller.getMethod(), currentLocation);
|
|
||||||
dependencyAnalyzer.getDiagnostics().error(location, "Substitutor for bootstrap "
|
|
||||||
+ "method {{m0}} was not found", bootstrapMethod);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasIndy = true;
|
|
||||||
BasicBlock splitBlock = splitter.split(block, insn);
|
|
||||||
|
|
||||||
pe.enter(block);
|
|
||||||
pe.setCurrentLocation(indy.getLocation());
|
|
||||||
insn.delete();
|
|
||||||
|
|
||||||
List<ValueEmitter> arguments = new ArrayList<>();
|
|
||||||
for (int k = 0; k < indy.getArguments().size(); ++k) {
|
|
||||||
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
|
|
||||||
}
|
|
||||||
DynamicCallSite callSite = new DynamicCallSite(
|
|
||||||
methodDep.getReference(), indy.getMethod(),
|
|
||||||
indy.getInstance() != null ? pe.var(indy.getInstance(),
|
|
||||||
ValueType.object(methodDep.getMethod().getOwnerName())) : null,
|
|
||||||
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
|
|
||||||
dependencyAnalyzer.getAgent());
|
|
||||||
ValueEmitter result = substitutor.substitute(callSite, pe);
|
|
||||||
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()) {
|
|
||||||
AssignInstruction assign = new AssignInstruction();
|
|
||||||
assign.setAssignee(result.getVariable());
|
|
||||||
assign.setReceiver(indy.getReceiver());
|
|
||||||
pe.addInstruction(assign);
|
|
||||||
}
|
|
||||||
pe.jump(splitBlock);
|
|
||||||
block = splitBlock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
splitter.fixProgram();
|
|
||||||
|
|
||||||
if (hasIndy && methodDep.method.getAnnotations().get(NoCache.class.getName()) == null) {
|
|
||||||
methodDep.method.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExceptionConsumer createExceptionConsumer(MethodDependency methodDep, BasicBlockReader block) {
|
private ExceptionConsumer createExceptionConsumer(MethodDependency methodDep, BasicBlockReader block) {
|
||||||
List<? extends TryCatchBlockReader> tryCatchBlocks = block.readTryCatchBlocks();
|
List<? extends TryCatchBlockReader> tryCatchBlocks = block.readTryCatchBlocks();
|
||||||
ClassReader[] exceptions = new ClassReader[tryCatchBlocks.size()];
|
ClassReader[] exceptions = new ClassReader[tryCatchBlocks.size()];
|
||||||
|
@ -282,10 +194,9 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(DependencyType type) {
|
public void consume(DependencyType type) {
|
||||||
ClassReaderSource classSource = analyzer.getClassSource();
|
ClassHierarchy hierarchy = analyzer.getClassHierarchy();
|
||||||
for (int i = 0; i < exceptions.length; ++i) {
|
for (int i = 0; i < exceptions.length; ++i) {
|
||||||
if (exceptions[i] == null || classSource.isSuperType(exceptions[i].getName(), type.getName())
|
if (exceptions[i] == null || hierarchy.isSuperType(exceptions[i].getName(), type.getName(), false)) {
|
||||||
.orElse(false)) {
|
|
||||||
if (vars[i] != null) {
|
if (vars[i] != null) {
|
||||||
vars[i].propagate(type);
|
vars[i].propagate(type);
|
||||||
}
|
}
|
||||||
|
@ -296,123 +207,7 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class VirtualCallConsumer implements DependencyConsumer {
|
private AbstractInstructionAnalyzer reader = new AbstractInstructionAnalyzer() {
|
||||||
private final DependencyNode node;
|
|
||||||
private final MethodDescriptor methodDesc;
|
|
||||||
private final DependencyAnalyzer analyzer;
|
|
||||||
private final DependencyNode[] parameters;
|
|
||||||
private final DependencyNode result;
|
|
||||||
private final DefaultCallGraphNode caller;
|
|
||||||
private final TextLocation location;
|
|
||||||
private final Set<MethodReference> knownMethods = new HashSet<>();
|
|
||||||
private final BitSet knownTypes = new BitSet();
|
|
||||||
private ExceptionConsumer exceptionConsumer;
|
|
||||||
private DependencyTypeFilter filter;
|
|
||||||
|
|
||||||
VirtualCallConsumer(DependencyNode node, String filterClass,
|
|
||||||
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
|
|
||||||
DependencyNode result, DefaultCallGraphNode caller, TextLocation location,
|
|
||||||
ExceptionConsumer exceptionConsumer) {
|
|
||||||
this.node = node;
|
|
||||||
this.filter = analyzer.getSuperClassFilter(filterClass);
|
|
||||||
this.methodDesc = methodDesc;
|
|
||||||
this.analyzer = analyzer;
|
|
||||||
this.parameters = parameters;
|
|
||||||
this.result = result;
|
|
||||||
this.caller = caller;
|
|
||||||
this.location = location;
|
|
||||||
this.exceptionConsumer = exceptionConsumer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consume(DependencyType type) {
|
|
||||||
if (knownTypes.get(type.index)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
knownTypes.set(type.index);
|
|
||||||
|
|
||||||
String className = type.getName();
|
|
||||||
if (DependencyAnalyzer.shouldLog) {
|
|
||||||
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
|
|
||||||
+ "Target class is " + className);
|
|
||||||
}
|
|
||||||
if (className.startsWith("[")) {
|
|
||||||
className = "java.lang.Object";
|
|
||||||
type = analyzer.getType(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filter.match(type)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MethodReference methodRef = new MethodReference(className, methodDesc);
|
|
||||||
MethodDependency methodDep = analyzer.linkMethod(methodRef, new CallLocation(caller.getMethod(), location));
|
|
||||||
if (!methodDep.isMissing() && knownMethods.add(methodRef)) {
|
|
||||||
methodDep.use();
|
|
||||||
DependencyNode[] targetParams = methodDep.getVariables();
|
|
||||||
if (parameters[0] != null && targetParams[0] != null) {
|
|
||||||
parameters[0].connect(targetParams[0],
|
|
||||||
analyzer.getSuperClassFilter(methodDep.getMethod().getOwnerName()));
|
|
||||||
}
|
|
||||||
for (int i = 1; i < parameters.length; ++i) {
|
|
||||||
if (parameters[i] != null && targetParams[i] != null) {
|
|
||||||
parameters[i].connect(targetParams[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result != null && methodDep.getResult() != null) {
|
|
||||||
methodDep.getResult().connect(result);
|
|
||||||
}
|
|
||||||
methodDep.getThrown().addConsumer(exceptionConsumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstructionReader reader = new AbstractInstructionReader() {
|
|
||||||
@Override
|
|
||||||
public void location(TextLocation location) {
|
|
||||||
currentLocation = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void classConstant(VariableReader receiver, ValueType cst) {
|
|
||||||
DependencyNode node = nodes[receiver.getIndex()];
|
|
||||||
if (node != null) {
|
|
||||||
node.propagate(dependencyAnalyzer.getType("java.lang.Class"));
|
|
||||||
if (!(cst instanceof ValueType.Primitive)) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
while (cst instanceof ValueType.Array) {
|
|
||||||
cst = ((ValueType.Array) cst).getItemType();
|
|
||||||
sb.append('[');
|
|
||||||
}
|
|
||||||
if (cst instanceof ValueType.Object) {
|
|
||||||
sb.append(((ValueType.Object) cst).getClassName());
|
|
||||||
} else {
|
|
||||||
sb.append(cst.toString());
|
|
||||||
}
|
|
||||||
node.getClassValueNode().propagate(dependencyAnalyzer.getType(sb.toString()));
|
|
||||||
} else {
|
|
||||||
node.getClassValueNode().propagate(dependencyAnalyzer.getType("~" + cst.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (cst instanceof ValueType.Array) {
|
|
||||||
cst = ((ValueType.Array) cst).getItemType();
|
|
||||||
}
|
|
||||||
if (cst instanceof ValueType.Object) {
|
|
||||||
final String className = ((ValueType.Object) cst).getClassName();
|
|
||||||
dependencyAnalyzer.linkClass(className, new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stringConstant(VariableReader receiver, String cst) {
|
|
||||||
DependencyNode node = nodes[receiver.getIndex()];
|
|
||||||
if (node != null) {
|
|
||||||
node.propagate(dependencyAnalyzer.getType("java.lang.String"));
|
|
||||||
}
|
|
||||||
MethodDependency method = dependencyAnalyzer.linkMethod(new MethodReference(String.class,
|
|
||||||
"<init>", char[].class, void.class), new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
method.use();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assign(VariableReader receiver, VariableReader assignee) {
|
public void assign(VariableReader receiver, VariableReader assignee) {
|
||||||
DependencyNode valueNode = nodes[assignee.getIndex()];
|
DependencyNode valueNode = nodes[assignee.getIndex()];
|
||||||
|
@ -472,106 +267,6 @@ class DependencyGraphBuilder {
|
||||||
nodes[exception.getIndex()].addConsumer(currentExceptionConsumer);
|
nodes[exception.getIndex()].addConsumer(currentExceptionConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
|
|
||||||
DependencyNode node = nodes[receiver.getIndex()];
|
|
||||||
if (node != null) {
|
|
||||||
node.propagate(dependencyAnalyzer.getType("[" + itemType));
|
|
||||||
}
|
|
||||||
String className = extractClassName(itemType);
|
|
||||||
if (className != null) {
|
|
||||||
dependencyAnalyzer.linkClass(className, new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractClassName(ValueType itemType) {
|
|
||||||
while (itemType instanceof ValueType.Array) {
|
|
||||||
itemType = ((ValueType.Array) itemType).getItemType();
|
|
||||||
}
|
|
||||||
return itemType instanceof ValueType.Object ? ((ValueType.Object) itemType).getClassName() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createArray(VariableReader receiver, ValueType itemType,
|
|
||||||
List<? extends VariableReader> dimensions) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (int i = 0; i < dimensions.size(); ++i) {
|
|
||||||
sb.append('[');
|
|
||||||
itemType = ((ValueType.Array) itemType).getItemType();
|
|
||||||
}
|
|
||||||
String itemTypeStr;
|
|
||||||
if (itemType instanceof ValueType.Object) {
|
|
||||||
itemTypeStr = ((ValueType.Object) itemType).getClassName();
|
|
||||||
} else {
|
|
||||||
itemTypeStr = itemType.toString();
|
|
||||||
}
|
|
||||||
sb.append(itemTypeStr);
|
|
||||||
DependencyNode node = nodes[receiver.getIndex()];
|
|
||||||
for (int i = 0; i < dimensions.size(); ++i) {
|
|
||||||
if (node == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
node.propagate(dependencyAnalyzer.getType(sb.substring(i, sb.length())));
|
|
||||||
node = node.getArrayItem();
|
|
||||||
}
|
|
||||||
String className = extractClassName(itemType);
|
|
||||||
if (className != null) {
|
|
||||||
dependencyAnalyzer.linkClass(className, new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void create(VariableReader receiver, String type) {
|
|
||||||
dependencyAnalyzer.linkClass(type, new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
DependencyNode node = nodes[receiver.getIndex()];
|
|
||||||
if (node != null) {
|
|
||||||
node.propagate(dependencyAnalyzer.getType(type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
|
|
||||||
ValueType fieldType) {
|
|
||||||
FieldDependency fieldDep = dependencyAnalyzer.linkField(field,
|
|
||||||
new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
if (!(fieldType instanceof ValueType.Primitive)) {
|
|
||||||
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
|
||||||
if (receiverNode != null) {
|
|
||||||
fieldDep.getValue().connect(receiverNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initClass(field.getClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putField(VariableReader instance, FieldReference field, VariableReader value,
|
|
||||||
ValueType fieldType) {
|
|
||||||
FieldDependency fieldDep = dependencyAnalyzer.linkField(field,
|
|
||||||
new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
if (!(fieldType instanceof ValueType.Primitive)) {
|
|
||||||
DependencyNode valueNode = nodes[value.getIndex()];
|
|
||||||
if (valueNode != null) {
|
|
||||||
valueNode.connect(fieldDep.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initClass(field.getClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cloneArray(VariableReader receiver, VariableReader array) {
|
|
||||||
DependencyNode arrayNode = nodes[array.getIndex()];
|
|
||||||
final DependencyNode receiverNode = nodes[receiver.getIndex()];
|
|
||||||
if (arrayNode != null && receiverNode != null) {
|
|
||||||
arrayNode.addConsumer(receiverNode::propagate);
|
|
||||||
arrayNode.getArrayItem().connect(receiverNode.getArrayItem());
|
|
||||||
}
|
|
||||||
MethodDependency cloneDep = dependencyAnalyzer.linkMethod(
|
|
||||||
new MethodReference(Object.class, "clone", Object.class),
|
|
||||||
new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
arrayNode.connect(cloneDep.getVariable(0));
|
|
||||||
cloneDep.use();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
|
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
|
||||||
DependencyNode arrayNode = nodes[array.getIndex()];
|
DependencyNode arrayNode = nodes[array.getIndex()];
|
||||||
|
@ -581,6 +276,20 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneArray(VariableReader receiver, VariableReader array) {
|
||||||
|
DependencyNode arrayNode = getNode(array);
|
||||||
|
DependencyNode receiverNode = getNode(receiver);
|
||||||
|
if (arrayNode != null && receiverNode != null) {
|
||||||
|
arrayNode.addConsumer(receiverNode::propagate);
|
||||||
|
arrayNode.getArrayItem().connect(receiverNode.getArrayItem());
|
||||||
|
}
|
||||||
|
MethodDependency cloneDep = getAnalyzer().linkMethod(CLONE_METHOD);
|
||||||
|
cloneDep.addLocation(getCallLocation());
|
||||||
|
arrayNode.connect(cloneDep.getVariable(0));
|
||||||
|
cloneDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||||
ArrayElementType type) {
|
ArrayElementType type) {
|
||||||
|
@ -612,31 +321,16 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
List<? extends VariableReader> arguments, InvocationType type) {
|
|
||||||
if (instance == null) {
|
|
||||||
invokeSpecial(receiver, null, method, arguments);
|
|
||||||
} else {
|
|
||||||
switch (type) {
|
|
||||||
case SPECIAL:
|
|
||||||
invokeSpecial(receiver, instance, method, arguments);
|
|
||||||
break;
|
|
||||||
case VIRTUAL:
|
|
||||||
invokeVirtual(receiver, instance, method, arguments);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (method.getName().equals("getClass") && method.parameterCount() == 0
|
|
||||||
&& method.getReturnType().isObject(Class.class) && receiver != null) {
|
|
||||||
nodes[instance.getIndex()].connect(nodes[receiver.getIndex()].getClassValueNode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
|
||||||
List<? extends VariableReader> arguments) {
|
List<? extends VariableReader> arguments) {
|
||||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
CallLocation callLocation = getCallLocation();
|
||||||
dependencyAnalyzer.linkClass(method.getClassName(), callLocation).initClass(callLocation);
|
if (instance == null) {
|
||||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method, callLocation);
|
dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation);
|
||||||
|
} else {
|
||||||
|
dependencyAnalyzer.linkClass(method.getClassName());
|
||||||
|
}
|
||||||
|
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method);
|
||||||
|
methodDep.addLocation(callLocation);
|
||||||
methodDep.use();
|
methodDep.use();
|
||||||
if (methodDep.isMissing()) {
|
if (methodDep.isMissing()) {
|
||||||
return;
|
return;
|
||||||
|
@ -662,86 +356,39 @@ class DependencyGraphBuilder {
|
||||||
initClass(method.getClassName());
|
initClass(method.getClassName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
@Override
|
||||||
|
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
List<? extends VariableReader> arguments) {
|
List<? extends VariableReader> arguments) {
|
||||||
DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1];
|
DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1];
|
||||||
for (int i = 0; i < arguments.size(); ++i) {
|
for (int i = 0; i < arguments.size(); ++i) {
|
||||||
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
|
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
|
||||||
}
|
}
|
||||||
actualArgs[0] = nodes[instance.getIndex()];
|
actualArgs[0] = getNode(instance);
|
||||||
DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()],
|
DependencyConsumer listener = new VirtualCallConsumer(getNode(instance),
|
||||||
method.getClassName(), method.getDescriptor(), dependencyAnalyzer, actualArgs,
|
method.getClassName(), method.getDescriptor(), dependencyAnalyzer, actualArgs,
|
||||||
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
|
receiver != null ? getNode(receiver) : null, getCallLocation(),
|
||||||
currentExceptionConsumer);
|
currentExceptionConsumer);
|
||||||
nodes[instance.getIndex()].addConsumer(listener);
|
getNode(instance).addConsumer(listener);
|
||||||
|
|
||||||
dependencyAnalyzer.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
|
dependencyAnalyzer.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
|
||||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
dependencyAnalyzer.linkMethod(methodImpl.getReference()).addLocation(getCallLocation());
|
||||||
dependencyAnalyzer.linkMethod(methodImpl.getReference(), callLocation);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void isInstance(VariableReader receiver, VariableReader value, final ValueType type) {
|
|
||||||
String className = extractClassName(type);
|
|
||||||
if (className != null) {
|
|
||||||
dependencyAnalyzer.linkClass(className, new CallLocation(caller.getMethod(), currentLocation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
|
||||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
|
||||||
List<RuntimeConstant> bootstrapArguments) {
|
|
||||||
// Should be eliminated by processInvokeDynamic method
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initClass(final String className) {
|
|
||||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
|
||||||
dependencyAnalyzer.linkClass(className, callLocation).initClass(callLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nullCheck(VariableReader receiver, VariableReader value) {
|
public void nullCheck(VariableReader receiver, VariableReader value) {
|
||||||
DependencyNode valueNode = nodes[value.getIndex()];
|
super.nullCheck(receiver, value);
|
||||||
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
|
||||||
if (valueNode != null) {
|
|
||||||
valueNode.connect(receiverNode);
|
|
||||||
}
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class),
|
|
||||||
new CallLocation(caller.getMethod(), currentLocation)).use();
|
|
||||||
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void monitorEnter(VariableReader objectRef) {
|
protected DependencyNode getNode(VariableReader variable) {
|
||||||
if (dependencyAnalyzer.asyncSupported) {
|
return nodes[variable.getIndex()];
|
||||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(
|
|
||||||
new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null);
|
|
||||||
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
|
|
||||||
methodDep.use();
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(
|
|
||||||
new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null);
|
|
||||||
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
|
|
||||||
methodDep.use();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void monitorExit(VariableReader objectRef) {
|
protected DependencyAnalyzer getAnalyzer() {
|
||||||
if (dependencyAnalyzer.asyncSupported) {
|
return dependencyAnalyzer;
|
||||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(
|
|
||||||
new MethodReference(Object.class, "monitorExit", Object.class, void.class), null);
|
|
||||||
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
|
|
||||||
methodDep.use();
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(
|
|
||||||
new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null);
|
|
||||||
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
|
|
||||||
methodDep.use();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.dependency;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -26,6 +27,8 @@ public interface DependencyInfo {
|
||||||
|
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
|
|
||||||
|
ClassHierarchy getClassHierarchy();
|
||||||
|
|
||||||
Collection<MethodReference> getReachableMethods();
|
Collection<MethodReference> getReachableMethods();
|
||||||
|
|
||||||
Collection<FieldReference> getReachableFields();
|
Collection<FieldReference> getReachableFields();
|
||||||
|
|
|
@ -15,16 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
|
|
||||||
public interface DependencyListener {
|
public interface DependencyListener {
|
||||||
void started(DependencyAgent agent);
|
void started(DependencyAgent agent);
|
||||||
|
|
||||||
void classReached(DependencyAgent agent, String className, CallLocation location);
|
void classReached(DependencyAgent agent, String className);
|
||||||
|
|
||||||
void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location);
|
void methodReached(DependencyAgent agent, MethodDependency method);
|
||||||
|
|
||||||
void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location);
|
void fieldReached(DependencyAgent agent, FieldDependency field);
|
||||||
|
|
||||||
void completing(DependencyAgent agent);
|
void completing(DependencyAgent agent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -40,10 +41,10 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
ObjectArrayList<Transition> transitionList;
|
ObjectArrayList<Transition> transitionList;
|
||||||
String tag;
|
String tag;
|
||||||
private DependencyNode arrayItemNode;
|
private DependencyNode arrayItemNode;
|
||||||
private DependencyNode classValueNode;
|
DependencyNode classValueNode;
|
||||||
DependencyNode classNodeParent;
|
DependencyNode classNodeParent;
|
||||||
boolean classNodeComplete;
|
boolean classNodeComplete;
|
||||||
private int degree;
|
int degree;
|
||||||
boolean locked;
|
boolean locked;
|
||||||
MethodReference method;
|
MethodReference method;
|
||||||
ValueType typeFilter;
|
ValueType typeFilter;
|
||||||
|
@ -78,15 +79,14 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectArrayList<Transition> transitions = new ObjectArrayList<>(typeSet.getTransitions());
|
Transition[] transitions = typeSet.getTransitions().toArray(Transition.class);
|
||||||
List<ConsumerWithNode> consumerEntries = typeSet.getConsumers();
|
List<ConsumerWithNode> consumerEntries = typeSet.getConsumers();
|
||||||
|
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ObjectCursor<Transition> cursor : transitions) {
|
for (Transition transition : transitions) {
|
||||||
Transition transition = cursor.value;
|
|
||||||
if (transition.source.filter(type) && transition.filterType(type)) {
|
if (transition.source.filter(type) && transition.filterType(type)) {
|
||||||
dependencyAnalyzer.schedulePropagation(transition, type);
|
dependencyAnalyzer.schedulePropagation(transition, type);
|
||||||
}
|
}
|
||||||
|
@ -233,17 +233,24 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cachedTypeFilter == null) {
|
return getFilter().match(type);
|
||||||
String superClass;
|
}
|
||||||
if (typeFilter instanceof ValueType.Object) {
|
|
||||||
superClass = ((ValueType.Object) typeFilter).getClassName();
|
|
||||||
} else {
|
|
||||||
superClass = "java.lang.Object";
|
|
||||||
}
|
|
||||||
cachedTypeFilter = dependencyAnalyzer.getSuperClassFilter(superClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedTypeFilter.match(type);
|
DependencyTypeFilter getFilter() {
|
||||||
|
if (cachedTypeFilter == null) {
|
||||||
|
if (typeFilter == null) {
|
||||||
|
cachedTypeFilter = t -> true;
|
||||||
|
} else {
|
||||||
|
String superClass;
|
||||||
|
if (typeFilter instanceof ValueType.Object) {
|
||||||
|
superClass = ((ValueType.Object) typeFilter).getClassName();
|
||||||
|
} else {
|
||||||
|
superClass = typeFilter.toString();
|
||||||
|
}
|
||||||
|
cachedTypeFilter = dependencyAnalyzer.getSuperClassFilter(superClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cachedTypeFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConsumer(DependencyConsumer consumer) {
|
public void addConsumer(DependencyConsumer consumer) {
|
||||||
|
@ -262,8 +269,18 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(DependencyNode node, DependencyTypeFilter filter) {
|
public void connect(DependencyNode node, DependencyTypeFilter filter) {
|
||||||
|
if (connectWithoutChildNodes(node, filter)) {
|
||||||
|
connectArrayItemNodes(node);
|
||||||
|
|
||||||
|
if (classValueNode != null && classValueNode != this) {
|
||||||
|
classValueNode.connect(node.getClassValueNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean connectWithoutChildNodes(DependencyNode node, DependencyTypeFilter filter) {
|
||||||
if (this == node) {
|
if (this == node) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
throw new IllegalArgumentException("Node must not be null");
|
throw new IllegalArgumentException("Node must not be null");
|
||||||
|
@ -273,7 +290,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
transitionList = new ObjectArrayList<>();
|
transitionList = new ObjectArrayList<>();
|
||||||
}
|
}
|
||||||
if (transitions.containsKey(node)) {
|
if (transitions.containsKey(node)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transition transition = new Transition(this, node, filter);
|
Transition transition = new Transition(this, node, filter);
|
||||||
|
@ -285,7 +302,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
|
|
||||||
if (typeSet != null) {
|
if (typeSet != null) {
|
||||||
if (typeSet == node.typeSet) {
|
if (typeSet == node.typeSet) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (typeSet.transitions != null) {
|
if (typeSet.transitions != null) {
|
||||||
typeSet.transitions.add(transition);
|
typeSet.transitions.add(transition);
|
||||||
|
@ -303,14 +320,13 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectArrayItemNodes(node);
|
return true;
|
||||||
|
|
||||||
if (classValueNode != null && classValueNode != this) {
|
|
||||||
classValueNode.connect(node.getClassValueNode());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectArrayItemNodes(DependencyNode node) {
|
private void connectArrayItemNodes(DependencyNode node) {
|
||||||
|
if (degree > DEGREE_THRESHOLD || node.degree > DEGREE_THRESHOLD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!isArray(typeFilter) || !isArray(node.typeFilter)) {
|
if (!isArray(typeFilter) || !isArray(node.typeFilter)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -382,15 +398,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
@Override
|
@Override
|
||||||
public DependencyNode getArrayItem() {
|
public DependencyNode getArrayItem() {
|
||||||
if (arrayItemNode == null) {
|
if (arrayItemNode == null) {
|
||||||
ValueType itemTypeFilter = typeFilter instanceof ValueType.Array
|
arrayItemNode = dependencyAnalyzer.createArrayItemNode(this);
|
||||||
? ((ValueType.Array) typeFilter).getItemType()
|
|
||||||
: null;
|
|
||||||
arrayItemNode = dependencyAnalyzer.createNode(itemTypeFilter);
|
|
||||||
arrayItemNode.degree = degree + 1;
|
|
||||||
arrayItemNode.method = method;
|
|
||||||
if (DependencyAnalyzer.shouldTag) {
|
|
||||||
arrayItemNode.tag = tag + "[";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return arrayItemNode;
|
return arrayItemNode;
|
||||||
}
|
}
|
||||||
|
@ -398,13 +406,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
@Override
|
@Override
|
||||||
public DependencyNode getClassValueNode() {
|
public DependencyNode getClassValueNode() {
|
||||||
if (classValueNode == null) {
|
if (classValueNode == null) {
|
||||||
classValueNode = dependencyAnalyzer.createNode();
|
classValueNode = dependencyAnalyzer.createClassValueNode(degree, this);
|
||||||
classValueNode.degree = degree;
|
|
||||||
classValueNode.classValueNode = classValueNode;
|
|
||||||
classValueNode.classNodeParent = this;
|
|
||||||
if (DependencyAnalyzer.shouldTag) {
|
|
||||||
classValueNode.tag = tag + "@";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return classValueNode;
|
return classValueNode;
|
||||||
}
|
}
|
||||||
|
@ -507,6 +509,10 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<DependencyNode> findDomain() {
|
Collection<DependencyNode> findDomain() {
|
||||||
|
if (!dependencyAnalyzer.domainOptimizationEnabled()) {
|
||||||
|
return Collections.singleton(this);
|
||||||
|
}
|
||||||
|
|
||||||
Set<DependencyNode> visited = new LinkedHashSet<>(50);
|
Set<DependencyNode> visited = new LinkedHashSet<>(50);
|
||||||
Deque<DependencyNode> stack = new ArrayDeque<>(50);
|
Deque<DependencyNode> stack = new ArrayDeque<>(50);
|
||||||
stack.push(this);
|
stack.push(this);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import org.teavm.model.CallLocation;
|
|
||||||
|
|
||||||
public interface DependencyPlugin {
|
public interface DependencyPlugin {
|
||||||
void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location);
|
void methodReached(DependencyAgent agent, MethodDependency method);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ public class DependencyType {
|
||||||
private DependencyAnalyzer dependencyAnalyzer;
|
private DependencyAnalyzer dependencyAnalyzer;
|
||||||
private String name;
|
private String name;
|
||||||
int index;
|
int index;
|
||||||
|
boolean subtypeExists;
|
||||||
|
|
||||||
DependencyType(DependencyAnalyzer dependencyAnalyzer, String name, int index) {
|
DependencyType(DependencyAnalyzer dependencyAnalyzer, String name, int index) {
|
||||||
this.dependencyAnalyzer = dependencyAnalyzer;
|
this.dependencyAnalyzer = dependencyAnalyzer;
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
public interface DependencyTypeFilter {
|
public interface DependencyTypeFilter {
|
||||||
boolean match(DependencyType type);
|
boolean match(DependencyType type);
|
||||||
|
|
||||||
|
default int[] tryExtract(BitSet types) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.dependency;
|
||||||
|
|
||||||
class ExactTypeFilter implements DependencyTypeFilter {
|
class ExactTypeFilter implements DependencyTypeFilter {
|
||||||
String typeName;
|
String typeName;
|
||||||
|
int cache = -1;
|
||||||
|
|
||||||
ExactTypeFilter(String typeName) {
|
ExactTypeFilter(String typeName) {
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
|
@ -24,6 +25,13 @@ class ExactTypeFilter implements DependencyTypeFilter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean match(DependencyType type) {
|
public boolean match(DependencyType type) {
|
||||||
return typeName.equals(type.getName());
|
if (cache >= 0) {
|
||||||
|
return type.index == cache;
|
||||||
|
}
|
||||||
|
boolean result = typeName.equals(type.getName());
|
||||||
|
if (result) {
|
||||||
|
cache = type.index;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_ENTER_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_ENTER_SYNC_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_METHOD;
|
||||||
|
import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_SYNC_METHOD;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.common.ServiceRepository;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.model.BasicBlockReader;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ProgramReader;
|
||||||
|
import org.teavm.model.TryCatchBlockReader;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
|
DependencyNode instancesNode;
|
||||||
|
DependencyNode classesNode;
|
||||||
|
private Map<MethodReference, FastVirtualCallConsumer> virtualCallConsumers = new HashMap<>();
|
||||||
|
private Map<String, DependencyNode> subtypeNodes = new HashMap<>();
|
||||||
|
|
||||||
|
public FastDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
||||||
|
ServiceRepository services, Diagnostics diagnostics) {
|
||||||
|
super(classSource, classLoader, services, diagnostics);
|
||||||
|
|
||||||
|
instancesNode = new DependencyNode(this, null);
|
||||||
|
classesNode = new DependencyNode(this, null);
|
||||||
|
|
||||||
|
instancesNode.addConsumer(type -> {
|
||||||
|
getSubtypeNode(type.getName()).propagate(type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processMethod(MethodDependency methodDep) {
|
||||||
|
MethodReader method = methodDep.getMethod();
|
||||||
|
ProgramReader program = method.getProgram();
|
||||||
|
|
||||||
|
if (program != null) {
|
||||||
|
FastInstructionAnalyzer instructionAnalyzer = new FastInstructionAnalyzer(this);
|
||||||
|
instructionAnalyzer.setCaller(method.getReference());
|
||||||
|
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||||
|
block.readAllInstructions(instructionAnalyzer);
|
||||||
|
|
||||||
|
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
||||||
|
if (tryCatch.getExceptionType() != null) {
|
||||||
|
linkClass(tryCatch.getExceptionType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
methodDep.variableNodes = new DependencyNode[program.variableCount()];
|
||||||
|
for (int i = 0; i < methodDep.variableNodes.length; ++i) {
|
||||||
|
methodDep.variableNodes[i] = instancesNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
|
||||||
|
processAsyncMethod(methodDep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processAsyncMethod(MethodDependency methodDep) {
|
||||||
|
if (asyncSupported) {
|
||||||
|
linkMethod(MONITOR_ENTER_METHOD).use();
|
||||||
|
}
|
||||||
|
|
||||||
|
linkMethod(MONITOR_ENTER_SYNC_METHOD).use();
|
||||||
|
|
||||||
|
if (asyncSupported) {
|
||||||
|
linkMethod(MONITOR_EXIT_METHOD).use();
|
||||||
|
}
|
||||||
|
|
||||||
|
linkMethod(MONITOR_EXIT_SYNC_METHOD).use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createParameterNode(MethodReference method, ValueType type, int index) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createResultNode(MethodReference method) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createThrownNode(MethodReference method) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createFieldNode(FieldReference field, ValueType type) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createArrayItemNode(DependencyNode parent) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createClassValueNode(int degree, DependencyNode parent) {
|
||||||
|
return classesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DependencyNode getSubtypeNode(String type) {
|
||||||
|
if (type.equals("java.lang.Object")) {
|
||||||
|
return instancesNode;
|
||||||
|
}
|
||||||
|
return subtypeNodes.computeIfAbsent(type, key -> {
|
||||||
|
DependencyNode node = createNode();
|
||||||
|
|
||||||
|
defer(() -> {
|
||||||
|
int degree = 0;
|
||||||
|
while (degree < key.length() && key.charAt(degree) == '[') {
|
||||||
|
degree++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (degree > 0) {
|
||||||
|
ValueType fullType = ValueType.parse(key);
|
||||||
|
if (fullType instanceof ValueType.Object) {
|
||||||
|
String prefix = key.substring(0, degree) + "L";
|
||||||
|
String className = ((ValueType.Object) fullType).getClassName();
|
||||||
|
ClassReader cls = getClassSource().get(key);
|
||||||
|
if (cls != null) {
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
node.connect(getSubtypeNode(prefix + cls.getParent().replace('.', '/') + ";"));
|
||||||
|
} else {
|
||||||
|
node.connect(getSubtypeNode("java.lang.Object"));
|
||||||
|
}
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
node.connect(getSubtypeNode(prefix + itf.replace('.', '/') + ";"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.connect(getSubtypeNode("java.lang.Object"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ClassReader cls = getClassSource().get(key);
|
||||||
|
if (cls != null) {
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
node.connect(getSubtypeNode(cls.getParent()));
|
||||||
|
}
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
node.connect(getSubtypeNode(itf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FastVirtualCallConsumer getVirtualCallConsumer(MethodReference method) {
|
||||||
|
return virtualCallConsumers.computeIfAbsent(method, key -> {
|
||||||
|
FastVirtualCallConsumer consumer = new FastVirtualCallConsumer(instancesNode, key.getDescriptor(), this);
|
||||||
|
defer(() -> {
|
||||||
|
getSubtypeNode(method.getClassName()).addConsumer(consumer);
|
||||||
|
});
|
||||||
|
return consumer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean domainOptimizationEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.VariableReader;
|
||||||
|
|
||||||
|
class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
||||||
|
private FastDependencyAnalyzer dependencyAnalyzer;
|
||||||
|
private MethodReference callerMethod;
|
||||||
|
|
||||||
|
FastInstructionAnalyzer(FastDependencyAnalyzer dependencyAnalyzer) {
|
||||||
|
this.dependencyAnalyzer = dependencyAnalyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments) {
|
||||||
|
CallLocation callLocation = getCallLocation();
|
||||||
|
if (instance == null) {
|
||||||
|
dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation);
|
||||||
|
}
|
||||||
|
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method);
|
||||||
|
methodDep.addLocation(callLocation);
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments) {
|
||||||
|
dependencyAnalyzer.getVirtualCallConsumer(method).addLocation(getCallLocation());
|
||||||
|
|
||||||
|
dependencyAnalyzer.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
|
||||||
|
dependencyAnalyzer.linkMethod(methodImpl.getReference()).addLocation(getCallLocation());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneArray(VariableReader receiver, VariableReader array) {
|
||||||
|
DependencyNode arrayNode = getNode(array);
|
||||||
|
MethodDependency cloneDep = getAnalyzer().linkMethod(CLONE_METHOD);
|
||||||
|
cloneDep.addLocation(getCallLocation());
|
||||||
|
cloneDep.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DependencyNode getNode(VariableReader variable) {
|
||||||
|
return dependencyAnalyzer.instancesNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DependencyAnalyzer getAnalyzer() {
|
||||||
|
return dependencyAnalyzer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
class FastVirtualCallConsumer implements DependencyConsumer {
|
||||||
|
private final DependencyNode node;
|
||||||
|
private final MethodDescriptor methodDesc;
|
||||||
|
private final DependencyAnalyzer analyzer;
|
||||||
|
private final Map<MethodReference, CallLocation> callLocations = new LinkedHashMap<>();
|
||||||
|
private final Set<MethodDependency> methods = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
FastVirtualCallConsumer(DependencyNode node, MethodDescriptor methodDesc, DependencyAnalyzer analyzer) {
|
||||||
|
this.node = node;
|
||||||
|
this.methodDesc = methodDesc;
|
||||||
|
this.analyzer = analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void consume(DependencyType type) {
|
||||||
|
String className = type.getName();
|
||||||
|
if (DependencyAnalyzer.shouldLog) {
|
||||||
|
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
|
||||||
|
+ "Target class is " + className);
|
||||||
|
}
|
||||||
|
if (className.startsWith("[")) {
|
||||||
|
className = "java.lang.Object";
|
||||||
|
type = analyzer.getType(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDependency methodDep = analyzer.linkMethod(className, methodDesc);
|
||||||
|
if (!methods.add(methodDep)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CallLocation location : callLocations.values()) {
|
||||||
|
methodDep.addLocation(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!methodDep.isMissing()) {
|
||||||
|
methodDep.use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLocation(CallLocation location) {
|
||||||
|
if (callLocations.putIfAbsent(location.getMethod(), location) == null) {
|
||||||
|
for (MethodDependency method : methods) {
|
||||||
|
method.addLocation(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
|
||||||
|
@ -22,6 +28,9 @@ public class FieldDependency implements FieldDependencyInfo {
|
||||||
DependencyNode value;
|
DependencyNode value;
|
||||||
private FieldReader field;
|
private FieldReader field;
|
||||||
private FieldReference reference;
|
private FieldReference reference;
|
||||||
|
private List<LocationListener> locationListeners;
|
||||||
|
private Set<CallLocation> locations;
|
||||||
|
boolean activated;
|
||||||
|
|
||||||
FieldDependency(DependencyNode value, FieldReader field, FieldReference reference) {
|
FieldDependency(DependencyNode value, FieldReader field, FieldReference reference) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -47,4 +56,32 @@ public class FieldDependency implements FieldDependencyInfo {
|
||||||
public boolean isMissing() {
|
public boolean isMissing() {
|
||||||
return field == null;
|
return field == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FieldDependency addLocation(CallLocation location) {
|
||||||
|
DefaultCallGraphNode node = value.dependencyAnalyzer.callGraph.getNode(location.getMethod());
|
||||||
|
if (locations == null) {
|
||||||
|
locations = new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
if (locations.add(location)) {
|
||||||
|
node.addFieldAccess(reference, location.getSourceLocation());
|
||||||
|
if (locationListeners != null) {
|
||||||
|
for (LocationListener listener : locationListeners.toArray(new LocationListener[0])) {
|
||||||
|
listener.locationAdded(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLocationListener(LocationListener listener) {
|
||||||
|
if (locationListeners == null) {
|
||||||
|
locationListeners = new ArrayList<>();
|
||||||
|
locationListeners.add(listener);
|
||||||
|
if (locations != null) {
|
||||||
|
for (CallLocation location : locations.toArray(new CallLocation[0])) {
|
||||||
|
listener.locationAdded(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
|
||||||
|
public interface LocationListener {
|
||||||
|
void locationAdded(CallLocation location);
|
||||||
|
}
|
|
@ -15,7 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -31,6 +37,9 @@ public class MethodDependency implements MethodDependencyInfo {
|
||||||
boolean used;
|
boolean used;
|
||||||
DependencyPlugin dependencyPlugin;
|
DependencyPlugin dependencyPlugin;
|
||||||
boolean dependencyPluginAttached;
|
boolean dependencyPluginAttached;
|
||||||
|
private List<LocationListener> locationListeners;
|
||||||
|
private Set<CallLocation> locations;
|
||||||
|
boolean activated;
|
||||||
|
|
||||||
MethodDependency(DependencyAnalyzer dependencyAnalyzer, DependencyNode[] variableNodes, int parameterCount,
|
MethodDependency(DependencyAnalyzer dependencyAnalyzer, DependencyNode[] variableNodes, int parameterCount,
|
||||||
DependencyNode resultNode, DependencyNode thrown, MethodHolder method, MethodReference reference) {
|
DependencyNode resultNode, DependencyNode thrown, MethodHolder method, MethodReference reference) {
|
||||||
|
@ -100,6 +109,34 @@ public class MethodDependency implements MethodDependencyInfo {
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MethodDependency addLocation(CallLocation location) {
|
||||||
|
DefaultCallGraphNode node = dependencyAnalyzer.callGraph.getNode(location.getMethod());
|
||||||
|
if (locations == null) {
|
||||||
|
locations = new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
if (locations.add(location)) {
|
||||||
|
node.addCallSite(reference, location.getSourceLocation());
|
||||||
|
if (locationListeners != null) {
|
||||||
|
for (LocationListener listener : locationListeners.toArray(new LocationListener[0])) {
|
||||||
|
listener.locationAdded(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLocationListener(LocationListener listener) {
|
||||||
|
if (locationListeners == null) {
|
||||||
|
locationListeners = new ArrayList<>();
|
||||||
|
locationListeners.add(listener);
|
||||||
|
if (locations != null) {
|
||||||
|
for (CallLocation location : locations.toArray(new CallLocation[0])) {
|
||||||
|
listener.locationAdded(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public MethodDependency propagate(int parameterIndex, Class<?> type) {
|
public MethodDependency propagate(int parameterIndex, Class<?> type) {
|
||||||
return propagate(parameterIndex, dependencyAnalyzer.getType(type.getName()));
|
return propagate(parameterIndex, dependencyAnalyzer.getType(type.getName()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012 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 org.teavm.common.ServiceRepository;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class PreciseDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
|
public PreciseDependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader,
|
||||||
|
ServiceRepository services, Diagnostics diagnostics) {
|
||||||
|
super(classSource, classLoader, services, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processMethod(MethodDependency methodDep) {
|
||||||
|
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(this);
|
||||||
|
graphBuilder.buildGraph(methodDep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createParameterNode(MethodReference method, ValueType type, int index) {
|
||||||
|
DependencyNode node = createNode(type);
|
||||||
|
node.method = method;
|
||||||
|
if (shouldTag) {
|
||||||
|
node.setTag(method + ":" + index);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createResultNode(MethodReference method) {
|
||||||
|
DependencyNode node = createNode(method.getReturnType());
|
||||||
|
node.method = method;
|
||||||
|
if (shouldTag) {
|
||||||
|
node.setTag(method + ":RESULT");
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createThrownNode(MethodReference method) {
|
||||||
|
DependencyNode node = createNode();
|
||||||
|
node.method = method;
|
||||||
|
if (shouldTag) {
|
||||||
|
node.setTag(method + ":THROWN");
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createFieldNode(FieldReference field, ValueType type) {
|
||||||
|
DependencyNode node = createNode(type);
|
||||||
|
if (shouldTag) {
|
||||||
|
node.setTag(field.getClassName() + "#" + field.getFieldName());
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createArrayItemNode(DependencyNode parent) {
|
||||||
|
ValueType itemTypeFilter = parent.typeFilter instanceof ValueType.Array
|
||||||
|
? ((ValueType.Array) parent.typeFilter).getItemType()
|
||||||
|
: null;
|
||||||
|
DependencyNode node = createNode(itemTypeFilter);
|
||||||
|
node.degree = parent.degree + 1;
|
||||||
|
node.method = parent.method;
|
||||||
|
if (DependencyAnalyzer.shouldTag) {
|
||||||
|
node.tag = parent.tag + "[";
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DependencyNode createClassValueNode(int degree, DependencyNode parent) {
|
||||||
|
DependencyNode node = createNode();
|
||||||
|
node.degree = degree;
|
||||||
|
node.classValueNode = node;
|
||||||
|
node.classNodeParent = parent;
|
||||||
|
if (DependencyAnalyzer.shouldTag) {
|
||||||
|
node.tag = parent.tag + "@";
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean domainOptimizationEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
class SuperArrayFilter implements DependencyTypeFilter {
|
class SuperArrayFilter implements DependencyTypeFilter {
|
||||||
private DependencyAnalyzer analyzer;
|
private DependencyAnalyzer analyzer;
|
||||||
private DependencyTypeFilter itemTypeFilter;
|
private DependencyTypeFilter itemTypeFilter;
|
||||||
|
private BitSet knownTypes = new BitSet();
|
||||||
|
private BitSet cache = new BitSet();
|
||||||
|
|
||||||
SuperArrayFilter(DependencyAnalyzer analyzer, DependencyTypeFilter itemTypeFilter) {
|
SuperArrayFilter(DependencyAnalyzer analyzer, DependencyTypeFilter itemTypeFilter) {
|
||||||
this.analyzer = analyzer;
|
this.analyzer = analyzer;
|
||||||
|
@ -28,6 +31,16 @@ class SuperArrayFilter implements DependencyTypeFilter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean match(DependencyType type) {
|
public boolean match(DependencyType type) {
|
||||||
|
if (knownTypes.get(type.index)) {
|
||||||
|
return cache.get(type.index);
|
||||||
|
}
|
||||||
|
boolean result = matchCacheMiss(type);
|
||||||
|
knownTypes.set(type.index);
|
||||||
|
cache.set(type.index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchCacheMiss(DependencyType type) {
|
||||||
if (!type.getName().startsWith("[")) {
|
if (!type.getName().startsWith("[")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -42,4 +55,26 @@ class SuperArrayFilter implements DependencyTypeFilter {
|
||||||
}
|
}
|
||||||
return itemTypeFilter.match(analyzer.getType(typeName));
|
return itemTypeFilter.match(analyzer.getType(typeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] tryExtract(BitSet types) {
|
||||||
|
int[] result = itemTypeFilter.tryExtract(types);
|
||||||
|
if (result == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < result.length; ++i) {
|
||||||
|
String name = analyzer.types.get(i).getName();
|
||||||
|
int mapped;
|
||||||
|
if (name.startsWith("[")) {
|
||||||
|
mapped = analyzer.getType("[" + name).index;
|
||||||
|
} else if (name.startsWith("~")) {
|
||||||
|
mapped = analyzer.getType("[" + name.substring(1)).index;
|
||||||
|
} else {
|
||||||
|
mapped = analyzer.getType(ValueType.arrayOf(ValueType.object(name)).toString()).index;
|
||||||
|
}
|
||||||
|
result[i] = mapped;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,27 +15,40 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntIntHashMap;
|
import java.util.BitSet;
|
||||||
import com.carrotsearch.hppc.IntIntMap;
|
import org.teavm.common.OptionalPredicate;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
|
|
||||||
class SuperClassFilter implements DependencyTypeFilter {
|
class SuperClassFilter implements DependencyTypeFilter {
|
||||||
private ClassReaderSource classSource;
|
private static final int[] EMPTY_ARRAY = new int[0];
|
||||||
private String superType;
|
private DependencyType superType;
|
||||||
private IntIntMap cache = new IntIntHashMap();
|
private OptionalPredicate<String> predicate;
|
||||||
|
private BitSet knownTypes = new BitSet();
|
||||||
|
private BitSet cache = new BitSet();
|
||||||
|
|
||||||
SuperClassFilter(ClassReaderSource classSource, String superType) {
|
SuperClassFilter(DependencyAnalyzer dependencyAnalyzer, DependencyType superType) {
|
||||||
this.classSource = classSource;
|
|
||||||
this.superType = superType;
|
this.superType = superType;
|
||||||
|
predicate = dependencyAnalyzer.getClassHierarchy().getSuperclassPredicate(superType.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean match(DependencyType type) {
|
public boolean match(DependencyType type) {
|
||||||
int result = cache.getOrDefault(type.index, -1);
|
if (!superType.subtypeExists) {
|
||||||
if (result < 0) {
|
return superType.index == type.index;
|
||||||
result = classSource.isSuperType(superType, type.getName()).orElse(false) ? 1 : 0;
|
|
||||||
cache.put(type.index, result);
|
|
||||||
}
|
}
|
||||||
return result != 0;
|
if (knownTypes.get(type.index)) {
|
||||||
|
return cache.get(type.index);
|
||||||
|
}
|
||||||
|
boolean result = predicate.test(type.getName(), false);
|
||||||
|
knownTypes.set(type.index);
|
||||||
|
cache.set(type.index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] tryExtract(BitSet types) {
|
||||||
|
if (superType.subtypeExists) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return types.get(superType.index) ? new int[] { superType.index } : EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,14 @@ package org.teavm.dependency;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
import com.carrotsearch.hppc.IntHashSet;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
class Transition {
|
class Transition {
|
||||||
DependencyNode source;
|
DependencyNode source;
|
||||||
DependencyNode destination;
|
DependencyNode destination;
|
||||||
DependencyTypeFilter filter;
|
DependencyTypeFilter filter;
|
||||||
private BitSet knownFilteredOffTypes;
|
|
||||||
IntHashSet pendingTypes;
|
IntHashSet pendingTypes;
|
||||||
byte destSubsetOfSrc;
|
byte destSubsetOfSrc;
|
||||||
|
|
||||||
|
@ -79,7 +77,7 @@ class Transition {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldMergeDomains() {
|
boolean shouldMergeDomains() {
|
||||||
if (filter != null || !isDestSubsetOfSrc()) {
|
if (!source.dependencyAnalyzer.domainOptimizationEnabled() || filter != null || !isDestSubsetOfSrc()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (destination.typeSet == null) {
|
if (destination.typeSet == null) {
|
||||||
|
@ -163,18 +161,7 @@ class Transition {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (knownFilteredOffTypes != null && knownFilteredOffTypes.get(type.index)) {
|
return filter.match(type);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!filter.match(type)) {
|
|
||||||
if (knownFilteredOffTypes == null) {
|
|
||||||
knownFilteredOffTypes = new BitSet(64);
|
|
||||||
}
|
|
||||||
knownFilteredOffTypes.set(type.index);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean pointsToDomainOrigin() {
|
boolean pointsToDomainOrigin() {
|
||||||
|
@ -198,7 +185,8 @@ class Transition {
|
||||||
|
|
||||||
ValueType sourceType = source.typeFilter;
|
ValueType sourceType = source.typeFilter;
|
||||||
ValueType destType = destination.typeFilter;
|
ValueType destType = destination.typeFilter;
|
||||||
ClassReaderSource classSource = source.dependencyAnalyzer.getClassSource();
|
ClassHierarchy hierarchy = source.dependencyAnalyzer.getClassHierarchy();
|
||||||
return classSource.isSuperType(sourceType, destType).orElse(false);
|
|
||||||
|
return hierarchy.isSuperType(sourceType, destType, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ class TypeSet {
|
||||||
|
|
||||||
DependencyType[] getTypes() {
|
DependencyType[] getTypes() {
|
||||||
if (this.types != null) {
|
if (this.types != null) {
|
||||||
DependencyType[] types = new DependencyType[this.types.cardinality()];
|
DependencyType[] types = new DependencyType[typesCount];
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
||||||
DependencyType type = dependencyAnalyzer.types.get(index);
|
DependencyType type = dependencyAnalyzer.types.get(index);
|
||||||
|
@ -95,12 +95,33 @@ class TypeSet {
|
||||||
int j = 0;
|
int j = 0;
|
||||||
DependencyType[] types;
|
DependencyType[] types;
|
||||||
if (this.types != null) {
|
if (this.types != null) {
|
||||||
types = new DependencyType[this.types.cardinality()];
|
int[] filteredTypes = null;
|
||||||
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
if (typesCount > 15) {
|
||||||
DependencyType type = dependencyAnalyzer.types.get(index);
|
filteredTypes = filter != null ? filter.tryExtract(this.types) : null;
|
||||||
if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type)
|
if (filteredTypes == null) {
|
||||||
&& (filter == null || filter.match(type))) {
|
filteredTypes = sourceNode.getFilter().tryExtract(this.types);
|
||||||
types[j++] = type;
|
}
|
||||||
|
if (filteredTypes == null) {
|
||||||
|
filteredTypes = targetNode.getFilter().tryExtract(this.types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filteredTypes != null) {
|
||||||
|
types = new DependencyType[filteredTypes.length];
|
||||||
|
for (int index : filteredTypes) {
|
||||||
|
DependencyType type = dependencyAnalyzer.types.get(index);
|
||||||
|
if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type)
|
||||||
|
&& (filter == null || filter.match(type))) {
|
||||||
|
types[j++] = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
types = new DependencyType[typesCount];
|
||||||
|
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
||||||
|
DependencyType type = dependencyAnalyzer.types.get(index);
|
||||||
|
if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type)
|
||||||
|
&& (filter == null || filter.match(type))) {
|
||||||
|
types[j++] = type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.smallTypes != null) {
|
} else if (this.smallTypes != null) {
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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.dependency;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
|
||||||
|
class VirtualCallConsumer implements DependencyConsumer {
|
||||||
|
private final DependencyNode node;
|
||||||
|
private final MethodDescriptor methodDesc;
|
||||||
|
private final DependencyAnalyzer analyzer;
|
||||||
|
private final DependencyNode[] parameters;
|
||||||
|
private final DependencyNode result;
|
||||||
|
private final CallLocation location;
|
||||||
|
private final BitSet knownTypes = new BitSet();
|
||||||
|
private DependencyGraphBuilder.ExceptionConsumer exceptionConsumer;
|
||||||
|
private DependencyTypeFilter filter;
|
||||||
|
|
||||||
|
VirtualCallConsumer(DependencyNode node, String filterClass,
|
||||||
|
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
|
||||||
|
DependencyNode result, CallLocation location,
|
||||||
|
DependencyGraphBuilder.ExceptionConsumer exceptionConsumer) {
|
||||||
|
this.node = node;
|
||||||
|
this.filter = analyzer.getSuperClassFilter(filterClass);
|
||||||
|
this.methodDesc = methodDesc;
|
||||||
|
this.analyzer = analyzer;
|
||||||
|
this.parameters = parameters;
|
||||||
|
this.result = result;
|
||||||
|
this.location = location;
|
||||||
|
this.exceptionConsumer = exceptionConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void consume(DependencyType type) {
|
||||||
|
if (!filter.match(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (knownTypes.get(type.index)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
knownTypes.set(type.index);
|
||||||
|
|
||||||
|
String className = type.getName();
|
||||||
|
if (DependencyAnalyzer.shouldLog) {
|
||||||
|
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
|
||||||
|
+ "Target class is " + className);
|
||||||
|
}
|
||||||
|
if (className.startsWith("[")) {
|
||||||
|
className = "java.lang.Object";
|
||||||
|
type = analyzer.getType(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDependency methodDep = analyzer.linkMethod(className, methodDesc);
|
||||||
|
methodDep.addLocation(location);
|
||||||
|
if (!methodDep.isMissing()) {
|
||||||
|
methodDep.use();
|
||||||
|
DependencyNode[] targetParams = methodDep.getVariables();
|
||||||
|
if (parameters[0] != null && targetParams[0] != null) {
|
||||||
|
parameters[0].connect(targetParams[0],
|
||||||
|
analyzer.getSuperClassFilter(methodDep.getMethod().getOwnerName()));
|
||||||
|
}
|
||||||
|
for (int i = 1; i < parameters.length; ++i) {
|
||||||
|
if (parameters[i] != null && targetParams[i] != null) {
|
||||||
|
parameters[i].connect(targetParams[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != null && methodDep.getResult() != null) {
|
||||||
|
methodDep.getResult().connect(result);
|
||||||
|
}
|
||||||
|
methodDep.getThrown().addConsumer(exceptionConsumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,7 +90,7 @@ public class Problem implements Serializable {
|
||||||
default:
|
default:
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
int digitsEnd = passDigits(next);
|
int digitsEnd = skipDigits(next);
|
||||||
if (digitsEnd == next) {
|
if (digitsEnd == next) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ public class Problem implements Serializable {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int passDigits(int index) {
|
private int skipDigits(int index) {
|
||||||
while (index < text.length() && Character.isDigit(text.charAt(index))) {
|
while (index < text.length() && Character.isDigit(text.charAt(index))) {
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
129
core/src/main/java/org/teavm/model/ClassHierarchy.java
Normal file
129
core/src/main/java/org/teavm/model/ClassHierarchy.java
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.common.OptionalPredicate;
|
||||||
|
|
||||||
|
public class ClassHierarchy {
|
||||||
|
private final ClassReaderSource classSource;
|
||||||
|
private final Map<String, OptionalPredicate<String>> superclassPredicateCache = new HashMap<>();
|
||||||
|
|
||||||
|
public ClassHierarchy(ClassReaderSource classSource) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
superclassPredicateCache.put("java.lang.Object", (c, d) -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuperType(ValueType superType, ValueType subType, boolean defaultValue) {
|
||||||
|
if (superType.equals(subType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (superType instanceof ValueType.Primitive || subType instanceof ValueType.Primitive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (superType.isObject("java.lang.Object")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (superType instanceof ValueType.Object && subType instanceof ValueType.Object) {
|
||||||
|
return isSuperType(((ValueType.Object) superType).getClassName(),
|
||||||
|
((ValueType.Object) subType).getClassName(), defaultValue);
|
||||||
|
} else if (superType instanceof ValueType.Array & subType instanceof ValueType.Array) {
|
||||||
|
return isSuperType(((ValueType.Array) superType).getItemType(), ((ValueType.Array) subType).getItemType(),
|
||||||
|
defaultValue);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuperType(String superType, String subType, boolean defaultValue) {
|
||||||
|
if (subType.equals(superType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return getSuperclassPredicate(superType).test(subType, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalPredicate<String> getSuperclassPredicate(String superclass) {
|
||||||
|
return superclassPredicateCache.computeIfAbsent(superclass, SuperclassPredicate::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuperclassPredicate implements OptionalPredicate<String> {
|
||||||
|
private final String superclass;
|
||||||
|
private final ObjectByteMap<String> cache = new ObjectByteHashMap<>(100, 0.5);
|
||||||
|
|
||||||
|
SuperclassPredicate(String superclass) {
|
||||||
|
this.superclass = superclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(String value, boolean defaultResult) {
|
||||||
|
if (value.startsWith("[") || value.startsWith("~")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (test(value)) {
|
||||||
|
case 1:
|
||||||
|
return true;
|
||||||
|
case 2:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return defaultResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte test(String value) {
|
||||||
|
if (value.equals(superclass)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
byte result = cache.get(value);
|
||||||
|
if (result == 0) {
|
||||||
|
result = testCacheMiss(value);
|
||||||
|
cache.put(value, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte testCacheMiss(String value) {
|
||||||
|
if (value.equals(superclass)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classSource.get(value);
|
||||||
|
if (cls == null) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
if (test(cls.getParent()) == 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
if (test(itf) == 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,10 @@ public interface ClassHolderSource extends ClassReaderSource {
|
||||||
return (MethodHolder) resolveImplementation(method);
|
return (MethodHolder) resolveImplementation(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default MethodHolder resolveMutableImplementation(String className, MethodDescriptor descriptor) {
|
||||||
|
return (MethodHolder) resolveImplementation(className, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
default FieldHolder resolveMutable(FieldReference field) {
|
default FieldHolder resolveMutable(FieldReference field) {
|
||||||
return (FieldHolder) resolve(field);
|
return (FieldHolder) resolve(field);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
|
|
||||||
public interface ClassHolderTransformer {
|
public interface ClassHolderTransformer {
|
||||||
void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics);
|
void transformClass(ClassHolder cls, ClassHolderTransformerContext context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
|
||||||
|
public interface ClassHolderTransformerContext {
|
||||||
|
ClassHierarchy getHierarchy();
|
||||||
|
|
||||||
|
Diagnostics getDiagnostics();
|
||||||
|
|
||||||
|
IncrementalDependencyRegistration getIncrementalCache();
|
||||||
|
}
|
|
@ -99,6 +99,10 @@ public interface ClassReaderSource {
|
||||||
methodReference.getDescriptor(), new HashSet<>());
|
methodReference.getDescriptor(), new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default MethodReader resolveImplementation(String className, MethodDescriptor descriptor) {
|
||||||
|
return ClassReaderSourceHelper.resolveMethodImplementation(this, className, descriptor, new HashSet<>());
|
||||||
|
}
|
||||||
|
|
||||||
default FieldReader resolve(FieldReference field) {
|
default FieldReader resolve(FieldReference field) {
|
||||||
return getAncestors(field.getClassName())
|
return getAncestors(field.getClassName())
|
||||||
.map(cls -> cls.getField(field.getFieldName()))
|
.map(cls -> cls.getField(field.getFieldName()))
|
||||||
|
@ -115,24 +119,4 @@ public interface ClassReaderSource {
|
||||||
default Optional<Boolean> isSuperType(String superType, String subType) {
|
default Optional<Boolean> isSuperType(String superType, String subType) {
|
||||||
return ClassReaderSourceHelper.isSuperType(this, superType, subType);
|
return ClassReaderSourceHelper.isSuperType(this, superType, subType);
|
||||||
}
|
}
|
||||||
|
|
||||||
default Optional<Boolean> isSuperType(ValueType superType, ValueType subType) {
|
|
||||||
if (superType.equals(subType)) {
|
|
||||||
return Optional.of(true);
|
|
||||||
}
|
|
||||||
if (superType instanceof ValueType.Primitive || subType instanceof ValueType.Primitive) {
|
|
||||||
return Optional.of(false);
|
|
||||||
}
|
|
||||||
if (superType.isObject("java.lang.Object")) {
|
|
||||||
return Optional.of(true);
|
|
||||||
}
|
|
||||||
if (superType instanceof ValueType.Object && subType instanceof ValueType.Object) {
|
|
||||||
return isSuperType(((ValueType.Object) superType).getClassName(),
|
|
||||||
((ValueType.Object) subType).getClassName());
|
|
||||||
} else if (superType instanceof ValueType.Array & subType instanceof ValueType.Array) {
|
|
||||||
return isSuperType(((ValueType.Array) superType).getItemType(), ((ValueType.Array) subType).getItemType());
|
|
||||||
} else {
|
|
||||||
return Optional.of(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class MethodHolder extends MemberHolder implements MethodReader {
|
||||||
private Program program;
|
private Program program;
|
||||||
private AnnotationValue annotationDefault;
|
private AnnotationValue annotationDefault;
|
||||||
private AnnotationContainer[] parameterAnnotations;
|
private AnnotationContainer[] parameterAnnotations;
|
||||||
|
private MethodReference reference;
|
||||||
|
|
||||||
public MethodHolder(MethodDescriptor descriptor) {
|
public MethodHolder(MethodDescriptor descriptor) {
|
||||||
super(descriptor.getName());
|
super(descriptor.getName());
|
||||||
|
@ -80,6 +81,7 @@ public class MethodHolder extends MemberHolder implements MethodReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOwner(ClassHolder owner) {
|
void setOwner(ClassHolder owner) {
|
||||||
|
reference = null;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +92,13 @@ public class MethodHolder extends MemberHolder implements MethodReader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodReference getReference() {
|
public MethodReference getReference() {
|
||||||
return owner != null ? new MethodReference(owner.getName(), descriptor) : null;
|
if (owner == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (reference == null) {
|
||||||
|
reference = new MethodReference(owner.getName(), descriptor);
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -140,7 +140,7 @@ public class MethodReference implements Serializable {
|
||||||
@JsonValue
|
@JsonValue
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (reprCache == null) {
|
if (reprCache == null) {
|
||||||
reprCache = className + "." + name + signatureToString();
|
reprCache = className + "." + getDescriptor().toString();
|
||||||
}
|
}
|
||||||
return reprCache;
|
return reprCache;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
public interface ProgramCache {
|
import java.util.function.Supplier;
|
||||||
Program get(MethodReference method);
|
import org.teavm.cache.CacheStatus;
|
||||||
|
|
||||||
void store(MethodReference method, Program program);
|
public interface ProgramCache {
|
||||||
|
Program get(MethodReference method, CacheStatus status);
|
||||||
|
|
||||||
|
void store(MethodReference method, Program program, Supplier<String[]> dependencies);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ReferenceCache {
|
public class ReferenceCache {
|
||||||
private Map<MethodReference, MethodReference> referenceCache = new HashMap<>();
|
private Map<String, Map<MethodDescriptor, MethodReference>> referenceCache = new HashMap<>();
|
||||||
private Map<FieldReference, FieldReference> fieldRefenceCache = new HashMap<>();
|
private Map<FieldReference, FieldReference> fieldRefenceCache = new HashMap<>();
|
||||||
private Map<MethodDescriptor, MethodDescriptor> descriptorCache = new HashMap<>();
|
private Map<MethodDescriptor, MethodDescriptor> descriptorCache = new HashMap<>();
|
||||||
private Map<ValueType, ValueType> valueTypeCache = new HashMap<>();
|
private Map<ValueType, ValueType> valueTypeCache = new HashMap<>();
|
||||||
|
@ -29,18 +29,13 @@ public class ReferenceCache {
|
||||||
private Map<String, ValueType> valueTypeParseCache = new HashMap<>();
|
private Map<String, ValueType> valueTypeParseCache = new HashMap<>();
|
||||||
|
|
||||||
public MethodReference getCached(MethodReference reference) {
|
public MethodReference getCached(MethodReference reference) {
|
||||||
MethodReference result = referenceCache.get(reference);
|
return getCached(reference.getClassName(), reference.getDescriptor());
|
||||||
if (result == null) {
|
}
|
||||||
MethodDescriptor descriptor = getCached(reference.getDescriptor());
|
|
||||||
String className = getCached(reference.getClassName());
|
public MethodReference getCached(String className, MethodDescriptor descriptor) {
|
||||||
if (descriptor != reference.getDescriptor() || className != reference.getClassName()) {
|
return referenceCache
|
||||||
result = new MethodReference(className, descriptor);
|
.computeIfAbsent(className, key -> new HashMap<>())
|
||||||
} else {
|
.computeIfAbsent(descriptor, key -> new MethodReference(className, descriptor));
|
||||||
result = reference;
|
|
||||||
}
|
|
||||||
referenceCache.put(result, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodDescriptor getCached(MethodDescriptor descriptor) {
|
public MethodDescriptor getCached(MethodDescriptor descriptor) {
|
||||||
|
@ -117,15 +112,6 @@ public class ReferenceCache {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference parseReferenceCached(String value) {
|
|
||||||
MethodReference result = referenceParseCache.get(value);
|
|
||||||
if (result == null) {
|
|
||||||
result = getCached(MethodReference.parse(value));
|
|
||||||
referenceParseCache.put(value, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MethodDescriptor parseDescriptorCached(String value) {
|
public MethodDescriptor parseDescriptorCached(String value) {
|
||||||
MethodDescriptor result = descriptorParseCache.get(value);
|
MethodDescriptor result = descriptorParseCache.get(value);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.teavm.dependency.FieldDependencyInfo;
|
||||||
import org.teavm.dependency.MethodDependencyInfo;
|
import org.teavm.dependency.MethodDependencyInfo;
|
||||||
import org.teavm.dependency.ValueDependencyInfo;
|
import org.teavm.dependency.ValueDependencyInfo;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
|
@ -402,7 +403,7 @@ public class ClassInference {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateAlongCasts() {
|
private void propagateAlongCasts() {
|
||||||
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
||||||
|
|
||||||
for (ValueCast cast : casts) {
|
for (ValueCast cast : casts) {
|
||||||
int fromNode = nodeMapping[packNodeAndDegree(cast.fromVariable, 0)];
|
int fromNode = nodeMapping[packNodeAndDegree(cast.fromVariable, 0)];
|
||||||
|
@ -429,7 +430,7 @@ public class ClassInference {
|
||||||
type = ValueType.object(className);
|
type = ValueType.object(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classSource.isSuperType(cast.targetType, type).orElse(false)) {
|
if (hierarchy.isSuperType(cast.targetType, type, false)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
nodeChanged[toNode] = true;
|
nodeChanged[toNode] = true;
|
||||||
targetTypes.add(cursor.value);
|
targetTypes.add(cursor.value);
|
||||||
|
@ -499,11 +500,11 @@ public class ClassInference {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateException(String thrownTypeName, BasicBlock block) {
|
private void propagateException(String thrownTypeName, BasicBlock block) {
|
||||||
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
||||||
|
|
||||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
String expectedType = tryCatch.getExceptionType();
|
String expectedType = tryCatch.getExceptionType();
|
||||||
if (expectedType == null || classSource.isSuperType(expectedType, thrownTypeName).orElse(false)) {
|
if (expectedType == null || hierarchy.isSuperType(expectedType, thrownTypeName, false)) {
|
||||||
if (tryCatch.getHandler().getExceptionVariable() == null) {
|
if (tryCatch.getHandler().getExceptionVariable() == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.model.emit;
|
package org.teavm.model.emit;
|
||||||
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
|
@ -52,12 +53,14 @@ public final class ProgramEmitter {
|
||||||
private Program program;
|
private Program program;
|
||||||
private BasicBlock block;
|
private BasicBlock block;
|
||||||
ClassReaderSource classSource;
|
ClassReaderSource classSource;
|
||||||
|
ClassHierarchy hierarchy;
|
||||||
private TextLocation currentLocation;
|
private TextLocation currentLocation;
|
||||||
|
|
||||||
private ProgramEmitter(Program program, BasicBlock block, ClassReaderSource classSource) {
|
private ProgramEmitter(Program program, BasicBlock block, ClassHierarchy hierarchy) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.block = block;
|
this.block = block;
|
||||||
this.classSource = classSource;
|
this.classSource = hierarchy.getClassSource();
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
|
@ -218,7 +221,7 @@ public final class ProgramEmitter {
|
||||||
|
|
||||||
public ValueEmitter invoke(MethodReference method, ValueEmitter... arguments) {
|
public ValueEmitter invoke(MethodReference method, ValueEmitter... arguments) {
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
if (!classSource.isSuperType(method.parameterType(i), arguments[i].getType()).orElse(true)) {
|
if (!hierarchy.isSuperType(method.parameterType(i), arguments[i].getType(), true)) {
|
||||||
throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is "
|
throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is "
|
||||||
+ "not compatible with method " + method);
|
+ "not compatible with method " + method);
|
||||||
}
|
}
|
||||||
|
@ -387,13 +390,13 @@ public final class ProgramEmitter {
|
||||||
block.add(insn);
|
block.add(insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProgramEmitter create(MethodHolder method, ClassReaderSource classSource) {
|
public static ProgramEmitter create(MethodHolder method, ClassHierarchy classSource) {
|
||||||
ProgramEmitter pe = create(method.getDescriptor(), classSource);
|
ProgramEmitter pe = create(method.getDescriptor(), classSource);
|
||||||
method.setProgram(pe.getProgram());
|
method.setProgram(pe.getProgram());
|
||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProgramEmitter create(MethodDescriptor method, ClassReaderSource classSource) {
|
public static ProgramEmitter create(MethodDescriptor method, ClassHierarchy classSource) {
|
||||||
Program program = new Program();
|
Program program = new Program();
|
||||||
BasicBlock zeroBlock = program.createBasicBlock();
|
BasicBlock zeroBlock = program.createBasicBlock();
|
||||||
BasicBlock block = program.createBasicBlock();
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
@ -483,7 +486,7 @@ public final class ProgramEmitter {
|
||||||
return new StringBuilderEmitter(this);
|
return new StringBuilderEmitter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProgramEmitter create(Program program, ClassReaderSource classSource) {
|
public static ProgramEmitter create(Program program, ClassHierarchy classSource) {
|
||||||
return new ProgramEmitter(program, null, classSource);
|
return new ProgramEmitter(program, null, classSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package org.teavm.model.emit;
|
package org.teavm.model.emit;
|
||||||
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -424,16 +424,15 @@ public class ValueEmitter {
|
||||||
throw new EmitException("Can't invoke method on non-object type: " + type);
|
throw new EmitException("Can't invoke method on non-object type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassReaderSource classSource = pe.getClassSource();
|
ClassHierarchy hierarchy = pe.hierarchy;
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
if (!classSource.isSuperType(method.parameterType(i), arguments[i].getType()).orElse(false)) {
|
if (!hierarchy.isSuperType(method.parameterType(i), arguments[i].getType(), false)) {
|
||||||
throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is "
|
throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is "
|
||||||
+ "not compatible with method " + method);
|
+ "not compatible with method " + method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pe.classSource.isSuperType(method.getClassName(), ((ValueType.Object) type).getClassName())
|
if (!hierarchy.isSuperType(method.getClassName(), ((ValueType.Object) type).getClassName(), true)) {
|
||||||
.orElse(true)) {
|
|
||||||
throw new EmitException("Can't call " + method + " on non-compatible class " + type);
|
throw new EmitException("Can't call " + method + " on non-compatible class " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +638,7 @@ public class ValueEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void raise() {
|
public void raise() {
|
||||||
if (!pe.classSource.isSuperType(ValueType.object("java.lang.Throwable"), type).orElse(true)) {
|
if (!pe.hierarchy.isSuperType(ValueType.object("java.lang.Throwable"), type, true)) {
|
||||||
throw new EmitException("Can't throw non-exception value: " + type);
|
throw new EmitException("Can't throw non-exception value: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,7 +654,7 @@ public class ValueEmitter {
|
||||||
public ValueEmitter cast(ValueType type) {
|
public ValueEmitter cast(ValueType type) {
|
||||||
if (type.equals(this.type)) {
|
if (type.equals(this.type)) {
|
||||||
return this;
|
return this;
|
||||||
} else if (pe.classSource.isSuperType(type, this.type).orElse(false)) {
|
} else if (pe.hierarchy.isSuperType(type, this.type, false)) {
|
||||||
return pe.var(variable.getIndex(), type);
|
return pe.var(variable.getIndex(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +734,7 @@ public class ValueEmitter {
|
||||||
PrimitiveType primitiveType = ((ValueType.Primitive) this.type).getKind();
|
PrimitiveType primitiveType = ((ValueType.Primitive) this.type).getKind();
|
||||||
String boxClassName = getPrimitiveClassName(primitiveType);
|
String boxClassName = getPrimitiveClassName(primitiveType);
|
||||||
ValueEmitter result = invokeValueOf(boxClassName);
|
ValueEmitter result = invokeValueOf(boxClassName);
|
||||||
if (!pe.getClassSource().isSuperType(targetClass, boxClassName).orElse(false)) {
|
if (!pe.hierarchy.isSuperType(targetClass, boxClassName, false)) {
|
||||||
throw new EmitException("Can't convert " + this.type + " to " + targetClass);
|
throw new EmitException("Can't convert " + this.type + " to " + targetClass);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -837,7 +836,7 @@ public class ValueEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueEmitter assertIs(ValueType type) {
|
public ValueEmitter assertIs(ValueType type) {
|
||||||
if (!pe.classSource.isSuperType(type, this.type).orElse(true)) {
|
if (!pe.hierarchy.isSuperType(type, this.type, true)) {
|
||||||
throw new EmitException("Value type " + this.type + " is not subtype of " + type);
|
throw new EmitException("Value type " + this.type + " is not subtype of " + type);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -31,8 +31,8 @@ import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.Function;
|
import org.teavm.interop.Function;
|
||||||
import org.teavm.model.BasicBlockReader;
|
import org.teavm.model.BasicBlockReader;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -65,7 +65,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
if (method.getMethod() == null || method.getMethod().getProgram() == null) {
|
if (method.getMethod() == null || method.getMethod().getProgram() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,10 +127,10 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
private void processInvocation(DependencyAgent agent, CallLocation location, String functionClassName,
|
private void processInvocation(DependencyAgent agent, CallLocation location, String functionClassName,
|
||||||
String targetClassName, String methodName) {
|
String targetClassName, String methodName) {
|
||||||
Diagnostics diagnostics = agent.getDiagnostics();
|
Diagnostics diagnostics = agent.getDiagnostics();
|
||||||
ClassReaderSource classSource = agent.getClassSource();
|
ClassHierarchy hierarchy = agent.getClassHierarchy();
|
||||||
boolean valid = true;
|
boolean valid = true;
|
||||||
|
|
||||||
ClassReader functionClass = classSource.get(functionClassName);
|
ClassReader functionClass = hierarchy.getClassSource().get(functionClassName);
|
||||||
if (functionClass == null) {
|
if (functionClass == null) {
|
||||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||||
valid = false;
|
valid = false;
|
||||||
|
@ -139,7 +139,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassReader targetClass = classSource.get(targetClassName);
|
ClassReader targetClass = hierarchy.getClassSource().get(targetClassName);
|
||||||
if (targetClass == null) {
|
if (targetClass == null) {
|
||||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||||
valid = false;
|
valid = false;
|
||||||
|
@ -168,7 +168,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MethodReader> signatureCandidates = candidates.stream()
|
List<MethodReader> signatureCandidates = candidates.stream()
|
||||||
.filter(method -> matchSignature(classSource, sam, method))
|
.filter(method -> matchSignature(hierarchy, sam, method))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (signatureCandidates.isEmpty()) {
|
if (signatureCandidates.isEmpty()) {
|
||||||
if (candidates.size() == 1) {
|
if (candidates.size() == 1) {
|
||||||
|
@ -181,12 +181,14 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodReader resolvedMethod = findMostSpecific(diagnostics, location, classSource, signatureCandidates);
|
MethodReader resolvedMethod = findMostSpecific(diagnostics, location, hierarchy, signatureCandidates);
|
||||||
if (resolvedMethod != null) {
|
if (resolvedMethod != null) {
|
||||||
MethodReference reference = resolvedMethod.getReference();
|
MethodReference reference = resolvedMethod.getReference();
|
||||||
resolvedMethods.put(new ExportedMethodKey(functionClassName, targetClassName, methodName), reference);
|
resolvedMethods.put(new ExportedMethodKey(functionClassName, targetClassName, methodName), reference);
|
||||||
exportedMethods.add(reference);
|
exportedMethods.add(reference);
|
||||||
agent.linkMethod(reference, location).use();
|
MethodDependency dep = agent.linkMethod(reference);
|
||||||
|
dep.addLocation(location);
|
||||||
|
dep.use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,13 +218,13 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReader findMostSpecific(Diagnostics diagnostics, CallLocation location,
|
private MethodReader findMostSpecific(Diagnostics diagnostics, CallLocation location,
|
||||||
ClassReaderSource classSource, List<MethodReader> methods) {
|
ClassHierarchy hierarchy, List<MethodReader> methods) {
|
||||||
MethodReader mostSpecificSoFar = methods.get(0);
|
MethodReader mostSpecificSoFar = methods.get(0);
|
||||||
for (int i = 1; i < methods.size(); ++i) {
|
for (int i = 1; i < methods.size(); ++i) {
|
||||||
MethodReader candidate = methods.get(i);
|
MethodReader candidate = methods.get(i);
|
||||||
if (matchSignature(classSource, mostSpecificSoFar, candidate)) {
|
if (matchSignature(hierarchy, mostSpecificSoFar, candidate)) {
|
||||||
mostSpecificSoFar = candidate;
|
mostSpecificSoFar = candidate;
|
||||||
} else if (!matchSignature(classSource, candidate, mostSpecificSoFar)) {
|
} else if (!matchSignature(hierarchy, candidate, mostSpecificSoFar)) {
|
||||||
diagnostics.error(location, "Ambiguous methods found for this export, examples are '{{m0}}' "
|
diagnostics.error(location, "Ambiguous methods found for this export, examples are '{{m0}}' "
|
||||||
+ "and {{m1}}", candidate, mostSpecificSoFar);
|
+ "and {{m1}}", candidate, mostSpecificSoFar);
|
||||||
return null;
|
return null;
|
||||||
|
@ -232,15 +234,14 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
||||||
return mostSpecificSoFar;
|
return mostSpecificSoFar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matchSignature(ClassReaderSource classSource, MethodReader functionMethod,
|
private boolean matchSignature(ClassHierarchy hierarchy, MethodReader functionMethod,
|
||||||
MethodReader candidateMethod) {
|
MethodReader candidateMethod) {
|
||||||
if (functionMethod.parameterCount() > candidateMethod.parameterCount()) {
|
if (functionMethod.parameterCount() > candidateMethod.parameterCount()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < functionMethod.parameterCount(); ++i) {
|
for (int i = 0; i < functionMethod.parameterCount(); ++i) {
|
||||||
if (!classSource.isSuperType(functionMethod.parameterType(i),
|
if (!hierarchy.isSuperType(functionMethod.parameterType(i), candidateMethod.parameterType(i), false)) {
|
||||||
candidateMethod.parameterType(i)).orElse(false)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,12 @@ package org.teavm.model.optimization;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.teavm.common.OptionalPredicate;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.dependency.MethodDependencyInfo;
|
import org.teavm.dependency.MethodDependencyInfo;
|
||||||
import org.teavm.dependency.ValueDependencyInfo;
|
import org.teavm.dependency.ValueDependencyInfo;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
|
@ -34,12 +36,14 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
public class Devirtualization {
|
public class Devirtualization {
|
||||||
private DependencyInfo dependency;
|
private DependencyInfo dependency;
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
private Set<MethodReference> virtualMethods = new HashSet<>();
|
private Set<MethodReference> virtualMethods = new HashSet<>();
|
||||||
private Set<? extends MethodReference> readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods);
|
private Set<? extends MethodReference> readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods);
|
||||||
|
|
||||||
public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) {
|
public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) {
|
||||||
this.dependency = dependency;
|
this.dependency = dependency;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
hierarchy = new ClassHierarchy(classSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(MethodHolder method) {
|
public void apply(MethodHolder method) {
|
||||||
|
@ -72,13 +76,14 @@ public class Devirtualization {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> getImplementations(String[] classNames, MethodReference ref) {
|
private Set<MethodReference> getImplementations(String[] classNames, MethodReference ref) {
|
||||||
|
OptionalPredicate<String> isSuperclass = hierarchy.getSuperclassPredicate(ref.getClassName());
|
||||||
Set<MethodReference> methods = new HashSet<>();
|
Set<MethodReference> methods = new HashSet<>();
|
||||||
for (String className : classNames) {
|
for (String className : classNames) {
|
||||||
if (className.startsWith("[")) {
|
if (className.startsWith("[")) {
|
||||||
className = "java.lang.Object";
|
className = "java.lang.Object";
|
||||||
}
|
}
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
if (cls == null || !classSource.isSuperType(ref.getClassName(), cls.getName()).orElse(false)) {
|
if (cls == null || !isSuperclass.test(cls.getName(), false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MethodDependencyInfo methodDep = dependency.getMethodImplementation(new MethodReference(
|
MethodDependencyInfo methodDep = dependency.getMethodImplementation(new MethodReference(
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.transformation;
|
package org.teavm.model.transformation;
|
||||||
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -33,7 +32,7 @@ public class ClassPatch implements ClassHolderTransformer {
|
||||||
private FieldReference platformClassField = new FieldReference("java.lang.Class", "platformClass");
|
private FieldReference platformClassField = new FieldReference("java.lang.Class", "platformClass");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||||
if (!cls.getName().equals("java.lang.Class")) {
|
if (!cls.getName().equals("java.lang.Class")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user