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>
|
||||
<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>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
|
|
|
@ -17,11 +17,11 @@ package org.teavm.classlib.impl;
|
|||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.common.DisjointSet;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
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);
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
Program program = method.getProgram();
|
||||
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();
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
varSet.create();
|
||||
|
@ -116,7 +120,7 @@ public class ClassForNameTransformer implements ClassHolderTransformer {
|
|||
if (nameIndex >= 0) {
|
||||
representative = program.variableAt(nameRepresentatives[nameIndex]);
|
||||
} else if (constant != null) {
|
||||
if (classSource.get(constant) == null || !filterClassName(constant)) {
|
||||
if (hierarchy.getClassSource().get(constant) == null || !filterClassName(constant)) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
switch (className) {
|
||||
// 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.addVirtualMethods(new AnnotationVirtualMethods());
|
||||
}
|
||||
|
||||
JavacSupport javacSupport = new JavacSupport();
|
||||
host.add(javacSupport);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -33,7 +32,7 @@ import org.teavm.model.instructions.NumericOperandType;
|
|||
|
||||
public class NumericClassTransformer implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
switch (cls.getName()) {
|
||||
case "java.lang.Integer":
|
||||
transformInteger(cls);
|
||||
|
|
|
@ -21,9 +21,10 @@ import org.teavm.model.AnnotationReader;
|
|||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
|
@ -50,16 +51,17 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
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,
|
||||
ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
ClassHierarchy hierarchy, Diagnostics diagnostics) {
|
||||
boolean hasChanges = false;
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
|
@ -68,7 +70,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
|||
MarkerKind kind;
|
||||
if (instruction instanceof InvokeInstruction) {
|
||||
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
|
||||
MethodReader method = innerSource.resolve(methodRef);
|
||||
MethodReader method = hierarchy.getClassSource().resolveImplementation(methodRef);
|
||||
if (method == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -92,7 +94,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
|||
receiver = ((InvokeInstruction) instruction).getReceiver();
|
||||
} else if (instruction instanceof GetFieldInstruction) {
|
||||
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
|
||||
FieldReader field = innerSource.resolve(fieldRef);
|
||||
FieldReader field = hierarchy.getClassSource().resolve(fieldRef);
|
||||
if (field == null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -63,10 +63,6 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
Class[].class);
|
||||
private MethodReference constructorGetParameterTypes = new MethodReference(Constructor.class, "getParameterTypes",
|
||||
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<MethodDescriptor>> accessibleMethodCache = new LinkedHashMap<>();
|
||||
private Set<String> classesWithReflectableFields = new LinkedHashSet<>();
|
||||
|
@ -101,20 +97,20 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
||||
public void classReached(DependencyAgent agent, String className) {
|
||||
allClasses.propagate(agent.getType(className));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getReference().equals(fieldGet)) {
|
||||
handleFieldGet(agent, method, location);
|
||||
handleFieldGet(agent, method);
|
||||
} else if (method.getReference().equals(fieldSet)) {
|
||||
handleFieldSet(agent, method, location);
|
||||
handleFieldSet(agent, method);
|
||||
} else if (method.getReference().equals(newInstance)) {
|
||||
handleNewInstance(agent, method, location);
|
||||
handleNewInstance(agent, method);
|
||||
} else if (method.getReference().equals(invokeMethod)) {
|
||||
handleInvoke(agent, method, location);
|
||||
handleInvoke(agent, method);
|
||||
} else if (method.getReference().equals(getFields)) {
|
||||
method.getVariable(0).getClassValueNode().addConsumer(type -> {
|
||||
if (!type.getName().startsWith("[")) {
|
||||
|
@ -156,13 +152,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleFieldGet(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (fieldGetHandled) {
|
||||
return;
|
||||
}
|
||||
fieldGetHandled = true;
|
||||
|
||||
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
|
||||
private void handleFieldGet(DependencyAgent agent, MethodDependency method) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
DependencyNode classValueNode = agent.linkMethod(getFields)
|
||||
.addLocation(location)
|
||||
.getVariable(0).getClassValueNode();
|
||||
classValueNode.addConsumer(reflectedType -> {
|
||||
if (reflectedType.getName().startsWith("[")) {
|
||||
return;
|
||||
|
@ -171,20 +165,19 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||
for (String fieldName : accessibleFields) {
|
||||
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);
|
||||
linkClassIfNecessary(agent, field, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleFieldSet(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (fieldSetHandled) {
|
||||
return;
|
||||
}
|
||||
fieldSetHandled = true;
|
||||
|
||||
DependencyNode classValueNode = agent.linkMethod(getFields, location).getVariable(0).getClassValueNode();
|
||||
private void handleFieldSet(DependencyAgent agent, MethodDependency method) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
DependencyNode classValueNode = agent.linkMethod(getFields)
|
||||
.addLocation(location)
|
||||
.getVariable(0).getClassValueNode();
|
||||
classValueNode.addConsumer(reflectedType -> {
|
||||
if (reflectedType.getName().startsWith("[")) {
|
||||
return;
|
||||
|
@ -194,20 +187,19 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||
for (String fieldName : accessibleFields) {
|
||||
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);
|
||||
linkClassIfNecessary(agent, field, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleNewInstance(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (newInstanceHandled) {
|
||||
return;
|
||||
}
|
||||
newInstanceHandled = true;
|
||||
private void handleNewInstance(DependencyAgent agent, MethodDependency method) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
|
||||
DependencyNode classValueNode = agent.linkMethod(getConstructors, location).getVariable(0).getClassValueNode();
|
||||
DependencyNode classValueNode = agent.linkMethod(getConstructors)
|
||||
.addLocation(location)
|
||||
.getVariable(0).getClassValueNode();
|
||||
classValueNode.addConsumer(reflectedType -> {
|
||||
if (reflectedType.getName().startsWith("[")) {
|
||||
return;
|
||||
|
@ -217,7 +209,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
||||
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference(), location);
|
||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
|
||||
calledMethodDep.use();
|
||||
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
||||
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(1).getArrayItem(),
|
||||
|
@ -230,13 +222,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
classValueNode.connect(method.getResult());
|
||||
}
|
||||
|
||||
private void handleInvoke(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (invokeHandled) {
|
||||
return;
|
||||
}
|
||||
invokeHandled = true;
|
||||
|
||||
DependencyNode classValueNode = agent.linkMethod(getMethods, location).getVariable(0).getClassValueNode();
|
||||
private void handleInvoke(DependencyAgent agent, MethodDependency method) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
DependencyNode classValueNode = agent.linkMethod(getMethods)
|
||||
.addLocation(location)
|
||||
.getVariable(0).getClassValueNode();
|
||||
classValueNode.addConsumer(reflectedType -> {
|
||||
if (reflectedType.getName().startsWith("[")) {
|
||||
return;
|
||||
|
@ -246,7 +236,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
|
||||
for (MethodDescriptor methodDescriptor : accessibleMethods) {
|
||||
MethodReader calledMethod = cls.getMethod(methodDescriptor);
|
||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference(), location);
|
||||
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
|
||||
calledMethodDep.use();
|
||||
for (int i = 0; i < calledMethod.parameterCount(); ++i) {
|
||||
propagateSet(agent, methodDescriptor.parameterType(i), method.getVariable(2).getArrayItem(),
|
||||
|
@ -267,14 +257,14 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
agent.linkClass(className, null);
|
||||
agent.linkClass(className);
|
||||
typesInReflectableSignaturesNode.propagate(agent.getType(className));
|
||||
}
|
||||
}
|
||||
|
||||
private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) {
|
||||
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:
|
||||
throw new AssertionError(type.toString());
|
||||
}
|
||||
MethodDependency boxMethodDep = agent.linkMethod(boxMethod, location);
|
||||
MethodDependency boxMethodDep = agent.linkMethod(boxMethod).addLocation(location);
|
||||
boxMethodDep.use();
|
||||
boxMethodDep.getResult().connect(targetNode);
|
||||
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
||||
|
@ -370,7 +360,7 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
|
|||
default:
|
||||
throw new AssertionError(type.toString());
|
||||
}
|
||||
MethodDependency unboxMethodDep = agent.linkMethod(unboxMethod, location);
|
||||
MethodDependency unboxMethodDep = agent.linkMethod(unboxMethod).addLocation(location);
|
||||
unboxMethodDep.use();
|
||||
sourceNode.connect(unboxMethodDep.getResult());
|
||||
} else if (type instanceof ValueType.Array || type instanceof ValueType.Object) {
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -33,10 +33,10 @@ import org.teavm.model.instructions.PutFieldInstruction;
|
|||
public class ScalaHacks implements ClassHolderTransformer {
|
||||
private static final String ATTR_NAME_CLASS = "java.util.jar.Attributes$Name";
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
switch (cls.getName()) {
|
||||
case "scala.util.PropertiesTrait$class":
|
||||
transformPropertiesTrait(cls, innerSource);
|
||||
transformPropertiesTrait(cls, context.getHierarchy());
|
||||
break;
|
||||
case "scala.util.Properties$":
|
||||
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()) {
|
||||
if (method.getName().equals("scalaProps")) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
pe.construct(Properties.class).returnValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,9 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
|
@ -42,9 +40,9 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.ValueType;
|
||||
|
||||
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 DependencyNode allClassesNode;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public ServiceLoaderSupport(ClassLoader classLoader) {
|
||||
|
@ -87,30 +85,8 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
|||
writer.append("return result;").softNewLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
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 {
|
||||
private void parseServiceFile(DependencyAgent agent, DependencyNode targetNode, String service,
|
||||
InputStream input, CallLocation location) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
|
@ -122,24 +98,37 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
|||
continue;
|
||||
}
|
||||
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
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
DependencyNode sourceNode = agent.linkMethod(new MethodReference(ServiceLoader.class, "load", Class.class,
|
||||
ServiceLoader.class), null).getVariable(1).getClassValueNode();
|
||||
DependencyNode sourceNode = agent.linkMethod(LOAD_METHOD).getVariable(1).getClassValueNode();
|
||||
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) {
|
||||
MethodReference ctor = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
|
||||
agent.linkMethod(ctor, location).use();
|
||||
private void initConstructor(DependencyAgent agent, DependencyNode targetNode, String type,
|
||||
CallLocation location) {
|
||||
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;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -31,7 +30,7 @@ import org.teavm.model.instructions.InvokeInstruction;
|
|||
|
||||
public class SystemClassTransformer implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
transformProgram(method.getProgram());
|
||||
|
|
|
@ -15,14 +15,15 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl.lambda;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.cache.NoCache;
|
||||
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||
import org.teavm.dependency.DynamicCallSite;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
@ -31,6 +32,8 @@ import org.teavm.model.FieldHolder;
|
|||
import org.teavm.model.MethodHandle;
|
||||
import org.teavm.model.MethodHandleType;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.PrimitiveType;
|
||||
import org.teavm.model.TextLocation;
|
||||
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_MARKERS = 2;
|
||||
private static final int FLAG_BRIDGES = 4;
|
||||
private Map<String, Integer> lambdaIdsByMethod = new HashMap<>();
|
||||
private Map<MethodReference, Integer> lambdaIdsByMethod = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter callerPe) {
|
||||
|
@ -52,14 +55,24 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
||||
|
||||
String samName = ((ValueType.Object) callSite.getCalledMethod().getResultType()).getClassName();
|
||||
ClassReaderSource classSource = callSite.getAgent().getClassSource();
|
||||
ClassReader samClass = classSource.get(samName);
|
||||
ClassHierarchy hierarchy = callSite.getAgent().getClassHierarchy();
|
||||
ClassReader samClass = hierarchy.getClassSource().get(samName);
|
||||
|
||||
String key = callSite.getCaller().getClassName() + "$" + callSite.getCaller().getName();
|
||||
int id = lambdaIdsByMethod.getOrDefault(key, 0);
|
||||
lambdaIdsByMethod.put(key, id + 1);
|
||||
ClassReaderSource classSource = callSite.getAgent().getClassSource();
|
||||
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);
|
||||
if (samClass != null && samClass.hasModifier(ElementModifier.INTERFACE)) {
|
||||
implementor.setParent("java.lang.Object");
|
||||
|
@ -69,16 +82,14 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
}
|
||||
|
||||
int capturedVarCount = callSite.getCalledMethod().parameterCount();
|
||||
MethodHolder ctor = createConstructor(classSource, implementor,
|
||||
MethodHolder ctor = createConstructor(hierarchy, implementor,
|
||||
Arrays.copyOfRange(invokedType, 0, capturedVarCount), callerPe.getCurrentLocation());
|
||||
ctor.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
||||
createBridge(classSource, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||
createBridge(hierarchy, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||
samMethodType, callerPe.getCurrentLocation());
|
||||
|
||||
MethodHolder worker = new MethodHolder(callSite.getCalledMethod().getName(), instantiatedMethodType);
|
||||
worker.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
||||
worker.setLevel(AccessLevel.PUBLIC);
|
||||
ProgramEmitter pe = ProgramEmitter.create(worker, callSite.getAgent().getClassSource());
|
||||
ProgramEmitter pe = ProgramEmitter.create(worker, callSite.getAgent().getClassHierarchy());
|
||||
pe.setCurrentLocation(callerPe.getCurrentLocation());
|
||||
ValueEmitter thisVar = pe.var(0, implementor);
|
||||
ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1];
|
||||
|
@ -128,13 +139,23 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
int bridgeCount = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getInt();
|
||||
for (int i = 0; i < bridgeCount; ++i) {
|
||||
ValueType[] bridgeType = callSite.getBootstrapArguments().get(bootstrapArgIndex++).getMethodType();
|
||||
createBridge(classSource, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||
createBridge(hierarchy, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType,
|
||||
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().getIncrementalCache().addDependencies(implementor.getName(),
|
||||
dependencies.toArray(new String[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) {
|
||||
ValueType[] signature = Arrays.copyOf(types, types.length + 1);
|
||||
signature[types.length] = ValueType.VOID;
|
||||
MethodHolder ctor = new MethodHolder("<init>", signature);
|
||||
ctor.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, classSource);
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
|
||||
pe.setCurrentLocation(location);
|
||||
ValueEmitter thisVar = pe.var(0, implementor);
|
||||
thisVar.invokeSpecial(implementor.getParent(), "<init>");
|
||||
|
@ -298,17 +319,16 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
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) {
|
||||
if (Arrays.equals(types, bridgeTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MethodHolder bridge = new MethodHolder(name, bridgeTypes);
|
||||
bridge.getAnnotations().add(new AnnotationHolder(NoCache.class.getName()));
|
||||
bridge.setLevel(AccessLevel.PUBLIC);
|
||||
bridge.getModifiers().add(ElementModifier.BRIDGE);
|
||||
ProgramEmitter pe = ProgramEmitter.create(bridge, classSource);
|
||||
ProgramEmitter pe = ProgramEmitter.create(bridge, hierarchy);
|
||||
pe.setCurrentLocation(location);
|
||||
ValueEmitter thisVar = pe.var(0, implementor);
|
||||
ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1];
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
package org.teavm.classlib.impl.tz;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import org.teavm.classlib.impl.Base46;
|
||||
import org.teavm.classlib.impl.CharFlow;
|
||||
|
||||
public class TimeZoneCache {
|
||||
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();
|
||||
for (StorableDateTimeZone timeZone : timeZones) {
|
||||
writer.append(timeZone.getID()).append(' ');
|
||||
|
@ -36,7 +37,7 @@ public class TimeZoneCache {
|
|||
|
||||
public Map<String, StorableDateTimeZone> read(InputStream input) throws IOException {
|
||||
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<>();
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
@ -50,11 +51,13 @@ public class TimeZoneGenerator implements MetadataGenerator {
|
|||
case "northamerica":
|
||||
case "pacificnew":
|
||||
case "southamerica":
|
||||
compiler.parseDataFile(new BufferedReader(new InputStreamReader(zip, "UTF-8")), false);
|
||||
compiler.parseDataFile(new BufferedReader(
|
||||
new InputStreamReader(zip, StandardCharsets.UTF_8)), false);
|
||||
break;
|
||||
case "backward":
|
||||
case "backzone":
|
||||
compiler.parseDataFile(new BufferedReader(new InputStreamReader(zip, "UTF-8")), true);
|
||||
compiler.parseDataFile(new BufferedReader(
|
||||
new InputStreamReader(zip, StandardCharsets.UTF_8)), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,13 @@ package org.teavm.classlib.java.lang;
|
|||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
|
||||
public class ClassDependencyListener implements DependencyPlugin {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "initialize":
|
||||
method.getVariable(0).getClassValueNode().addConsumer(type -> agent
|
||||
.linkClass(type.getName(), location)
|
||||
.initClass(location));
|
||||
method.getVariable(0).getClassValueNode().addConsumer(type -> agent.linkClass(type.getName()));
|
||||
break;
|
||||
case "getSimpleNameCacheLowLevel":
|
||||
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.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReader;
|
||||
|
@ -65,7 +64,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "newEmptyInstance":
|
||||
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.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
|
||||
public class ObjectDependencyPlugin implements DependencyPlugin {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "clone":
|
||||
method.getVariable(0).connect(method.getResult());
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.teavm.dependency.DependencyAgent;
|
|||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
||||
|
@ -40,7 +39,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "doArrayCopy":
|
||||
reachArrayCopy(method);
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.teavm.classlib.java.lang.reflect;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
|
@ -26,10 +28,9 @@ import org.teavm.dependency.MethodDependency;
|
|||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -41,16 +42,18 @@ import org.teavm.model.emit.ValueEmitter;
|
|||
import org.teavm.platform.PlatformAnnotationProvider;
|
||||
|
||||
public class AnnotationDependencyListener extends AbstractDependencyListener {
|
||||
private Set<MethodReference> reachedMethods = new HashSet<>();
|
||||
|
||||
private String getAnnotationImplementor(DependencyAgent agent, String annotationType) {
|
||||
String implementorName = annotationType + "$$_impl";
|
||||
if (agent.getClassSource().get(implementorName) == null) {
|
||||
ClassHolder implementor = createImplementor(agent.getClassSource(), annotationType, implementorName);
|
||||
ClassHolder implementor = createImplementor(agent.getClassHierarchy(), annotationType, implementorName);
|
||||
agent.submitClass(implementor);
|
||||
}
|
||||
return implementorName;
|
||||
}
|
||||
|
||||
private ClassHolder createImplementor(ClassReaderSource classSource, String annotationType,
|
||||
private ClassHolder createImplementor(ClassHierarchy hierarchy, String annotationType,
|
||||
String implementorName) {
|
||||
ClassHolder implementor = new ClassHolder(implementorName);
|
||||
implementor.setParent("java.lang.Object");
|
||||
|
@ -58,7 +61,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
implementor.getModifiers().add(ElementModifier.FINAL);
|
||||
implementor.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
ClassReader annotation = classSource.get(annotationType);
|
||||
ClassReader annotation = hierarchy.getClassSource().get(annotationType);
|
||||
if (annotation == null) {
|
||||
return implementor;
|
||||
}
|
||||
|
@ -74,7 +77,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
implementor.addField(field);
|
||||
|
||||
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 result = thisVal.getField(field.getName(), field.getType());
|
||||
if (field.getType() instanceof ValueType.Array) {
|
||||
|
@ -87,8 +90,8 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
ctorSignature.add(ValueType.VOID);
|
||||
|
||||
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[ctorSignature.size()]));
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, classSource);
|
||||
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[0]));
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, hierarchy);
|
||||
ValueEmitter thisVar = pe.var(0, implementor);
|
||||
thisVar.invokeSpecial(Object.class, "<init>");
|
||||
int index = 1;
|
||||
|
@ -103,7 +106,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
implementor.addMethod(ctor);
|
||||
|
||||
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();
|
||||
implementor.addMethod(annotTypeMethod);
|
||||
|
||||
|
@ -111,7 +114,11 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
|
||||
@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();
|
||||
while (type instanceof ValueType.Array) {
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
|
@ -120,7 +127,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
String className = ((ValueType.Object) type).getClassName();
|
||||
ClassReader cls = agent.getClassSource().get(className);
|
||||
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());
|
||||
if (cls != null) {
|
||||
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
||||
agent.linkClass(annotation.getType(), location);
|
||||
agent.linkClass(annotation.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MethodReference methodRef = method.getMethod().getReference();
|
||||
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 -> {
|
||||
String className = type.getName();
|
||||
|
||||
|
@ -150,7 +157,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
|
||||
for (AnnotationReader annotation : cls.getAnnotations().all()) {
|
||||
agent.linkClass(annotation.getType(), location);
|
||||
agent.linkClass(annotation.getType());
|
||||
}
|
||||
|
||||
createAnnotationClass(agent, className);
|
||||
|
@ -170,7 +177,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
|
||||
MethodHolder ctor = new MethodHolder("<init>", ValueType.VOID);
|
||||
ctor.setLevel(AccessLevel.PUBLIC);
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, agent.getClassSource());
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor, agent.getClassHierarchy());
|
||||
ValueEmitter thisVar = pe.var(0, cls);
|
||||
thisVar.invokeSpecial(Object.class, "<init>").exit();
|
||||
|
||||
|
@ -184,7 +191,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
private MethodHolder addReader(DependencyAgent agent, ClassReader cls) {
|
||||
MethodHolder readerMethod = new MethodHolder("getAnnotations", ValueType.parse(Annotation[].class));
|
||||
readerMethod.setLevel(AccessLevel.PUBLIC);
|
||||
ProgramEmitter pe = ProgramEmitter.create(readerMethod, agent.getClassSource());
|
||||
ProgramEmitter pe = ProgramEmitter.create(readerMethod, agent.getClassHierarchy());
|
||||
|
||||
List<AnnotationReader> annotations = new ArrayList<>();
|
||||
for (AnnotationReader annot : cls.getAnnotations().all()) {
|
||||
|
@ -230,7 +237,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
.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,
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
package org.teavm.classlib.java.lang.reflect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
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,
|
||||
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
|
||||
|
||||
private Set<MethodReference> reachedMethods = new HashSet<>();
|
||||
|
||||
@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()) {
|
||||
case "getLength":
|
||||
reachGetLength(agent, method);
|
||||
|
@ -51,7 +57,9 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
|||
} else {
|
||||
arrayTypeName = ValueType.object(t.getName()).toString();
|
||||
}
|
||||
if (!arrayTypeName.startsWith("[[[")) {
|
||||
method.getResult().propagate(agent.getType("[" + arrayTypeName));
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "getImpl":
|
||||
|
@ -91,11 +99,11 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
|||
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 -> {
|
||||
if (!type.getName().startsWith("[")) {
|
||||
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];
|
||||
MethodReference methodRef = new MethodReference(wrapper, "valueOf",
|
||||
primitiveTypes[i], ValueType.object(wrapper));
|
||||
agent.linkMethod(methodRef, null).use();
|
||||
agent.linkMethod(methodRef).use();
|
||||
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];
|
||||
MethodReference methodRef = new MethodReference(wrapper,
|
||||
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.VariableNode;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.ast.cache.MethodNodeCache;
|
||||
import org.teavm.ast.optimization.Optimizer;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
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.GraphIndexer;
|
||||
import org.teavm.common.Loop;
|
||||
|
@ -74,6 +75,7 @@ import org.teavm.model.util.TypeInferer;
|
|||
public class Decompiler {
|
||||
private ClassHolderSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private CacheStatus cacheStatus;
|
||||
private Graph graph;
|
||||
private LoopGraph loopGraph;
|
||||
private GraphIndexer indexer;
|
||||
|
@ -90,15 +92,18 @@ public class Decompiler {
|
|||
private Set<MethodReference> asyncMethods;
|
||||
private Set<MethodReference> splitMethods = new HashSet<>();
|
||||
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
|
||||
private final AstDependencyExtractor astDependencyExtractor = new AstDependencyExtractor();
|
||||
private Deque<Block> stack;
|
||||
private Program program;
|
||||
private boolean friendlyToDebugger;
|
||||
private boolean moveConstants;
|
||||
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods,
|
||||
Set<MethodReference> asyncFamilyMethods, boolean friendlyToDebugger, boolean moveConstants) {
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader,
|
||||
CacheStatus cacheStatus, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
|
||||
boolean friendlyToDebugger, boolean moveConstants) {
|
||||
this.classSource = classSource;
|
||||
this.classLoader = classLoader;
|
||||
this.cacheStatus = cacheStatus;
|
||||
this.asyncMethods = asyncMethods;
|
||||
splitMethods.addAll(asyncMethods);
|
||||
splitMethods.addAll(asyncFamilyMethods);
|
||||
|
@ -239,8 +244,7 @@ public class Decompiler {
|
|||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||
}
|
||||
}
|
||||
NativeMethodNode methodNode = new NativeMethodNode(new MethodReference(method.getOwnerName(),
|
||||
method.getDescriptor()));
|
||||
NativeMethodNode methodNode = new NativeMethodNode(method.getReference());
|
||||
methodNode.getModifiers().addAll(method.getModifiers());
|
||||
methodNode.setGenerator(generator);
|
||||
methodNode.setAsync(asyncMethods.contains(method.getReference()));
|
||||
|
@ -253,13 +257,16 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
public RegularMethodNode decompileRegular(MethodHolder method) {
|
||||
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
||||
if (regularMethodCache == null) {
|
||||
return decompileRegularCacheMiss(method);
|
||||
}
|
||||
RegularMethodNode node = regularMethodCache.get(method.getReference());
|
||||
RegularMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
||||
? regularMethodCache.get(method.getReference(), cacheStatus)
|
||||
: null;
|
||||
if (node == null) {
|
||||
node = decompileRegularCacheMiss(method);
|
||||
regularMethodCache.store(method.getReference(), node);
|
||||
RegularMethodNode finalNode = node;
|
||||
regularMethodCache.store(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
@ -293,36 +300,20 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
public AsyncMethodNode decompileAsync(MethodHolder method) {
|
||||
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
||||
if (regularMethodCache == null) {
|
||||
return decompileAsyncCacheMiss(method);
|
||||
}
|
||||
AsyncMethodNode node = regularMethodCache.getAsync(method.getReference());
|
||||
if (node == null || !checkAsyncRelevant(node)) {
|
||||
AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference())
|
||||
? regularMethodCache.getAsync(method.getReference(), cacheStatus)
|
||||
: null;
|
||||
if (node == null) {
|
||||
node = decompileAsyncCacheMiss(method);
|
||||
regularMethodCache.storeAsync(method.getReference(), node);
|
||||
AsyncMethodNode finalNode = node;
|
||||
regularMethodCache.storeAsync(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode));
|
||||
}
|
||||
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) {
|
||||
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
|
||||
AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods);
|
||||
|
|
|
@ -44,11 +44,9 @@ import org.teavm.common.GraphIndexer;
|
|||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||
|
@ -93,6 +91,7 @@ import org.teavm.model.instructions.SwitchTableEntry;
|
|||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||
|
||||
class StatementGenerator implements InstructionVisitor {
|
||||
private static final MethodReference CLONE_METHOD = new MethodReference(Object.class, "clone", Object.class);
|
||||
private int lastSwitchId;
|
||||
final List<Statement> statements = new ArrayList<>();
|
||||
GraphIndexer indexer;
|
||||
|
@ -432,9 +431,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(CloneArrayInstruction insn) {
|
||||
MethodDescriptor cloneMethodDesc = new MethodDescriptor("clone", ValueType.object("java.lang.Object"));
|
||||
MethodReference cloneMethod = new MethodReference("java.lang.Object", cloneMethodDesc);
|
||||
assign(Expr.invoke(cloneMethod, Expr.var(insn.getArray().getIndex()), new Expr[0]), insn.getReceiver());
|
||||
assign(Expr.invoke(CLONE_METHOD, Expr.var(insn.getArray().getIndex()), new Expr[0]), insn.getReceiver());
|
||||
}
|
||||
|
||||
@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.ShadowStackIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
||||
import org.teavm.cache.AlwaysStaleCacheStatus;
|
||||
import org.teavm.dependency.ClassDependency;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
|
@ -164,34 +165,34 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
@Override
|
||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
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",
|
||||
RuntimeClass.class, int.class, Address.class), null).use();
|
||||
RuntimeClass.class, int.class, Address.class)).use();
|
||||
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",
|
||||
Throwable.class, void.class), null).use();
|
||||
Throwable.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
||||
void.class), null).use();
|
||||
void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||
void.class), null).use();
|
||||
void.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||
Throwable.class), null).use();
|
||||
Throwable.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkClass("java.lang.String", null);
|
||||
dependencyAnalyzer.linkClass("java.lang.Class", null);
|
||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"), null);
|
||||
dependencyAnalyzer.linkClass("java.lang.String");
|
||||
dependencyAnalyzer.linkClass("java.lang.Class");
|
||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"));
|
||||
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName());
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName());
|
||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||
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);
|
||||
StringPool stringPool = new StringPool();
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false, true);
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
|
||||
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
|
||||
Characteristics characteristics = new Characteristics(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.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
||||
public class CDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
||||
@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());
|
||||
if (delegateAnnot != null) {
|
||||
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
||||
|
@ -40,7 +38,9 @@ public class CDependencyListener extends AbstractDependencyListener implements C
|
|||
for (MethodReader delegate : cls.getMethods()) {
|
||||
if (delegate.getName().equals(delegateMethodName)) {
|
||||
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
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateAnnot != null) {
|
||||
|
|
|
@ -35,8 +35,6 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
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.backend.javascript.codegen.AliasProvider;
|
||||
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.VirtualMethodContributor;
|
||||
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.DummyDebugInformationEmitter;
|
||||
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<RendererListener> rendererListeners = new ArrayList<>();
|
||||
private DebugInformationEmitter debugEmitter;
|
||||
private MethodNodeCache astCache = new EmptyRegularMethodNodeCache();
|
||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
||||
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
|
||||
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
|
||||
|
@ -211,42 +211,41 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
DependencyType stringType = dependencyAnalyzer.getType("java.lang.String");
|
||||
|
||||
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.getResult().propagate(dependencyAnalyzer.getType("java.lang.Class"));
|
||||
dep.use();
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
|
||||
null);
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class));
|
||||
dep.getVariable(0).propagate(stringType);
|
||||
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C"));
|
||||
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(
|
||||
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.use();
|
||||
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchFieldError.class, "<init>",
|
||||
String.class, void.class), null);
|
||||
String.class, void.class));
|
||||
exceptionCons.use();
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchFieldError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchMethodError.class, "<init>",
|
||||
String.class, void.class), null);
|
||||
String.class, void.class));
|
||||
exceptionCons.use();
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
RuntimeException.class, "<init>", String.class, void.class), null);
|
||||
RuntimeException.class, "<init>", String.class, void.class));
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons.use();
|
||||
|
@ -263,7 +262,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
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(1).propagate(stringType);
|
||||
dep.getVariable(2).propagate(stringType);
|
||||
|
@ -271,7 +270,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
dep.use();
|
||||
|
||||
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(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/StackTraceElement;"));
|
||||
dep.getVariable(1).getArrayItem().propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
||||
|
@ -440,9 +439,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
asyncMethods.addAll(asyncFinder.getAsyncMethods());
|
||||
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), asyncMethods, asyncFamilyMethods,
|
||||
controller.isFriendlyToDebugger(), false);
|
||||
decompiler.setRegularMethodCache(controller.isIncremental() ? astCache : null);
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), controller.getCacheStatus(),
|
||||
asyncMethods, asyncFamilyMethods, controller.isFriendlyToDebugger(), false);
|
||||
decompiler.setRegularMethodCache(astCache);
|
||||
|
||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||
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;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -25,12 +27,15 @@ import org.teavm.model.MethodReference;
|
|||
|
||||
public class DefaultAliasProvider implements AliasProvider {
|
||||
private final Map<String, String> classAliases = new HashMap<>();
|
||||
private final Set<String> knownAliases = new HashSet<>();
|
||||
private final Set<String> knownVirtualAliases = new HashSet<>();
|
||||
private final Set<String> knownAliases = new HashSet<>(200, 0.5f);
|
||||
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
|
||||
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) {
|
||||
|
@ -80,7 +85,7 @@ public class DefaultAliasProvider implements AliasProvider {
|
|||
alias = "$" + alias;
|
||||
break;
|
||||
}
|
||||
return makeUnique(knownVirtualAliases, alias);
|
||||
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,17 +100,18 @@ public class DefaultAliasProvider implements AliasProvider {
|
|||
break;
|
||||
}
|
||||
|
||||
return makeUnique(knownAliases, getClassAlias(method.getClassName()) + "_" + alias);
|
||||
return makeUnique(knownAliases, knowAliasesCounter, getClassAlias(method.getClassName()) + "_" + alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldAlias(FieldReference field) {
|
||||
return makeUnique(knownVirtualAliases, "$" + field.getFieldName());
|
||||
return makeUnique(knownVirtualAliases, knowVirtualAliasesCounter, "$" + field.getFieldName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStaticFieldAlias(FieldReference field) {
|
||||
return makeUnique(knownAliases, getClassAlias(field.getClassName()) + "_" + field.getFieldName());
|
||||
return makeUnique(knownAliases, knowAliasesCounter,
|
||||
getClassAlias(field.getClassName()) + "_" + field.getFieldName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,15 +121,19 @@ public class DefaultAliasProvider implements AliasProvider {
|
|||
|
||||
@Override
|
||||
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;
|
||||
int index = 1;
|
||||
int index = indexMap.get(alias);
|
||||
if (index > 0) {
|
||||
uniqueAlias = alias + index++;
|
||||
}
|
||||
while (!knowAliases.add(uniqueAlias)) {
|
||||
uniqueAlias = alias + index++;
|
||||
}
|
||||
indexMap.put(alias, index);
|
||||
return uniqueAlias;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
|||
if (method.getLevel() == AccessLevel.PRIVATE && !className.equals(methodRef.getClassName())) {
|
||||
return null;
|
||||
}
|
||||
return new MethodReference(className, method.getDescriptor());
|
||||
return method.getReference();
|
||||
}
|
||||
className = cls.getParent();
|
||||
}
|
||||
|
|
|
@ -51,6 +51,15 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.ValueType;
|
||||
|
||||
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 ClassReaderSource classSource;
|
||||
private boolean async;
|
||||
|
@ -167,14 +176,10 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
|||
public void visit(MonitorEnterStatement statement) {
|
||||
super.visit(statement);
|
||||
if (async) {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorEnter", Object.class, void.class);
|
||||
consumer.consume(monitorEnterRef);
|
||||
consumer.consume(MONITOR_ENTER_METHOD);
|
||||
consumer.consumeFunction("$rt_suspending");
|
||||
} else {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorEnterSync", Object.class, void.class);
|
||||
consumer.consume(monitorEnterRef);
|
||||
consumer.consume(MONITOR_ENTER_SYNC_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,13 +187,9 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit
|
|||
public void visit(MonitorExitStatement statement) {
|
||||
super.visit(statement);
|
||||
if (async) {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorExit", Object.class, void.class);
|
||||
consumer.consume(monitorEnterRef);
|
||||
consumer.consume(MONITOR_EXIT_METHOD);
|
||||
} else {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorExitSync", Object.class, void.class);
|
||||
consumer.consume(monitorEnterRef);
|
||||
consumer.consume(MONITOR_EXIT_SYNC_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -836,7 +836,29 @@ public class Renderer implements RenderingManager {
|
|||
|
||||
statementRenderer.setEnd(true);
|
||||
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);
|
||||
|
||||
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) {
|
||||
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) {
|
||||
writer.append("case ").append(i).append(":").indent().softNewLine();
|
||||
if (i == 0 && methodNode.getModifiers().contains(ElementModifier.SYNCHRONIZED)) {
|
||||
writer.appendMethodBody(new MethodReference(Object.class, "monitorEnter",
|
||||
Object.class, void.class));
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD);
|
||||
writer.append("(");
|
||||
appendMonitor(statementRenderer, methodNode);
|
||||
writer.append(");").softNewLine();
|
||||
|
@ -932,8 +953,7 @@ public class Renderer implements RenderingManager {
|
|||
writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();
|
||||
writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())")
|
||||
.ws().append("{").indent().softNewLine();
|
||||
writer.appendMethodBody(new MethodReference(Object.class, "monitorExit",
|
||||
Object.class, void.class));
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD);
|
||||
writer.append("(");
|
||||
appendMonitor(statementRenderer, methodNode);
|
||||
writer.append(");").softNewLine();
|
||||
|
|
|
@ -1459,6 +1459,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
|
||||
first = false;
|
||||
|
||||
if (defaultHandlerOccurred) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!defaultHandlerOccurred) {
|
||||
writer.ws().append("else").ws().append("{").indent().softNewLine();
|
||||
|
@ -1492,17 +1496,13 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
public void visit(MonitorEnterStatement statement) {
|
||||
try {
|
||||
if (async) {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorEnter", Object.class, void.class);
|
||||
writer.appendMethodBody(monitorEnterRef).append("(");
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_METHOD).append("(");
|
||||
precedence = Precedence.min();
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
writer.append(");").softNewLine();
|
||||
emitSuspendChecker();
|
||||
} else {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorEnterSync", Object.class, void.class);
|
||||
writer.appendMethodBody(monitorEnterRef).append('(');
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_ENTER_SYNC_METHOD).append('(');
|
||||
precedence = Precedence.min();
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
writer.append(");").softNewLine();
|
||||
|
@ -1523,16 +1523,12 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
|||
public void visit(MonitorExitStatement statement) {
|
||||
try {
|
||||
if (async) {
|
||||
MethodReference monitorExitRef = new MethodReference(
|
||||
Object.class, "monitorExit", Object.class, void.class);
|
||||
writer.appendMethodBody(monitorExitRef).append("(");
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_METHOD).append("(");
|
||||
precedence = Precedence.min();
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
writer.append(");").softNewLine();
|
||||
} else {
|
||||
MethodReference monitorEnterRef = new MethodReference(
|
||||
Object.class, "monitorExitSync", Object.class, void.class);
|
||||
writer.appendMethodBody(monitorEnterRef).append('(');
|
||||
writer.appendMethodBody(NameFrequencyEstimator.MONITOR_EXIT_SYNC_METHOD).append('(');
|
||||
precedence = Precedence.min();
|
||||
statement.getObjectRef().acceptVisitor(this);
|
||||
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.transformation.IndirectCallTraceTransformation;
|
||||
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
|
||||
import org.teavm.cache.AlwaysStaleCacheStatus;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.dependency.ClassDependency;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
|
@ -241,61 +242,60 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.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)) {
|
||||
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,
|
||||
Address.class), null).use();
|
||||
Address.class)).use();
|
||||
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,
|
||||
Address.class, int.class, void.class), null).use();
|
||||
Address.class, int.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "allocStack",
|
||||
int.class, Address.class), null).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackTop", Address.class),
|
||||
null) .use();
|
||||
int.class, Address.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getStackTop", Address.class)).use();
|
||||
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,
|
||||
int.class), null).use();
|
||||
int.class)).use();
|
||||
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,
|
||||
int.class, void.class), null).use();
|
||||
int.class, void.class)).use();
|
||||
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,
|
||||
String[].class), null).use();
|
||||
String[].class)).use();
|
||||
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",
|
||||
RuntimeClass.class, Address.class), null).use();
|
||||
RuntimeClass.class, Address.class)).use();
|
||||
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",
|
||||
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",
|
||||
Throwable.class, void.class), null).use();
|
||||
Throwable.class, void.class)).use();
|
||||
|
||||
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 runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName());
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName());
|
||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||
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(),
|
||||
vtableProvider, tagRegistry, binaryWriter, names);
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false, true);
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(),
|
||||
AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true);
|
||||
WasmStringPool stringPool = classGenerator.getStringPool();
|
||||
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
||||
vtableProvider, tagRegistry, stringPool, names);
|
||||
|
|
|
@ -18,32 +18,30 @@ package org.teavm.backend.wasm.generate;
|
|||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Export;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
||||
public class WasmDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
||||
@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()) {
|
||||
AnnotationReader annotation = reader.getAnnotations().get(Export.class.getName());
|
||||
if (annotation != null) {
|
||||
agent.linkMethod(reader.getReference(), null).use();
|
||||
agent.linkMethod(reader.getReference()).use();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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());
|
||||
if (delegateAnnot != null) {
|
||||
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
||||
|
@ -51,7 +49,9 @@ public class WasmDependencyListener extends AbstractDependencyListener implement
|
|||
for (MethodReader delegate : cls.getMethods()) {
|
||||
if (delegate.getName().equals(delegateMethodName)) {
|
||||
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
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
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");
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model;
|
||||
package org.teavm.cache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class InMemoryProgramCache implements ProgramCache {
|
||||
private Map<MethodReference, Program> cache = new HashMap<>();
|
||||
public class AlwaysFreshCacheStatus implements CacheStatus {
|
||||
public static final AlwaysFreshCacheStatus INSTANCE = new AlwaysFreshCacheStatus();
|
||||
|
||||
@Override
|
||||
public Program get(MethodReference method) {
|
||||
Program program = cache.get(method);
|
||||
return program != null ? ProgramUtils.copy(program) : null;
|
||||
private AlwaysFreshCacheStatus() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(MethodReference method, Program program) {
|
||||
cache.put(method, ProgramUtils.copy(program));
|
||||
public boolean isStaleClass(String className) {
|
||||
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 fileTable;
|
||||
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) {
|
||||
this.symbolTable = symbolTable;
|
||||
|
@ -949,8 +951,12 @@ public class AstIO {
|
|||
InvocationExpr expr = new InvocationExpr();
|
||||
expr.setType(invocationType);
|
||||
String className = symbolTable.at(input.readInt());
|
||||
MethodDescriptor method = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
||||
expr.setMethod(new MethodReference(className, method));
|
||||
MethodDescriptor method = methodDescriptorCache.computeIfAbsent(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor::parse);
|
||||
MethodReference methodRef = methodReferenceCache
|
||||
.computeIfAbsent(className, k -> new HashMap<>())
|
||||
.computeIfAbsent(method, k -> new MethodReference(className, k));
|
||||
expr.setMethod(methodRef);
|
||||
int argCount = input.readShort();
|
||||
for (int i = 0; i < argCount; ++i) {
|
||||
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;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import org.teavm.model.*;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
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;
|
||||
|
||||
public class DiskCachedClassHolderSource implements ClassHolderSource {
|
||||
public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStatus {
|
||||
private static AccessLevel[] accessLevels = AccessLevel.values();
|
||||
private static ElementModifier[] elementModifiers = ElementModifier.values();
|
||||
private File directory;
|
||||
|
@ -42,6 +73,20 @@ public class DiskCachedClassHolderSource implements ClassHolderSource {
|
|||
|
||||
@Override
|
||||
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);
|
||||
if (item == null) {
|
||||
item = new Item();
|
||||
|
@ -59,15 +104,17 @@ public class DiskCachedClassHolderSource implements ClassHolderSource {
|
|||
}
|
||||
}
|
||||
if (item.cls == null) {
|
||||
item.dirty = true;
|
||||
item.cls = innerSource.get(name);
|
||||
newClasses.add(name);
|
||||
}
|
||||
}
|
||||
return item.cls;
|
||||
return item;
|
||||
}
|
||||
|
||||
private static class Item {
|
||||
ClassHolder cls;
|
||||
boolean dirty;
|
||||
}
|
||||
|
||||
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");
|
||||
* 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.IOException;
|
||||
import java.io.InputStream;
|
||||
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.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.cache.MethodNodeCache;
|
||||
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 AstIO astIO;
|
||||
private final ClassDateProvider classDateProvider;
|
||||
private final Map<MethodReference, Item> cache = new HashMap<>();
|
||||
private final Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
||||
private final Set<MethodReference> newMethods = new HashSet<>();
|
||||
private final Set<MethodReference> newAsyncMethods = new HashSet<>();
|
||||
|
||||
public DiskRegularMethodNodeCache(File directory, SymbolTable symbolTable, SymbolTable fileTable,
|
||||
ClassDateProvider classDateProvider) {
|
||||
public DiskMethodNodeCache(File directory, SymbolTable symbolTable, SymbolTable fileTable) {
|
||||
this.directory = directory;
|
||||
astIO = new AstIO(symbolTable, fileTable);
|
||||
this.classDateProvider = classDateProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegularMethodNode get(MethodReference methodReference) {
|
||||
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||
Item item = cache.get(methodReference);
|
||||
if (item == null) {
|
||||
item = new Item();
|
||||
|
@ -66,7 +57,7 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
if (file.exists()) {
|
||||
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
DataInput input = new DataInputStream(stream);
|
||||
if (!checkIfDependenciesChanged(input, file)) {
|
||||
if (!checkIfDependenciesChanged(input, cacheStatus)) {
|
||||
item.node = astIO.read(input, methodReference);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -78,15 +69,16 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void store(MethodReference methodReference, RegularMethodNode node) {
|
||||
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||
Item item = new Item();
|
||||
item.node = node;
|
||||
item.dependencies = dependencies.get().clone();
|
||||
cache.put(methodReference, item);
|
||||
newMethods.add(methodReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncMethodNode getAsync(MethodReference methodReference) {
|
||||
public AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||
AsyncItem item = asyncCache.get(methodReference);
|
||||
if (item == null) {
|
||||
item = new AsyncItem();
|
||||
|
@ -95,7 +87,7 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
if (file.exists()) {
|
||||
try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
DataInput input = new DataInputStream(stream);
|
||||
if (!checkIfDependenciesChanged(input, file)) {
|
||||
if (!checkIfDependenciesChanged(input, cacheStatus)) {
|
||||
item.node = astIO.readAsync(input, methodReference);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -106,12 +98,11 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
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();
|
||||
for (int i = 0; i < depCount; ++i) {
|
||||
String depClass = input.readUTF();
|
||||
Date depDate = classDateProvider.getModificationDate(depClass);
|
||||
if (depDate == null || depDate.after(new Date(file.lastModified()))) {
|
||||
if (cacheStatus.isStaleClass(depClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -119,9 +110,10 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node) {
|
||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> depenencies) {
|
||||
AsyncItem item = new AsyncItem();
|
||||
item.node = node;
|
||||
item.dependencies = depenencies.get().clone();
|
||||
asyncCache.put(methodReference, item);
|
||||
newAsyncMethods.add(methodReference);
|
||||
}
|
||||
|
@ -129,32 +121,24 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
public void flush() throws IOException {
|
||||
for (MethodReference method : newMethods) {
|
||||
File file = getMethodFile(method, true);
|
||||
AstDependencyAnalyzer analyzer = new AstDependencyAnalyzer();
|
||||
RegularMethodNode node = cache.get(method).node;
|
||||
node.getBody().acceptVisitor(analyzer);
|
||||
analyzer.dependencies.add(method.getClassName());
|
||||
Item item = cache.get(method);
|
||||
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
||||
output.writeShort(analyzer.dependencies.size());
|
||||
for (String dependency : analyzer.dependencies) {
|
||||
output.writeShort(item.dependencies.length);
|
||||
for (String dependency : item.dependencies) {
|
||||
output.writeUTF(dependency);
|
||||
}
|
||||
astIO.write(output, node);
|
||||
astIO.write(output, item.node);
|
||||
}
|
||||
}
|
||||
for (MethodReference method : newAsyncMethods) {
|
||||
File file = getMethodFile(method, true);
|
||||
AstDependencyAnalyzer analyzer = new AstDependencyAnalyzer();
|
||||
AsyncMethodNode node = asyncCache.get(method).node;
|
||||
for (AsyncMethodPart part : node.getBody()) {
|
||||
part.getStatement().acceptVisitor(analyzer);
|
||||
}
|
||||
analyzer.dependencies.add(method.getClassName());
|
||||
AsyncItem item = asyncCache.get(method);
|
||||
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
|
||||
output.writeShort(analyzer.dependencies.size());
|
||||
for (String dependency : analyzer.dependencies) {
|
||||
output.writeShort(item.dependencies.length);
|
||||
for (String dependency : item.dependencies) {
|
||||
output.writeUTF(dependency);
|
||||
}
|
||||
astIO.writeAsync(output, node);
|
||||
astIO.writeAsync(output, item.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,27 +149,13 @@ public class DiskRegularMethodNodeCache implements MethodNodeCache {
|
|||
+ (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 {
|
||||
RegularMethodNode node;
|
||||
String[] dependencies;
|
||||
}
|
||||
|
||||
private static class AsyncItem {
|
||||
AsyncMethodNode node;
|
||||
String[] dependencies;
|
||||
}
|
||||
}
|
|
@ -15,28 +15,42 @@
|
|||
*/
|
||||
package org.teavm.cache;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
import org.teavm.parsing.ClassDateProvider;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
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.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 {
|
||||
private File directory;
|
||||
private ProgramIO programIO;
|
||||
private Map<MethodReference, Item> cache = new HashMap<>();
|
||||
private Set<MethodReference> newMethods = new HashSet<>();
|
||||
private ClassDateProvider classDateProvider;
|
||||
|
||||
public DiskProgramCache(File directory, SymbolTable symbolTable, SymbolTable fileTable,
|
||||
ClassDateProvider classDateProvider) {
|
||||
public DiskProgramCache(File directory, SymbolTable symbolTable, SymbolTable fileTable) {
|
||||
this.directory = directory;
|
||||
programIO = new ProgramIO(symbolTable, fileTable);
|
||||
this.classDateProvider = classDateProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program get(MethodReference method) {
|
||||
public Program get(MethodReference method, CacheStatus cacheStatus) {
|
||||
Item item = cache.get(method);
|
||||
if (item == null) {
|
||||
item = new Item();
|
||||
|
@ -49,8 +63,7 @@ public class DiskProgramCache implements ProgramCache {
|
|||
boolean dependenciesChanged = false;
|
||||
for (int i = 0; i < depCount; ++i) {
|
||||
String depClass = input.readUTF();
|
||||
Date depDate = classDateProvider.getModificationDate(depClass);
|
||||
if (depDate == null || depDate.after(new Date(file.lastModified()))) {
|
||||
if (cacheStatus.isStaleClass(depClass)) {
|
||||
dependenciesChanged = true;
|
||||
break;
|
||||
}
|
||||
|
@ -67,33 +80,29 @@ public class DiskProgramCache implements ProgramCache {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void store(MethodReference method, Program program) {
|
||||
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
||||
Item item = new Item();
|
||||
cache.put(method, item);
|
||||
item.program = program;
|
||||
item.dependencies = dependencies.get().clone();
|
||||
newMethods.add(method);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
public void flush(ClassReaderSource classSource) throws IOException {
|
||||
Date currentTime = new Date();
|
||||
for (MethodReference method : newMethods) {
|
||||
Item item = cache.get(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();
|
||||
|
||||
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
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);
|
||||
}
|
||||
programIO.write(program, stream);
|
||||
programIO.write(item.program, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,60 +114,6 @@ public class DiskProgramCache implements ProgramCache {
|
|||
|
||||
static class Item {
|
||||
Program program;
|
||||
}
|
||||
|
||||
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) { }
|
||||
String[] dependencies;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,28 +13,34 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.RegularMethodNode;
|
||||
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
|
||||
public RegularMethodNode get(MethodReference methodReference) {
|
||||
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(MethodReference methodReference, RegularMethodNode node) {
|
||||
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncMethodNode getAsync(MethodReference methodReference) {
|
||||
public AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@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");
|
||||
* 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
|
||||
* 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.RegularMethodNode;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
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;
|
||||
|
||||
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 org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
import org.teavm.model.BasicBlock;
|
||||
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 {
|
||||
private SymbolTable symbolTable;
|
||||
private SymbolTable fileTable;
|
||||
private ReferenceCache referenceCache = new ReferenceCache();
|
||||
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
||||
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
||||
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
||||
|
@ -733,7 +800,7 @@ public class ProgramIO {
|
|||
case 1: {
|
||||
ClassConstantInstruction insn = new ClassConstantInstruction();
|
||||
insn.setReceiver(program.variableAt(input.readShort()));
|
||||
insn.setConstant(ValueType.parse(symbolTable.at(input.readInt())));
|
||||
insn.setConstant(parseValueType(symbolTable.at(input.readInt())));
|
||||
return insn;
|
||||
}
|
||||
case 2: {
|
||||
|
@ -798,7 +865,7 @@ public class ProgramIO {
|
|||
case 11: {
|
||||
CastInstruction insn = new CastInstruction();
|
||||
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()));
|
||||
return insn;
|
||||
}
|
||||
|
@ -870,7 +937,7 @@ public class ProgramIO {
|
|||
case 21: {
|
||||
ConstructArrayInstruction insn = new ConstructArrayInstruction();
|
||||
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()));
|
||||
return insn;
|
||||
}
|
||||
|
@ -883,7 +950,7 @@ public class ProgramIO {
|
|||
case 23: {
|
||||
ConstructMultiArrayInstruction insn = new ConstructMultiArrayInstruction();
|
||||
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();
|
||||
for (int i = 0; i < dimensionCount; ++i) {
|
||||
insn.getDimensions().add(program.variableAt(input.readShort()));
|
||||
|
@ -897,7 +964,7 @@ public class ProgramIO {
|
|||
String className = symbolTable.at(input.readInt());
|
||||
String fieldName = symbolTable.at(input.readInt());
|
||||
insn.setField(new FieldReference(className, fieldName));
|
||||
insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt())));
|
||||
insn.setFieldType(parseValueType(symbolTable.at(input.readInt())));
|
||||
return insn;
|
||||
}
|
||||
case 25: {
|
||||
|
@ -906,7 +973,7 @@ public class ProgramIO {
|
|||
String className = symbolTable.at(input.readInt());
|
||||
String fieldName = symbolTable.at(input.readInt());
|
||||
insn.setField(new FieldReference(className, fieldName));
|
||||
insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt())));
|
||||
insn.setFieldType(parseValueType(symbolTable.at(input.readInt())));
|
||||
return insn;
|
||||
}
|
||||
case 26: {
|
||||
|
@ -914,7 +981,7 @@ public class ProgramIO {
|
|||
insn.setInstance(program.variableAt(input.readShort()));
|
||||
String className = 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.setValue(program.variableAt(input.readShort()));
|
||||
insn.setFieldType(type);
|
||||
|
@ -924,7 +991,7 @@ public class ProgramIO {
|
|||
PutFieldInstruction insn = new PutFieldInstruction();
|
||||
String className = 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.setValue(program.variableAt(input.readShort()));
|
||||
insn.setFieldType(type);
|
||||
|
@ -969,8 +1036,8 @@ public class ProgramIO {
|
|||
int receiverIndex = input.readShort();
|
||||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||
String className = symbolTable.at(input.readInt());
|
||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(new MethodReference(className, methodDesc));
|
||||
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(createMethodReference(className, methodDesc));
|
||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||
|
@ -984,8 +1051,8 @@ public class ProgramIO {
|
|||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||
insn.setInstance(program.variableAt(input.readShort()));
|
||||
String className = symbolTable.at(input.readInt());
|
||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(new MethodReference(className, methodDesc));
|
||||
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(createMethodReference(className, methodDesc));
|
||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||
|
@ -999,8 +1066,8 @@ public class ProgramIO {
|
|||
insn.setReceiver(receiverIndex >= 0 ? program.variableAt(receiverIndex) : null);
|
||||
insn.setInstance(program.variableAt(input.readShort()));
|
||||
String className = symbolTable.at(input.readInt());
|
||||
MethodDescriptor methodDesc = MethodDescriptor.parse(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(new MethodReference(className, methodDesc));
|
||||
MethodDescriptor methodDesc = parseMethodDescriptor(symbolTable.at(input.readInt()));
|
||||
insn.setMethod(createMethodReference(className, methodDesc));
|
||||
int paramCount = insn.getMethod().getDescriptor().parameterCount();
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||
|
@ -1010,7 +1077,7 @@ public class ProgramIO {
|
|||
case 36: {
|
||||
IsInstanceInstruction insn = new IsInstanceInstruction();
|
||||
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()));
|
||||
return insn;
|
||||
}
|
||||
|
@ -1041,7 +1108,7 @@ public class ProgramIO {
|
|||
short instance = input.readShort();
|
||||
insn.setReceiver(receiver >= 0 ? program.variableAt(receiver) : 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();
|
||||
for (int i = 0; i < argsCount; ++i) {
|
||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||
|
@ -1063,31 +1130,31 @@ public class ProgramIO {
|
|||
switch (kind) {
|
||||
case 0:
|
||||
return MethodHandle.fieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
parseValueType(symbolTable.at(input.readInt())));
|
||||
case 1:
|
||||
return MethodHandle.staticFieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
parseValueType(symbolTable.at(input.readInt())));
|
||||
case 2:
|
||||
return MethodHandle.fieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
parseValueType(symbolTable.at(input.readInt())));
|
||||
case 3:
|
||||
return MethodHandle.staticFieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
parseValueType(symbolTable.at(input.readInt())));
|
||||
case 4:
|
||||
return MethodHandle.virtualCaller(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||
case 5:
|
||||
return MethodHandle.staticCaller(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||
case 6:
|
||||
return MethodHandle.specialCaller(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||
case 7:
|
||||
return MethodHandle.constructorCaller(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||
case 8:
|
||||
return MethodHandle.interfaceCaller(symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
parseMethodDescriptor(symbolTable.at(input.readInt())));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected method handle type: " + kind);
|
||||
}
|
||||
|
@ -1107,7 +1174,7 @@ public class ProgramIO {
|
|||
case 4:
|
||||
return new RuntimeConstant(input.readUTF());
|
||||
case 5:
|
||||
return new RuntimeConstant(ValueType.parse(symbolTable.at(input.readInt())));
|
||||
return new RuntimeConstant(parseValueType(symbolTable.at(input.readInt())));
|
||||
case 6:
|
||||
return new RuntimeConstant(MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 7:
|
||||
|
@ -1116,4 +1183,16 @@ public class ProgramIO {
|
|||
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
|
||||
public DefaultCallGraphNode getNode(MethodReference method) {
|
||||
return nodes.computeIfAbsent(method, k -> new DefaultCallGraphNode(this, method));
|
||||
return nodes.computeIfAbsent(method, k -> new DefaultCallGraphNode(this, k));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.teavm.model.TextLocation;
|
|||
public class DefaultCallGraphNode implements CallGraphNode {
|
||||
private DefaultCallGraph graph;
|
||||
private MethodReference method;
|
||||
private Set<DefaultCallSite> callSites = new LinkedHashSet<>();
|
||||
private Set<DefaultCallSite> callSites = new LinkedHashSet<>(10, 0.5f);
|
||||
private Set<DefaultCallSite> safeCallSites;
|
||||
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
||||
private List<DefaultCallSite> safeCallersCallSites;
|
||||
|
|
|
@ -48,7 +48,7 @@ public class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(location, callee, field);
|
||||
return Objects.hash(location, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,7 +60,6 @@ public class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
|
|||
return false;
|
||||
}
|
||||
DefaultFieldAccessSite other = (DefaultFieldAccessSite) obj;
|
||||
return Objects.equals(location, other.location) && Objects.equals(callee, other.callee)
|
||||
&& Objects.equals(field, other.field);
|
||||
return Objects.equals(location, other.location) && 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.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ReferenceCache;
|
||||
|
||||
public class DebugInformation {
|
||||
String[] fileNames;
|
||||
|
@ -50,6 +51,7 @@ public class DebugInformation {
|
|||
Map<String, ClassMetadata> classMetadataByJsName;
|
||||
RecordArray methodEntrances;
|
||||
MethodTree methodTree;
|
||||
ReferenceCache referenceCache = new ReferenceCache();
|
||||
|
||||
public String[] getFilesNames() {
|
||||
return fileNames.clone();
|
||||
|
@ -82,13 +84,13 @@ public class DebugInformation {
|
|||
public MethodDescriptor[] getMethods() {
|
||||
MethodDescriptor[] descriptors = new MethodDescriptor[methods.length];
|
||||
for (int i = 0; i < descriptors.length; ++i) {
|
||||
descriptors[i] = MethodDescriptor.parse(methods[i]);
|
||||
descriptors[i] = referenceCache.parseDescriptorCached(methods[i]);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public MethodDescriptor getMethod(int methodId) {
|
||||
return MethodDescriptor.parse(methods[methodId]);
|
||||
return referenceCache.parseDescriptorCached(methods[methodId]);
|
||||
}
|
||||
|
||||
public MethodReference[] getExactMethods() {
|
||||
|
@ -115,7 +117,8 @@ public class DebugInformation {
|
|||
long item = exactMethods[index];
|
||||
int classIndex = (int) (item >>> 32);
|
||||
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) {
|
||||
|
@ -186,7 +189,7 @@ public class DebugInformation {
|
|||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
return new MethodReference(className, MethodDescriptor.parse(method));
|
||||
return referenceCache.getCached(className, referenceCache.parseDescriptorCached(method));
|
||||
}
|
||||
|
||||
public MethodReference getMethodAt(int line, int column) {
|
||||
|
@ -678,8 +681,8 @@ public class DebugInformation {
|
|||
long item = exactMethods[data[start + i]];
|
||||
int classIndex = (int) (item >>> 32);
|
||||
int methodIndex = (int) item;
|
||||
references[i] = new MethodReference(classNames[classIndex],
|
||||
MethodDescriptor.parse(methods[methodIndex]));
|
||||
references[i] = referenceCache.getCached(classNames[classIndex],
|
||||
referenceCache.parseDescriptorCached(methods[methodIndex]));
|
||||
}
|
||||
return references;
|
||||
}
|
||||
|
|
|
@ -15,23 +15,21 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import org.teavm.model.CallLocation;
|
||||
|
||||
public abstract class AbstractDependencyListener implements DependencyListener {
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
||||
public void classReached(DependencyAgent agent, String className) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location) {
|
||||
public void fieldReached(DependencyAgent agent, FieldDependency field) {
|
||||
}
|
||||
|
||||
@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 String className;
|
||||
private ClassReader classReader;
|
||||
boolean activated;
|
||||
|
||||
ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) {
|
||||
this.analyzer = analyzer;
|
||||
|
@ -43,9 +44,9 @@ public class ClassDependency implements ClassDependencyInfo {
|
|||
return classReader;
|
||||
}
|
||||
|
||||
public void initClass(CallLocation callLocation) {
|
||||
public void initClass(CallLocation location) {
|
||||
if (!isMissing()) {
|
||||
analyzer.initClass(this, callLocation);
|
||||
analyzer.initClass(this, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.dependency;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
|
@ -52,16 +53,20 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
|||
analyzer.submitMethod(method, program);
|
||||
}
|
||||
|
||||
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
|
||||
return analyzer.linkMethod(methodRef, callLocation);
|
||||
public MethodDependency linkMethod(MethodReference methodRef) {
|
||||
return analyzer.linkMethod(methodRef);
|
||||
}
|
||||
|
||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
||||
return analyzer.linkClass(className, callLocation);
|
||||
public MethodDependency linkMethod(String className, MethodDescriptor descriptor) {
|
||||
return analyzer.linkMethod(className, descriptor);
|
||||
}
|
||||
|
||||
public FieldDependency linkField(FieldReference fieldRef, CallLocation callLocation) {
|
||||
return analyzer.linkField(fieldRef, callLocation);
|
||||
public ClassDependency linkClass(String className) {
|
||||
return analyzer.linkClass(className);
|
||||
}
|
||||
|
||||
public FieldDependency linkField(FieldReference fieldRef) {
|
||||
return analyzer.linkField(fieldRef);
|
||||
}
|
||||
|
||||
public Diagnostics getDiagnostics() {
|
||||
|
@ -83,6 +88,11 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
|||
return analyzer.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassHierarchy getClassHierarchy() {
|
||||
return analyzer.getClassHierarchy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MethodReference> getReachableMethods() {
|
||||
return analyzer.getReachableMethods();
|
||||
|
@ -122,4 +132,8 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
|||
public CallGraph 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");
|
||||
* 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 java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
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.CallGraphNode;
|
||||
import org.teavm.callgraph.DefaultCallGraph;
|
||||
import org.teavm.common.CachedMapper;
|
||||
import org.teavm.common.Mapper;
|
||||
|
@ -38,7 +45,9 @@ import org.teavm.common.ServiceRepository;
|
|||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
|
@ -47,6 +56,8 @@ import org.teavm.model.ElementModifier;
|
|||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
@ -54,23 +65,32 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ReferenceCache;
|
||||
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.util.BasicBlockSplitter;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
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 MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", void.class);
|
||||
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
|
||||
static final boolean shouldTag = System.getProperty("org.teavm.tagDependencies", "false").equals("true")
|
||||
|| shouldLog;
|
||||
static final boolean dependencyReport = System.getProperty("org.teavm.dependencyReport", "false").equals("true");
|
||||
private int classNameSuffix;
|
||||
private ClassReaderSource unprocessedClassSource;
|
||||
private DependencyClassSource classSource;
|
||||
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 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<String, ClassDependency> classCache;
|
||||
private List<DependencyListener> listeners = new ArrayList<>();
|
||||
|
@ -90,23 +110,19 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
private boolean completing;
|
||||
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
||||
private List<DependencyNode> allNodes = new ArrayList<>();
|
||||
private ClassHierarchy classHierarchy;
|
||||
IncrementalCache incrementalCache = new IncrementalCache();
|
||||
boolean asyncSupported;
|
||||
|
||||
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||
Diagnostics diagnostics) {
|
||||
unprocessedClassSource = classSource;
|
||||
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.services = services;
|
||||
methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutableImplementation(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 -> {
|
||||
FieldReader field = fieldReaderCache.map(preimage);
|
||||
if (field != null && !field.getReference().equals(preimage)) {
|
||||
|
@ -150,10 +166,32 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
type = new DependencyType(this, name, types.size());
|
||||
types.add(type);
|
||||
typeMap.put(name, type);
|
||||
|
||||
if (!name.startsWith("[") && !name.startsWith("~")) {
|
||||
markSupertypesAsHavingSubtypes(name);
|
||||
}
|
||||
}
|
||||
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() {
|
||||
return createNode(null);
|
||||
}
|
||||
|
@ -172,6 +210,15 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
return classSource;
|
||||
}
|
||||
|
||||
public boolean isSynthesizedClass(String className) {
|
||||
return classSource.isGeneratedClass(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassHierarchy getClassHierarchy() {
|
||||
return classHierarchy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
|
@ -231,8 +278,8 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
dep.used = false;
|
||||
lock(dep, false);
|
||||
deferredTasks.add(() -> {
|
||||
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyAnalyzer.this);
|
||||
graphBuilder.buildGraph(dep);
|
||||
processInvokeDynamic(dep);
|
||||
processMethod(dep);
|
||||
dep.used = true;
|
||||
});
|
||||
|
||||
|
@ -240,6 +287,8 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract void processMethod(MethodDependency methodDep);
|
||||
|
||||
public void addDependencyListener(DependencyListener listener) {
|
||||
listeners.add(listener);
|
||||
listener.started(agent);
|
||||
|
@ -254,7 +303,7 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
if (parameters.length + 1 != argumentTypes.length) {
|
||||
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();
|
||||
DependencyNode[] varNodes = method.getVariables();
|
||||
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) {
|
||||
deferredTasks.add(task);
|
||||
}
|
||||
|
||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
||||
public ClassDependency linkClass(String className) {
|
||||
if (completing && getClass(className) == null) {
|
||||
throw new IllegalStateException("Can't link class during completion phase");
|
||||
}
|
||||
ClassDependency dep = classCache.map(className);
|
||||
boolean added = true;
|
||||
if (callLocation == null || callLocation.getMethod() == null) {
|
||||
added = classesAddedByRoot.add(className);
|
||||
}
|
||||
if (!dep.isMissing() && added) {
|
||||
if (!dep.activated) {
|
||||
dep.activated = true;
|
||||
if (!dep.isMissing()) {
|
||||
deferredTasks.add(() -> {
|
||||
for (DependencyListener listener : listeners) {
|
||||
listener.classReached(agent, className, callLocation);
|
||||
listener.classReached(agent, className);
|
||||
}
|
||||
});
|
||||
|
||||
ClassReader cls = dep.getClassReader();
|
||||
if (cls.getParent() != null) {
|
||||
linkClass(cls.getParent(), callLocation);
|
||||
if (cls.getParent() != null && !classCache.caches(cls.getParent())) {
|
||||
linkClass(cls.getParent());
|
||||
}
|
||||
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) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
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;
|
||||
}
|
||||
|
||||
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 (methodRef == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
MethodReader methodReader = methodReaderCache.map(methodRef);
|
||||
if (methodReader != null) {
|
||||
methodRef = methodReader.getReference();
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!dep.activated) {
|
||||
dep.activated = true;
|
||||
if (!dep.isMissing()) {
|
||||
for (DependencyListener listener : listeners) {
|
||||
listener.methodReached(agent, graph, callLocation);
|
||||
listener.methodReached(agent, dep);
|
||||
}
|
||||
activateDependencyPlugin(graph, callLocation);
|
||||
activateDependencyPlugin(dep);
|
||||
}
|
||||
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();
|
||||
MethodReader method = reader.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||
MethodReader method = reader.getMethod(CLINIT_METHOD);
|
||||
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;
|
||||
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
|
||||
|
||||
parameterNodes[0] = createNode(ValueType.object(methodRef.getClassName()));
|
||||
parameterNodes[0].method = methodRef;
|
||||
if (shouldTag) {
|
||||
parameterNodes[0].setTag(methodRef + ":0");
|
||||
}
|
||||
parameterNodes[0] = createParameterNode(methodRef, ValueType.object(methodRef.getClassName()), 0);
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
parameterNodes[i + 1] = createNode(arguments[i]);
|
||||
parameterNodes[i + 1].method = methodRef;
|
||||
if (shouldTag) {
|
||||
parameterNodes[i + 1].setTag(methodRef + ":" + (i + 1));
|
||||
}
|
||||
parameterNodes[i + 1] = createParameterNode(methodRef, arguments[i], i + 1);
|
||||
}
|
||||
|
||||
DependencyNode resultNode;
|
||||
if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
|
||||
resultNode = null;
|
||||
} else {
|
||||
resultNode = createNode(methodRef.getDescriptor().getResultType());
|
||||
resultNode.method = methodRef;
|
||||
if (shouldTag) {
|
||||
resultNode.setTag(methodRef + ":RESULT");
|
||||
}
|
||||
}
|
||||
DependencyNode thrown = createNode();
|
||||
thrown.method = methodRef;
|
||||
if (shouldTag) {
|
||||
thrown.setTag(methodRef + ":THROWN");
|
||||
resultNode = createResultNode(methodRef);
|
||||
}
|
||||
DependencyNode thrown = createThrownNode(methodRef);
|
||||
MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
|
||||
method, methodRef);
|
||||
if (method != null) {
|
||||
deferredTasks.add(() -> {
|
||||
CallLocation caller = new CallLocation(dep.getMethod().getReference());
|
||||
linkClass(dep.getMethod().getOwnerName(), caller).initClass(caller);
|
||||
});
|
||||
deferredTasks.add(() -> linkClass(dep.getMethod().getOwnerName())
|
||||
.initClass(new CallLocation(dep.getMethod().getReference())));
|
||||
}
|
||||
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) {
|
||||
deferredTasks.add(() -> {
|
||||
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyAnalyzer.this);
|
||||
graphBuilder.buildGraph(dep);
|
||||
processInvokeDynamic(dep);
|
||||
processMethod(dep);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MethodReference> getReachableMethods() {
|
||||
return methodCache.getCachedPreimages();
|
||||
return readonlyReachedMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -500,23 +527,14 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
|
||||
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
|
||||
|
||||
public FieldDependency linkField(FieldReference fieldRef, CallLocation location) {
|
||||
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);
|
||||
}
|
||||
public FieldDependency linkField(FieldReference fieldRef) {
|
||||
FieldDependency dep = fieldCache.map(fieldRef);
|
||||
if (!dep.activated) {
|
||||
dep.activated = true;
|
||||
if (!dep.isMissing()) {
|
||||
deferredTasks.add(() -> linkClass(fieldRef.getClassName(), location).initClass(location));
|
||||
}
|
||||
if (!dep.isMissing() && added) {
|
||||
for (DependencyListener listener : listeners) {
|
||||
listener.fieldReached(agent, dep, location);
|
||||
listener.fieldReached(agent, dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dep;
|
||||
|
@ -533,21 +551,14 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
|
||||
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field) {
|
||||
DependencyNode node = createNode(field != null ? field.getType() : null);
|
||||
if (shouldTag) {
|
||||
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;
|
||||
DependencyNode node = createFieldNode(fieldRef, field != null ? field.getType() : null);
|
||||
return new FieldDependency(node, field, fieldRef);
|
||||
}
|
||||
|
||||
private void activateDependencyPlugin(MethodDependency methodDep, CallLocation location) {
|
||||
private void activateDependencyPlugin(MethodDependency methodDep) {
|
||||
attachDependencyPlugin(methodDep);
|
||||
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
|
||||
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
|
||||
public MethodDependency getMethodImplementation(MethodReference methodRef) {
|
||||
MethodReader method = methodReaderCache.map(methodRef);
|
||||
return method != null ? methodCache.getKnown(method.getReference()) : null;
|
||||
MethodReader method = getMethodHolder(methodRef.getClassName(), methodRef.getDescriptor());
|
||||
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() {
|
||||
|
@ -763,6 +809,10 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
dependencyPlugins.put(method, dependencyPlugin);
|
||||
}
|
||||
|
||||
public IncrementalDependencyProvider getIncrementalDependencies() {
|
||||
return incrementalCache;
|
||||
}
|
||||
|
||||
DependencyTypeFilter getSuperClassFilter(String superClass) {
|
||||
DependencyTypeFilter result = superClassFilters.get(superClass);
|
||||
if (result == null) {
|
||||
|
@ -777,10 +827,148 @@ public class DependencyAnalyzer implements DependencyInfo {
|
|||
result = new ExactTypeFilter(superClass);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -32,19 +36,24 @@ import org.teavm.model.util.ModelUtils;
|
|||
|
||||
class DependencyClassSource implements ClassHolderSource {
|
||||
private ClassReaderSource innerSource;
|
||||
private ClassHierarchy innerHierarchy;
|
||||
private Diagnostics diagnostics;
|
||||
private IncrementalDependencyRegistration dependencyRegistration;
|
||||
private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>();
|
||||
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.diagnostics = diagnostics;
|
||||
innerHierarchy = new ClassHierarchy(innerSource);
|
||||
this.dependencyRegistration = dependencyRegistration;
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
|
@ -53,7 +62,7 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
}
|
||||
if (!transformers.isEmpty()) {
|
||||
for (ClassHolderTransformer transformer : transformers) {
|
||||
transformer.transformClass(cls, innerSource, diagnostics);
|
||||
transformer.transformClass(cls, transformContext);
|
||||
}
|
||||
cls = ModelUtils.copyClass(cls);
|
||||
}
|
||||
|
@ -70,7 +79,7 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
ClassHolder cls = findClass(name);
|
||||
if (cls != null && !transformers.isEmpty()) {
|
||||
for (ClassHolderTransformer transformer : transformers) {
|
||||
transformer.transformClass(cls, innerSource, diagnostics);
|
||||
transformer.transformClass(cls, transformContext);
|
||||
}
|
||||
}
|
||||
return cls;
|
||||
|
@ -88,7 +97,28 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
return generatedClasses.values();
|
||||
}
|
||||
|
||||
public boolean isGeneratedClass(String className) {
|
||||
return generatedClasses.containsKey(className);
|
||||
}
|
||||
|
||||
public void addTransformer(ClassHolderTransformer 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;
|
||||
|
||||
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.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.cache.NoCache;
|
||||
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
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.MethodReference;
|
||||
import org.teavm.model.PhiReader;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.RuntimeConstant;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.TryCatchBlockReader;
|
||||
import org.teavm.model.ValueType;
|
||||
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.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.util.BasicBlockSplitter;
|
||||
|
||||
class DependencyGraphBuilder {
|
||||
private DependencyAnalyzer dependencyAnalyzer;
|
||||
|
@ -62,7 +46,6 @@ class DependencyGraphBuilder {
|
|||
private DependencyNode resultNode;
|
||||
private Program program;
|
||||
private DefaultCallGraphNode caller;
|
||||
private TextLocation currentLocation;
|
||||
private ExceptionConsumer currentExceptionConsumer;
|
||||
|
||||
DependencyGraphBuilder(DependencyAnalyzer dependencyAnalyzer) {
|
||||
|
@ -78,8 +61,6 @@ class DependencyGraphBuilder {
|
|||
program = method.getProgram();
|
||||
resultNode = dep.getResult();
|
||||
|
||||
processInvokeDynamic(dep);
|
||||
|
||||
DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder();
|
||||
boolean[] significantParams = new boolean[dep.getParameterCount()];
|
||||
significantParams[0] = true;
|
||||
|
@ -122,6 +103,7 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
dep.setVariables(nodes);
|
||||
|
||||
reader.setCaller(caller.getMethod());
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlockReader block = program.basicBlockAt(i);
|
||||
currentExceptionConsumer = createExceptionConsumer(dep, block);
|
||||
|
@ -139,7 +121,7 @@ class DependencyGraphBuilder {
|
|||
|
||||
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
||||
if (tryCatch.getExceptionType() != null) {
|
||||
dependencyAnalyzer.linkClass(tryCatch.getExceptionType(), new CallLocation(caller.getMethod()));
|
||||
dependencyAnalyzer.linkClass(tryCatch.getExceptionType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,26 +131,22 @@ class DependencyGraphBuilder {
|
|||
|
||||
MethodDependency methodDep;
|
||||
if (dependencyAnalyzer.asyncSupported) {
|
||||
methodDep = dependencyAnalyzer.linkMethod(
|
||||
new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null);
|
||||
methodDep = dependencyAnalyzer.linkMethod(MONITOR_ENTER_METHOD);
|
||||
syncNodes.add(methodDep.getVariable(1));
|
||||
methodDep.use();
|
||||
}
|
||||
|
||||
methodDep = dependencyAnalyzer.linkMethod(
|
||||
new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null);
|
||||
methodDep = dependencyAnalyzer.linkMethod(MONITOR_ENTER_SYNC_METHOD);
|
||||
syncNodes.add(methodDep.getVariable(1));
|
||||
methodDep.use();
|
||||
|
||||
if (dependencyAnalyzer.asyncSupported) {
|
||||
methodDep = dependencyAnalyzer.linkMethod(
|
||||
new MethodReference(Object.class, "monitorExit", Object.class, void.class), null);
|
||||
methodDep = dependencyAnalyzer.linkMethod(MONITOR_EXIT_METHOD);
|
||||
syncNodes.add(methodDep.getVariable(1));
|
||||
methodDep.use();
|
||||
}
|
||||
|
||||
methodDep = dependencyAnalyzer.linkMethod(
|
||||
new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null);
|
||||
methodDep = dependencyAnalyzer.linkMethod(MONITOR_EXIT_SYNC_METHOD);
|
||||
syncNodes.add(methodDep.getVariable(1));
|
||||
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) {
|
||||
List<? extends TryCatchBlockReader> tryCatchBlocks = block.readTryCatchBlocks();
|
||||
ClassReader[] exceptions = new ClassReader[tryCatchBlocks.size()];
|
||||
|
@ -282,10 +194,9 @@ class DependencyGraphBuilder {
|
|||
|
||||
@Override
|
||||
public void consume(DependencyType type) {
|
||||
ClassReaderSource classSource = analyzer.getClassSource();
|
||||
ClassHierarchy hierarchy = analyzer.getClassHierarchy();
|
||||
for (int i = 0; i < exceptions.length; ++i) {
|
||||
if (exceptions[i] == null || classSource.isSuperType(exceptions[i].getName(), type.getName())
|
||||
.orElse(false)) {
|
||||
if (exceptions[i] == null || hierarchy.isSuperType(exceptions[i].getName(), type.getName(), false)) {
|
||||
if (vars[i] != null) {
|
||||
vars[i].propagate(type);
|
||||
}
|
||||
|
@ -296,123 +207,7 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
static 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 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();
|
||||
}
|
||||
|
||||
private AbstractInstructionAnalyzer reader = new AbstractInstructionAnalyzer() {
|
||||
@Override
|
||||
public void assign(VariableReader receiver, VariableReader assignee) {
|
||||
DependencyNode valueNode = nodes[assignee.getIndex()];
|
||||
|
@ -472,106 +267,6 @@ class DependencyGraphBuilder {
|
|||
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
|
||||
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
|
||||
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
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
|
@ -612,31 +321,16 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
|
||||
@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) {
|
||||
nodes[instance.getIndex()].connect(nodes[receiver.getIndex()].getClassValueNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments) {
|
||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
||||
dependencyAnalyzer.linkClass(method.getClassName(), callLocation).initClass(callLocation);
|
||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method, callLocation);
|
||||
CallLocation callLocation = getCallLocation();
|
||||
if (instance == null) {
|
||||
dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation);
|
||||
} else {
|
||||
dependencyAnalyzer.linkClass(method.getClassName());
|
||||
}
|
||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method);
|
||||
methodDep.addLocation(callLocation);
|
||||
methodDep.use();
|
||||
if (methodDep.isMissing()) {
|
||||
return;
|
||||
|
@ -662,86 +356,39 @@ class DependencyGraphBuilder {
|
|||
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) {
|
||||
DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1];
|
||||
for (int i = 0; i < arguments.size(); ++i) {
|
||||
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
|
||||
}
|
||||
actualArgs[0] = nodes[instance.getIndex()];
|
||||
DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()],
|
||||
actualArgs[0] = getNode(instance);
|
||||
DependencyConsumer listener = new VirtualCallConsumer(getNode(instance),
|
||||
method.getClassName(), method.getDescriptor(), dependencyAnalyzer, actualArgs,
|
||||
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
|
||||
receiver != null ? getNode(receiver) : null, getCallLocation(),
|
||||
currentExceptionConsumer);
|
||||
nodes[instance.getIndex()].addConsumer(listener);
|
||||
getNode(instance).addConsumer(listener);
|
||||
|
||||
dependencyAnalyzer.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
|
||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
||||
dependencyAnalyzer.linkMethod(methodImpl.getReference(), callLocation);
|
||||
dependencyAnalyzer.linkMethod(methodImpl.getReference()).addLocation(getCallLocation());
|
||||
});
|
||||
}
|
||||
|
||||
@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
|
||||
public void nullCheck(VariableReader receiver, VariableReader value) {
|
||||
DependencyNode valueNode = nodes[value.getIndex()];
|
||||
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();
|
||||
super.nullCheck(receiver, value);
|
||||
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitorEnter(VariableReader objectRef) {
|
||||
if (dependencyAnalyzer.asyncSupported) {
|
||||
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();
|
||||
protected DependencyNode getNode(VariableReader variable) {
|
||||
return nodes[variable.getIndex()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitorExit(VariableReader objectRef) {
|
||||
if (dependencyAnalyzer.asyncSupported) {
|
||||
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();
|
||||
protected DependencyAnalyzer getAnalyzer() {
|
||||
return dependencyAnalyzer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.dependency;
|
|||
|
||||
import java.util.Collection;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -26,6 +27,8 @@ public interface DependencyInfo {
|
|||
|
||||
ClassLoader getClassLoader();
|
||||
|
||||
ClassHierarchy getClassHierarchy();
|
||||
|
||||
Collection<MethodReference> getReachableMethods();
|
||||
|
||||
Collection<FieldReference> getReachableFields();
|
||||
|
|
|
@ -15,16 +15,14 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import org.teavm.model.CallLocation;
|
||||
|
||||
public interface DependencyListener {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.ArrayDeque;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
@ -40,10 +41,10 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
ObjectArrayList<Transition> transitionList;
|
||||
String tag;
|
||||
private DependencyNode arrayItemNode;
|
||||
private DependencyNode classValueNode;
|
||||
DependencyNode classValueNode;
|
||||
DependencyNode classNodeParent;
|
||||
boolean classNodeComplete;
|
||||
private int degree;
|
||||
int degree;
|
||||
boolean locked;
|
||||
MethodReference method;
|
||||
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();
|
||||
|
||||
if (action != null) {
|
||||
action.run();
|
||||
}
|
||||
|
||||
for (ObjectCursor<Transition> cursor : transitions) {
|
||||
Transition transition = cursor.value;
|
||||
for (Transition transition : transitions) {
|
||||
if (transition.source.filter(type) && transition.filterType(type)) {
|
||||
dependencyAnalyzer.schedulePropagation(transition, type);
|
||||
}
|
||||
|
@ -233,17 +233,24 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return true;
|
||||
}
|
||||
|
||||
return getFilter().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 = "java.lang.Object";
|
||||
superClass = typeFilter.toString();
|
||||
}
|
||||
cachedTypeFilter = dependencyAnalyzer.getSuperClassFilter(superClass);
|
||||
}
|
||||
|
||||
return cachedTypeFilter.match(type);
|
||||
}
|
||||
return cachedTypeFilter;
|
||||
}
|
||||
|
||||
public void addConsumer(DependencyConsumer consumer) {
|
||||
|
@ -262,8 +269,18 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Node must not be null");
|
||||
|
@ -273,7 +290,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
transitionList = new ObjectArrayList<>();
|
||||
}
|
||||
if (transitions.containsKey(node)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
Transition transition = new Transition(this, node, filter);
|
||||
|
@ -285,7 +302,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
|
||||
if (typeSet != null) {
|
||||
if (typeSet == node.typeSet) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (typeSet.transitions != null) {
|
||||
typeSet.transitions.add(transition);
|
||||
|
@ -303,14 +320,13 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
connectArrayItemNodes(node);
|
||||
|
||||
if (classValueNode != null && classValueNode != this) {
|
||||
classValueNode.connect(node.getClassValueNode());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void connectArrayItemNodes(DependencyNode node) {
|
||||
if (degree > DEGREE_THRESHOLD || node.degree > DEGREE_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
if (!isArray(typeFilter) || !isArray(node.typeFilter)) {
|
||||
return;
|
||||
}
|
||||
|
@ -382,15 +398,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
@Override
|
||||
public DependencyNode getArrayItem() {
|
||||
if (arrayItemNode == null) {
|
||||
ValueType itemTypeFilter = typeFilter instanceof ValueType.Array
|
||||
? ((ValueType.Array) typeFilter).getItemType()
|
||||
: null;
|
||||
arrayItemNode = dependencyAnalyzer.createNode(itemTypeFilter);
|
||||
arrayItemNode.degree = degree + 1;
|
||||
arrayItemNode.method = method;
|
||||
if (DependencyAnalyzer.shouldTag) {
|
||||
arrayItemNode.tag = tag + "[";
|
||||
}
|
||||
arrayItemNode = dependencyAnalyzer.createArrayItemNode(this);
|
||||
}
|
||||
return arrayItemNode;
|
||||
}
|
||||
|
@ -398,13 +406,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
@Override
|
||||
public DependencyNode getClassValueNode() {
|
||||
if (classValueNode == null) {
|
||||
classValueNode = dependencyAnalyzer.createNode();
|
||||
classValueNode.degree = degree;
|
||||
classValueNode.classValueNode = classValueNode;
|
||||
classValueNode.classNodeParent = this;
|
||||
if (DependencyAnalyzer.shouldTag) {
|
||||
classValueNode.tag = tag + "@";
|
||||
}
|
||||
classValueNode = dependencyAnalyzer.createClassValueNode(degree, this);
|
||||
}
|
||||
return classValueNode;
|
||||
}
|
||||
|
@ -507,6 +509,10 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
|
||||
Collection<DependencyNode> findDomain() {
|
||||
if (!dependencyAnalyzer.domainOptimizationEnabled()) {
|
||||
return Collections.singleton(this);
|
||||
}
|
||||
|
||||
Set<DependencyNode> visited = new LinkedHashSet<>(50);
|
||||
Deque<DependencyNode> stack = new ArrayDeque<>(50);
|
||||
stack.push(this);
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import org.teavm.model.CallLocation;
|
||||
|
||||
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 String name;
|
||||
int index;
|
||||
boolean subtypeExists;
|
||||
|
||||
DependencyType(DependencyAnalyzer dependencyAnalyzer, String name, int index) {
|
||||
this.dependencyAnalyzer = dependencyAnalyzer;
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public interface DependencyTypeFilter {
|
||||
boolean match(DependencyType type);
|
||||
|
||||
default int[] tryExtract(BitSet types) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.dependency;
|
|||
|
||||
class ExactTypeFilter implements DependencyTypeFilter {
|
||||
String typeName;
|
||||
int cache = -1;
|
||||
|
||||
ExactTypeFilter(String typeName) {
|
||||
this.typeName = typeName;
|
||||
|
@ -24,6 +25,13 @@ class ExactTypeFilter implements DependencyTypeFilter {
|
|||
|
||||
@Override
|
||||
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;
|
||||
|
||||
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.FieldReference;
|
||||
|
||||
|
@ -22,6 +28,9 @@ public class FieldDependency implements FieldDependencyInfo {
|
|||
DependencyNode value;
|
||||
private FieldReader field;
|
||||
private FieldReference reference;
|
||||
private List<LocationListener> locationListeners;
|
||||
private Set<CallLocation> locations;
|
||||
boolean activated;
|
||||
|
||||
FieldDependency(DependencyNode value, FieldReader field, FieldReference reference) {
|
||||
this.value = value;
|
||||
|
@ -47,4 +56,32 @@ public class FieldDependency implements FieldDependencyInfo {
|
|||
public boolean isMissing() {
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -31,6 +37,9 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
boolean used;
|
||||
DependencyPlugin dependencyPlugin;
|
||||
boolean dependencyPluginAttached;
|
||||
private List<LocationListener> locationListeners;
|
||||
private Set<CallLocation> locations;
|
||||
boolean activated;
|
||||
|
||||
MethodDependency(DependencyAnalyzer dependencyAnalyzer, DependencyNode[] variableNodes, int parameterCount,
|
||||
DependencyNode resultNode, DependencyNode thrown, MethodHolder method, MethodReference reference) {
|
||||
|
@ -100,6 +109,34 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
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) {
|
||||
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;
|
||||
|
||||
import java.util.BitSet;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class SuperArrayFilter implements DependencyTypeFilter {
|
||||
private DependencyAnalyzer analyzer;
|
||||
private DependencyTypeFilter itemTypeFilter;
|
||||
private BitSet knownTypes = new BitSet();
|
||||
private BitSet cache = new BitSet();
|
||||
|
||||
SuperArrayFilter(DependencyAnalyzer analyzer, DependencyTypeFilter itemTypeFilter) {
|
||||
this.analyzer = analyzer;
|
||||
|
@ -28,6 +31,16 @@ class SuperArrayFilter implements DependencyTypeFilter {
|
|||
|
||||
@Override
|
||||
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("[")) {
|
||||
return false;
|
||||
}
|
||||
|
@ -42,4 +55,26 @@ class SuperArrayFilter implements DependencyTypeFilter {
|
|||
}
|
||||
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;
|
||||
|
||||
import com.carrotsearch.hppc.IntIntHashMap;
|
||||
import com.carrotsearch.hppc.IntIntMap;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import java.util.BitSet;
|
||||
import org.teavm.common.OptionalPredicate;
|
||||
|
||||
class SuperClassFilter implements DependencyTypeFilter {
|
||||
private ClassReaderSource classSource;
|
||||
private String superType;
|
||||
private IntIntMap cache = new IntIntHashMap();
|
||||
private static final int[] EMPTY_ARRAY = new int[0];
|
||||
private DependencyType superType;
|
||||
private OptionalPredicate<String> predicate;
|
||||
private BitSet knownTypes = new BitSet();
|
||||
private BitSet cache = new BitSet();
|
||||
|
||||
SuperClassFilter(ClassReaderSource classSource, String superType) {
|
||||
this.classSource = classSource;
|
||||
SuperClassFilter(DependencyAnalyzer dependencyAnalyzer, DependencyType superType) {
|
||||
this.superType = superType;
|
||||
predicate = dependencyAnalyzer.getClassHierarchy().getSuperclassPredicate(superType.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(DependencyType type) {
|
||||
int result = cache.getOrDefault(type.index, -1);
|
||||
if (result < 0) {
|
||||
result = classSource.isSuperType(superType, type.getName()).orElse(false) ? 1 : 0;
|
||||
cache.put(type.index, result);
|
||||
if (!superType.subtypeExists) {
|
||||
return superType.index == type.index;
|
||||
}
|
||||
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 java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class Transition {
|
||||
DependencyNode source;
|
||||
DependencyNode destination;
|
||||
DependencyTypeFilter filter;
|
||||
private BitSet knownFilteredOffTypes;
|
||||
IntHashSet pendingTypes;
|
||||
byte destSubsetOfSrc;
|
||||
|
||||
|
@ -79,7 +77,7 @@ class Transition {
|
|||
}
|
||||
|
||||
boolean shouldMergeDomains() {
|
||||
if (filter != null || !isDestSubsetOfSrc()) {
|
||||
if (!source.dependencyAnalyzer.domainOptimizationEnabled() || filter != null || !isDestSubsetOfSrc()) {
|
||||
return false;
|
||||
}
|
||||
if (destination.typeSet == null) {
|
||||
|
@ -163,18 +161,7 @@ class Transition {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (knownFilteredOffTypes != null && knownFilteredOffTypes.get(type.index)) {
|
||||
return false;
|
||||
}
|
||||
if (!filter.match(type)) {
|
||||
if (knownFilteredOffTypes == null) {
|
||||
knownFilteredOffTypes = new BitSet(64);
|
||||
}
|
||||
knownFilteredOffTypes.set(type.index);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return filter.match(type);
|
||||
}
|
||||
|
||||
boolean pointsToDomainOrigin() {
|
||||
|
@ -198,7 +185,8 @@ class Transition {
|
|||
|
||||
ValueType sourceType = source.typeFilter;
|
||||
ValueType destType = destination.typeFilter;
|
||||
ClassReaderSource classSource = source.dependencyAnalyzer.getClassSource();
|
||||
return classSource.isSuperType(sourceType, destType).orElse(false);
|
||||
ClassHierarchy hierarchy = source.dependencyAnalyzer.getClassHierarchy();
|
||||
|
||||
return hierarchy.isSuperType(sourceType, destType, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class TypeSet {
|
|||
|
||||
DependencyType[] getTypes() {
|
||||
if (this.types != null) {
|
||||
DependencyType[] types = new DependencyType[this.types.cardinality()];
|
||||
DependencyType[] types = new DependencyType[typesCount];
|
||||
int j = 0;
|
||||
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
||||
DependencyType type = dependencyAnalyzer.types.get(index);
|
||||
|
@ -95,7 +95,27 @@ class TypeSet {
|
|||
int j = 0;
|
||||
DependencyType[] types;
|
||||
if (this.types != null) {
|
||||
types = new DependencyType[this.types.cardinality()];
|
||||
int[] filteredTypes = null;
|
||||
if (typesCount > 15) {
|
||||
filteredTypes = filter != null ? filter.tryExtract(this.types) : null;
|
||||
if (filteredTypes == null) {
|
||||
filteredTypes = sourceNode.getFilter().tryExtract(this.types);
|
||||
}
|
||||
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)
|
||||
|
@ -103,6 +123,7 @@ class TypeSet {
|
|||
types[j++] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this.smallTypes != null) {
|
||||
types = new DependencyType[smallTypes.length];
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
|
|
|
@ -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:
|
||||
return index;
|
||||
}
|
||||
int digitsEnd = passDigits(next);
|
||||
int digitsEnd = skipDigits(next);
|
||||
if (digitsEnd == next) {
|
||||
return index;
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ public class Problem implements Serializable {
|
|||
return next;
|
||||
}
|
||||
|
||||
private int passDigits(int index) {
|
||||
private int skipDigits(int index) {
|
||||
while (index < text.length() && Character.isDigit(text.charAt(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);
|
||||
}
|
||||
|
||||
default MethodHolder resolveMutableImplementation(String className, MethodDescriptor descriptor) {
|
||||
return (MethodHolder) resolveImplementation(className, descriptor);
|
||||
}
|
||||
|
||||
default FieldHolder resolveMutable(FieldReference field) {
|
||||
return (FieldHolder) resolve(field);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
|
||||
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<>());
|
||||
}
|
||||
|
||||
default MethodReader resolveImplementation(String className, MethodDescriptor descriptor) {
|
||||
return ClassReaderSourceHelper.resolveMethodImplementation(this, className, descriptor, new HashSet<>());
|
||||
}
|
||||
|
||||
default FieldReader resolve(FieldReference field) {
|
||||
return getAncestors(field.getClassName())
|
||||
.map(cls -> cls.getField(field.getFieldName()))
|
||||
|
@ -115,24 +119,4 @@ public interface ClassReaderSource {
|
|||
default Optional<Boolean> isSuperType(String superType, String 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 AnnotationValue annotationDefault;
|
||||
private AnnotationContainer[] parameterAnnotations;
|
||||
private MethodReference reference;
|
||||
|
||||
public MethodHolder(MethodDescriptor descriptor) {
|
||||
super(descriptor.getName());
|
||||
|
@ -80,6 +81,7 @@ public class MethodHolder extends MemberHolder implements MethodReader {
|
|||
}
|
||||
|
||||
void setOwner(ClassHolder owner) {
|
||||
reference = null;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
|
@ -90,7 +92,13 @@ public class MethodHolder extends MemberHolder implements MethodReader {
|
|||
|
||||
@Override
|
||||
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
|
||||
|
|
|
@ -140,7 +140,7 @@ public class MethodReference implements Serializable {
|
|||
@JsonValue
|
||||
public String toString() {
|
||||
if (reprCache == null) {
|
||||
reprCache = className + "." + name + signatureToString();
|
||||
reprCache = className + "." + getDescriptor().toString();
|
||||
}
|
||||
return reprCache;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
public interface ProgramCache {
|
||||
Program get(MethodReference method);
|
||||
import java.util.function.Supplier;
|
||||
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;
|
||||
|
||||
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<MethodDescriptor, MethodDescriptor> descriptorCache = new HashMap<>();
|
||||
private Map<ValueType, ValueType> valueTypeCache = new HashMap<>();
|
||||
|
@ -29,18 +29,13 @@ public class ReferenceCache {
|
|||
private Map<String, ValueType> valueTypeParseCache = new HashMap<>();
|
||||
|
||||
public MethodReference getCached(MethodReference reference) {
|
||||
MethodReference result = referenceCache.get(reference);
|
||||
if (result == null) {
|
||||
MethodDescriptor descriptor = getCached(reference.getDescriptor());
|
||||
String className = getCached(reference.getClassName());
|
||||
if (descriptor != reference.getDescriptor() || className != reference.getClassName()) {
|
||||
result = new MethodReference(className, descriptor);
|
||||
} else {
|
||||
result = reference;
|
||||
return getCached(reference.getClassName(), reference.getDescriptor());
|
||||
}
|
||||
referenceCache.put(result, result);
|
||||
}
|
||||
return result;
|
||||
|
||||
public MethodReference getCached(String className, MethodDescriptor descriptor) {
|
||||
return referenceCache
|
||||
.computeIfAbsent(className, key -> new HashMap<>())
|
||||
.computeIfAbsent(descriptor, key -> new MethodReference(className, descriptor));
|
||||
}
|
||||
|
||||
public MethodDescriptor getCached(MethodDescriptor descriptor) {
|
||||
|
@ -117,15 +112,6 @@ public class ReferenceCache {
|
|||
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) {
|
||||
MethodDescriptor result = descriptorParseCache.get(value);
|
||||
if (result == null) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.teavm.dependency.FieldDependencyInfo;
|
|||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
import org.teavm.dependency.ValueDependencyInfo;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.Instruction;
|
||||
|
@ -402,7 +403,7 @@ public class ClassInference {
|
|||
}
|
||||
|
||||
private void propagateAlongCasts() {
|
||||
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
||||
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
||||
|
||||
for (ValueCast cast : casts) {
|
||||
int fromNode = nodeMapping[packNodeAndDegree(cast.fromVariable, 0)];
|
||||
|
@ -429,7 +430,7 @@ public class ClassInference {
|
|||
type = ValueType.object(className);
|
||||
}
|
||||
|
||||
if (classSource.isSuperType(cast.targetType, type).orElse(false)) {
|
||||
if (hierarchy.isSuperType(cast.targetType, type, false)) {
|
||||
changed = true;
|
||||
nodeChanged[toNode] = true;
|
||||
targetTypes.add(cursor.value);
|
||||
|
@ -499,11 +500,11 @@ public class ClassInference {
|
|||
}
|
||||
|
||||
private void propagateException(String thrownTypeName, BasicBlock block) {
|
||||
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
||||
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
||||
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.model.emit;
|
||||
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReader;
|
||||
|
@ -52,12 +53,14 @@ public final class ProgramEmitter {
|
|||
private Program program;
|
||||
private BasicBlock block;
|
||||
ClassReaderSource classSource;
|
||||
ClassHierarchy hierarchy;
|
||||
private TextLocation currentLocation;
|
||||
|
||||
private ProgramEmitter(Program program, BasicBlock block, ClassReaderSource classSource) {
|
||||
private ProgramEmitter(Program program, BasicBlock block, ClassHierarchy hierarchy) {
|
||||
this.program = program;
|
||||
this.block = block;
|
||||
this.classSource = classSource;
|
||||
this.classSource = hierarchy.getClassSource();
|
||||
this.hierarchy = hierarchy;
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
|
@ -218,7 +221,7 @@ public final class ProgramEmitter {
|
|||
|
||||
public ValueEmitter invoke(MethodReference method, ValueEmitter... arguments) {
|
||||
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 "
|
||||
+ "not compatible with method " + method);
|
||||
}
|
||||
|
@ -387,13 +390,13 @@ public final class ProgramEmitter {
|
|||
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);
|
||||
method.setProgram(pe.getProgram());
|
||||
return pe;
|
||||
}
|
||||
|
||||
public static ProgramEmitter create(MethodDescriptor method, ClassReaderSource classSource) {
|
||||
public static ProgramEmitter create(MethodDescriptor method, ClassHierarchy classSource) {
|
||||
Program program = new Program();
|
||||
BasicBlock zeroBlock = program.createBasicBlock();
|
||||
BasicBlock block = program.createBasicBlock();
|
||||
|
@ -483,7 +486,7 @@ public final class ProgramEmitter {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package org.teavm.model.emit;
|
||||
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -424,16 +424,15 @@ public class ValueEmitter {
|
|||
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) {
|
||||
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 "
|
||||
+ "not compatible with method " + method);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pe.classSource.isSuperType(method.getClassName(), ((ValueType.Object) type).getClassName())
|
||||
.orElse(true)) {
|
||||
if (!hierarchy.isSuperType(method.getClassName(), ((ValueType.Object) type).getClassName(), true)) {
|
||||
throw new EmitException("Can't call " + method + " on non-compatible class " + type);
|
||||
}
|
||||
|
||||
|
@ -639,7 +638,7 @@ public class ValueEmitter {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -655,7 +654,7 @@ public class ValueEmitter {
|
|||
public ValueEmitter cast(ValueType type) {
|
||||
if (type.equals(this.type)) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -735,7 +734,7 @@ public class ValueEmitter {
|
|||
PrimitiveType primitiveType = ((ValueType.Primitive) this.type).getKind();
|
||||
String boxClassName = getPrimitiveClassName(primitiveType);
|
||||
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);
|
||||
}
|
||||
return result;
|
||||
|
@ -837,7 +836,7 @@ public class ValueEmitter {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return this;
|
||||
|
|
|
@ -31,8 +31,8 @@ import org.teavm.diagnostics.Diagnostics;
|
|||
import org.teavm.interop.Function;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -65,7 +65,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
|
||||
@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) {
|
||||
return;
|
||||
}
|
||||
|
@ -127,10 +127,10 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
private void processInvocation(DependencyAgent agent, CallLocation location, String functionClassName,
|
||||
String targetClassName, String methodName) {
|
||||
Diagnostics diagnostics = agent.getDiagnostics();
|
||||
ClassReaderSource classSource = agent.getClassSource();
|
||||
ClassHierarchy hierarchy = agent.getClassHierarchy();
|
||||
boolean valid = true;
|
||||
|
||||
ClassReader functionClass = classSource.get(functionClassName);
|
||||
ClassReader functionClass = hierarchy.getClassSource().get(functionClassName);
|
||||
if (functionClass == null) {
|
||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||
valid = false;
|
||||
|
@ -139,7 +139,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
valid = false;
|
||||
}
|
||||
|
||||
ClassReader targetClass = classSource.get(targetClassName);
|
||||
ClassReader targetClass = hierarchy.getClassSource().get(targetClassName);
|
||||
if (targetClass == null) {
|
||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||
valid = false;
|
||||
|
@ -168,7 +168,7 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
}
|
||||
|
||||
List<MethodReader> signatureCandidates = candidates.stream()
|
||||
.filter(method -> matchSignature(classSource, sam, method))
|
||||
.filter(method -> matchSignature(hierarchy, sam, method))
|
||||
.collect(Collectors.toList());
|
||||
if (signatureCandidates.isEmpty()) {
|
||||
if (candidates.size() == 1) {
|
||||
|
@ -181,12 +181,14 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
return;
|
||||
}
|
||||
|
||||
MethodReader resolvedMethod = findMostSpecific(diagnostics, location, classSource, signatureCandidates);
|
||||
MethodReader resolvedMethod = findMostSpecific(diagnostics, location, hierarchy, signatureCandidates);
|
||||
if (resolvedMethod != null) {
|
||||
MethodReference reference = resolvedMethod.getReference();
|
||||
resolvedMethods.put(new ExportedMethodKey(functionClassName, targetClassName, methodName), 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,
|
||||
ClassReaderSource classSource, List<MethodReader> methods) {
|
||||
ClassHierarchy hierarchy, List<MethodReader> methods) {
|
||||
MethodReader mostSpecificSoFar = methods.get(0);
|
||||
for (int i = 1; i < methods.size(); ++i) {
|
||||
MethodReader candidate = methods.get(i);
|
||||
if (matchSignature(classSource, mostSpecificSoFar, candidate)) {
|
||||
if (matchSignature(hierarchy, 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}}' "
|
||||
+ "and {{m1}}", candidate, mostSpecificSoFar);
|
||||
return null;
|
||||
|
@ -232,15 +234,14 @@ public class ExportDependencyListener extends AbstractDependencyListener {
|
|||
return mostSpecificSoFar;
|
||||
}
|
||||
|
||||
private boolean matchSignature(ClassReaderSource classSource, MethodReader functionMethod,
|
||||
private boolean matchSignature(ClassHierarchy hierarchy, MethodReader functionMethod,
|
||||
MethodReader candidateMethod) {
|
||||
if (functionMethod.parameterCount() > candidateMethod.parameterCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < functionMethod.parameterCount(); ++i) {
|
||||
if (!classSource.isSuperType(functionMethod.parameterType(i),
|
||||
candidateMethod.parameterType(i)).orElse(false)) {
|
||||
if (!hierarchy.isSuperType(functionMethod.parameterType(i), candidateMethod.parameterType(i), false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,12 @@ package org.teavm.model.optimization;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.common.OptionalPredicate;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
import org.teavm.dependency.ValueDependencyInfo;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.Instruction;
|
||||
|
@ -34,12 +36,14 @@ import org.teavm.model.instructions.InvokeInstruction;
|
|||
public class Devirtualization {
|
||||
private DependencyInfo dependency;
|
||||
private ClassReaderSource classSource;
|
||||
private ClassHierarchy hierarchy;
|
||||
private Set<MethodReference> virtualMethods = new HashSet<>();
|
||||
private Set<? extends MethodReference> readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods);
|
||||
|
||||
public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) {
|
||||
this.dependency = dependency;
|
||||
this.classSource = classSource;
|
||||
hierarchy = new ClassHierarchy(classSource);
|
||||
}
|
||||
|
||||
public void apply(MethodHolder method) {
|
||||
|
@ -72,13 +76,14 @@ public class Devirtualization {
|
|||
}
|
||||
|
||||
private Set<MethodReference> getImplementations(String[] classNames, MethodReference ref) {
|
||||
OptionalPredicate<String> isSuperclass = hierarchy.getSuperclassPredicate(ref.getClassName());
|
||||
Set<MethodReference> methods = new HashSet<>();
|
||||
for (String className : classNames) {
|
||||
if (className.startsWith("[")) {
|
||||
className = "java.lang.Object";
|
||||
}
|
||||
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;
|
||||
}
|
||||
MethodDependencyInfo methodDep = dependency.getMethodImplementation(new MethodReference(
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
*/
|
||||
package org.teavm.model.transformation;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -33,7 +32,7 @@ public class ClassPatch implements ClassHolderTransformer {
|
|||
private FieldReference platformClassField = new FieldReference("java.lang.Class", "platformClass");
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (!cls.getName().equals("java.lang.Class")) {
|
||||
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