mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-12-09 02:00:25 +08:00
Make bytecode parser to cache method references to reduce memory consumption
This commit is contained in:
parent
a3f60996e2
commit
6b6c968aea
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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])) {
|
||||
|
@ -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;
|
||||
|
@ -17,10 +17,6 @@ package org.teavm.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class MethodDescriptor {
|
||||
private String name;
|
||||
private ValueType[] signature;
|
||||
|
146
core/src/main/java/org/teavm/model/ReferenceCache.java
Normal file
146
core/src/main/java/org/teavm/model/ReferenceCache.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user