Make bytecode parser to cache method references to reduce memory consumption

This commit is contained in:
Alexey Andreev 2016-07-28 23:51:39 +03:00
parent a3f60996e2
commit 6b6c968aea
10 changed files with 190 additions and 96 deletions

View File

@ -48,6 +48,7 @@ import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ReferenceCache;
import org.teavm.model.ValueType;
import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils;
@ -156,7 +157,7 @@ public class DependencyChecker implements DependencyInfo {
ClassNode node = new ClassNode();
org.objectweb.asm.ClassReader reader = new org.objectweb.asm.ClassReader(data);
reader.accept(node, 0);
submitClass(Parser.parseClass(node));
submitClass(new Parser(new ReferenceCache()).parseClass(node));
return node.name;
}

View File

@ -364,8 +364,7 @@ class DependencyGraphBuilder {
return;
}
MethodReference methodRef = new MethodReference(className, methodDesc);
final MethodDependency methodDep = checker.linkMethod(methodRef,
new CallLocation(caller.getMethod(), location));
MethodDependency methodDep = checker.linkMethod(methodRef, new CallLocation(caller.getMethod(), location));
if (!methodDep.isMissing() && knownMethods.add(methodRef)) {
methodDep.use();
DependencyNode[] targetParams = methodDep.getVariables();

View File

@ -29,10 +29,6 @@ import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
/**
*
* @author Alexey Andreev
*/
public class Linker {
public void link(DependencyInfo dependency, ClassHolder cls) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {

View File

@ -17,10 +17,6 @@ package org.teavm.model;
import org.teavm.model.instructions.InstructionVisitor;
/**
*
* @author Alexey Andreev
*/
public abstract class Instruction {
private BasicBlock basicBlock;
private InstructionLocation location;

View File

@ -17,10 +17,6 @@ package org.teavm.model;
import java.util.Arrays;
/**
*
* @author Alexey Andreev
*/
public class MethodDescriptor {
private String name;
private ValueType[] signature;

View File

@ -0,0 +1,146 @@
/*
* 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.model;
import java.util.HashMap;
import java.util.Map;
public class ReferenceCache {
private Map<MethodReference, 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<>();
private Map<String, String> classCache = new HashMap<>();
private Map<String, MethodReference> referenceParseCache = new HashMap<>();
private Map<String, MethodDescriptor> descriptorParseCache = new HashMap<>();
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;
}
referenceCache.put(result, result);
}
return result;
}
public MethodDescriptor getCached(MethodDescriptor descriptor) {
MethodDescriptor result = descriptorCache.get(descriptor);
if (result == null) {
result = descriptor;
ValueType[] signature = descriptor.getSignature();
boolean signatureChanged = false;
for (int i = 0; i < signature.length; ++i) {
ValueType type = signature[i];
if (type == null) {
continue;
}
ValueType cachedType = getCached(type);
if (type != cachedType) {
signatureChanged = true;
signature[i] = cachedType;
}
}
if (signatureChanged) {
result = new MethodDescriptor(descriptor.getName(), signature);
}
descriptorCache.put(result, result);
}
return result;
}
public FieldReference getCached(FieldReference reference) {
FieldReference result = fieldRefenceCache.get(reference);
if (result == null) {
result = reference;
String classNameCached = getCached(reference.getClassName());
String fieldNameCached = getCached(reference.getFieldName());
if (classNameCached != reference.getClassName() || fieldNameCached != reference.getFieldName()) {
result = new FieldReference(classNameCached, fieldNameCached);
}
fieldRefenceCache.put(result, result);
}
return result;
}
public ValueType getCached(ValueType valueType) {
if (valueType instanceof ValueType.Primitive) {
return valueType;
}
ValueType result = valueTypeCache.get(valueType);
if (result == null) {
result = valueType;
if (result instanceof ValueType.Object) {
String className = ((ValueType.Object) result).getClassName();
String cachedClassName = getCached(className);
if (cachedClassName != className) {
result = ValueType.object(cachedClassName);
}
} else if (result instanceof ValueType.Array) {
ValueType item = ((ValueType.Array) result).getItemType();
ValueType cachedItem = getCached(item);
if (item != cachedItem) {
result = ValueType.arrayOf(cachedItem);
}
}
valueTypeCache.put(result, result);
}
return result;
}
public String getCached(String className) {
String result = classCache.get(className);
if (result == null) {
result = className;
classCache.put(result, result);
}
return result;
}
public MethodReference parseReferenceCached(String value) {
MethodReference result = referenceParseCache.get(value);
if (result == null) {
result = getCached(MethodReference.parse(value));
referenceParseCache.put(value, result);
}
return result;
}
public MethodDescriptor parseDescriptorCached(String value) {
MethodDescriptor result = descriptorParseCache.get(value);
if (result == null) {
result = getCached(MethodDescriptor.parse(value));
descriptorParseCache.put(value, result);
}
return result;
}
public ValueType parseValueTypeCached(String value) {
ValueType result = valueTypeParseCache.get(value);
if (result == null) {
result = getCached(ValueType.parse(value));
valueTypeParseCache.put(value, result);
}
return result;
}
}

View File

@ -35,11 +35,14 @@ import org.teavm.model.util.PhiUpdater;
import org.teavm.model.util.ProgramUtils;
import org.teavm.optimization.UnreachableBasicBlockEliminator;
public final class Parser {
private Parser() {
public class Parser {
private ReferenceCache referenceCache;
public Parser(ReferenceCache referenceCache) {
this.referenceCache = referenceCache;
}
public static MethodHolder parseMethod(MethodNode node, String className, String fileName) {
public MethodHolder parseMethod(MethodNode node, String className, String fileName) {
MethodNode nodeWithoutJsr = new MethodNode(Opcodes.ASM5, node.access, node.name, node.desc, node.signature,
node.exceptions.toArray(new String[0]));
JSRInlinerAdapter adapter = new JSRInlinerAdapter(nodeWithoutJsr, node.access, node.name, node.desc,
@ -50,7 +53,7 @@ public final class Parser {
MethodHolder method = new MethodHolder(node.name, signature);
parseModifiers(node.access, method);
ProgramParser programParser = new ProgramParser();
ProgramParser programParser = new ProgramParser(referenceCache);
programParser.setFileName(fileName);
Program program = programParser.parse(node, className);
new UnreachableBasicBlockEliminator().optimize(program);
@ -75,7 +78,7 @@ public final class Parser {
return method;
}
private static void applyDebugNames(Program program, PhiUpdater phiUpdater, ProgramParser parser,
private void applyDebugNames(Program program, PhiUpdater phiUpdater, ProgramParser parser,
Variable[] argumentMapping) {
if (program.basicBlockCount() == 0) {
return;
@ -110,7 +113,7 @@ public final class Parser {
}
}
private static IntIntMap[] getBlockEntryVariableMappings(Program program, PhiUpdater phiUpdater,
private IntIntMap[] getBlockEntryVariableMappings(Program program, PhiUpdater phiUpdater,
Variable[] argumentMapping) {
class Step {
int node;
@ -179,7 +182,7 @@ public final class Parser {
return result;
}
private static Variable[] applySignature(Program program, ValueType[] arguments) {
private Variable[] applySignature(Program program, ValueType[] arguments) {
if (program.variableCount() == 0) {
return new Variable[0];
}
@ -204,7 +207,7 @@ public final class Parser {
return Arrays.copyOf(variableMap, index);
}
public static ClassHolder parseClass(ClassNode node) {
public ClassHolder parseClass(ClassNode node) {
ClassHolder cls = new ClassHolder(node.name.replace('/', '.'));
parseModifiers(node.access, cls);
if (node.superName != null) {
@ -238,7 +241,7 @@ public final class Parser {
return cls;
}
public static FieldHolder parseField(FieldNode node) {
public FieldHolder parseField(FieldNode node) {
FieldHolder field = new FieldHolder(node.name);
field.setType(ValueType.parse(node.desc));
field.setInitialValue(node.value);
@ -247,7 +250,7 @@ public final class Parser {
return field;
}
public static void parseModifiers(int access, ElementHolder member) {
public void parseModifiers(int access, ElementHolder member) {
if ((access & Opcodes.ACC_PRIVATE) != 0) {
member.setLevel(AccessLevel.PRIVATE);
} else if ((access & Opcodes.ACC_PROTECTED) != 0) {
@ -306,7 +309,7 @@ public final class Parser {
}
}
private static void parseAnnotations(AnnotationContainer annotations, List<AnnotationNode> visibleAnnotations,
private void parseAnnotations(AnnotationContainer annotations, List<AnnotationNode> visibleAnnotations,
List<AnnotationNode> invisibleAnnotations) {
List<Object> annotNodes = new ArrayList<>();
if (visibleAnnotations != null) {
@ -328,7 +331,7 @@ public final class Parser {
}
}
private static void parseAnnotationValues(AnnotationHolder annot, List<Object> values) {
private void parseAnnotationValues(AnnotationHolder annot, List<Object> values) {
if (values == null) {
return;
}
@ -339,7 +342,7 @@ public final class Parser {
}
}
private static AnnotationValue parseAnnotationValue(Object value) {
private AnnotationValue parseAnnotationValue(Object value) {
if (value instanceof String[]) {
String[] enumInfo = (String[]) value;
ValueType.Object object = (ValueType.Object) ValueType.parse(enumInfo[0]);

View File

@ -24,6 +24,7 @@ import org.teavm.model.util.InstructionTransitionExtractor;
import org.teavm.model.util.ProgramUtils;
public class ProgramParser {
private ReferenceCache referenceCache;
private static final byte ROOT = 0;
private static final byte SINGLE = 1;
private static final byte DOUBLE_FIRST_HALF = 2;
@ -45,6 +46,10 @@ public class ProgramParser {
private Map<Integer, List<LocalVariableNode>> localVariableMap = new HashMap<>();
private Map<Instruction, Map<Integer, String>> variableDebugNames = new HashMap<>();
public ProgramParser(ReferenceCache methodReferenceCache) {
this.referenceCache = methodReferenceCache;
}
private static class Step {
public final int source;
public final int target;
@ -389,9 +394,9 @@ public class ProgramParser {
private ValueType parseType(String type) {
if (type.startsWith("[")) {
return ValueType.parse(type);
return referenceCache.parseValueTypeCached(type);
} else {
return ValueType.object(type.replace('/', '.'));
return referenceCache.getCached(ValueType.object(type.replace('/', '.')));
}
}
@ -507,7 +512,8 @@ public class ProgramParser {
insn.setReceiver(getVariable(returnType.getSize() == 2 ? pushDouble() : pushSingle()));
}
insn.setMethod(new MethodDescriptor(name, MethodDescriptor.parseSignature(desc)));
insn.setMethod(referenceCache.getCached(
new MethodDescriptor(name, MethodDescriptor.parseSignature(desc))));
for (Object bsmArg : bsmArgs) {
insn.getBootstrapArguments().add(convertConstant(bsmArg));
}
@ -531,7 +537,7 @@ public class ProgramParser {
if (type.getSort() == Type.METHOD) {
return new RuntimeConstant(MethodDescriptor.parseSignature(type.getDescriptor()));
} else {
return new RuntimeConstant(ValueType.parse(type.getDescriptor()));
return new RuntimeConstant(referenceCache.parseValueTypeCached(type.getDescriptor()));
}
} else if (value instanceof Handle) {
return new RuntimeConstant(parseHandle((Handle) value));
@ -566,7 +572,8 @@ public class ProgramParser {
for (int i = types.length - 1; i >= 0; --i) {
args[--j] = types[i].getSize() == 2 ? getVariable(popDouble()) : getVariable(popSingle());
}
MethodDescriptor method = new MethodDescriptor(name, MethodDescriptor.parseSignature(desc));
MethodDescriptor method = referenceCache.getCached(
new MethodDescriptor(name, MethodDescriptor.parseSignature(desc)));
int instance = -1;
if (opcode != Opcodes.INVOKESTATIC) {
instance = popSingle();
@ -579,7 +586,7 @@ public class ProgramParser {
if (instance == -1) {
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(ownerCls, method));
insn.setMethod(referenceCache.getCached(new MethodReference(ownerCls, method)));
if (result >= 0) {
insn.setReceiver(getVariable(result));
}
@ -592,7 +599,7 @@ public class ProgramParser {
} else {
insn.setType(InvocationType.VIRTUAL);
}
insn.setMethod(new MethodReference(ownerCls, method));
insn.setMethod(referenceCache.getCached(new MethodReference(ownerCls, method)));
if (result >= 0) {
insn.setReceiver(getVariable(result));
}
@ -656,7 +663,7 @@ public class ProgramParser {
addInstruction(insn);
} else if (cst instanceof Type) {
ClassConstantInstruction insn = new ClassConstantInstruction();
insn.setConstant(ValueType.parse(((Type) cst).getDescriptor()));
insn.setConstant(referenceCache.getCached(ValueType.parse(((Type) cst).getDescriptor())));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
} else {
@ -1652,11 +1659,11 @@ public class ProgramParser {
switch (opcode) {
case Opcodes.GETFIELD: {
int instance = popSingle();
ValueType type = ValueType.parse(desc);
ValueType type = referenceCache.parseValueTypeCached(desc);
int value = desc.equals("D") || desc.equals("J") ? pushDouble() : pushSingle();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setInstance(getVariable(instance));
insn.setField(new FieldReference(ownerCls, name));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
addInstruction(insn);
@ -1667,17 +1674,17 @@ public class ProgramParser {
int instance = popSingle();
PutFieldInstruction insn = new PutFieldInstruction();
insn.setInstance(getVariable(instance));
insn.setField(new FieldReference(ownerCls, name));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setValue(getVariable(value));
insn.setFieldType(ValueType.parse(desc));
insn.setFieldType(referenceCache.parseValueTypeCached(desc));
addInstruction(insn);
break;
}
case Opcodes.GETSTATIC: {
ValueType type = ValueType.parse(desc);
ValueType type = referenceCache.parseValueTypeCached(desc);
int value = desc.equals("D") || desc.equals("J") ? pushDouble() : pushSingle();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setField(new FieldReference(ownerCls, name));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
addInstruction(insn);
@ -1691,7 +1698,7 @@ public class ProgramParser {
}
int value = desc.equals("D") || desc.equals("J") ? popDouble() : popSingle();
PutFieldInstruction insn = new PutFieldInstruction();
insn.setField(new FieldReference(ownerCls, name));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setValue(getVariable(value));
addInstruction(insn);
break;

View File

@ -21,13 +21,11 @@ import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.teavm.common.Mapper;
import org.teavm.model.ClassHolder;
import org.teavm.model.ReferenceCache;
import org.teavm.parsing.Parser;
/**
*
* @author Alexey Andreev
*/
public class ResourceClassHolderMapper implements Mapper<String, ClassHolder> {
private Parser parser = new Parser(new ReferenceCache());
private ResourceReader resourceReader;
public ResourceClassHolderMapper(ResourceReader resourceReader) {
@ -47,6 +45,6 @@ public class ResourceClassHolderMapper implements Mapper<String, ClassHolder> {
} catch (IOException e) {
throw new RuntimeException(e);
}
return Parser.parseClass(clsNode);
return parser.parseClass(clsNode);
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2013 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.resource;
import java.io.IOException;
import java.io.InputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.teavm.common.Mapper;
import org.teavm.model.ClassHolder;
import org.teavm.parsing.Parser;
/**
*
* @author Alexey Andreev
*/
public class ResourceParser implements Mapper<String, ClassHolder> {
private ResourceReader resourceReader;
public ResourceParser(ResourceReader resourceReader) {
this.resourceReader = resourceReader;
}
@Override
public ClassHolder map(String name) {
ClassNode clsNode = new ClassNode();
try (InputStream input = resourceReader.openResource(name.replace('.', '/') + ".class")) {
ClassReader reader = new ClassReader(input);
reader.accept(clsNode, 0);
} catch (IOException e) {
throw new RuntimeException(e);
}
return Parser.parseClass(clsNode);
}
}