Fast dependency analyzer, fix bugs in incremental compiler

This commit is contained in:
Alexey Andreev 2018-11-30 19:53:12 +03:00
parent eaf0f5a24e
commit d74bcbe2b9
174 changed files with 4498 additions and 1879 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View 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) {
}
}

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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