From e27d5967029f4f3da8889dd673ba516dcd9f3ac8 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sun, 16 Jun 2019 23:49:25 +0200 Subject: Plugin rework along with API rework: Enigma split from EnigmaProject; plugins now provide services configurable via a profile --- .../java/cuchaz/enigma/analysis/ClassCache.java | 127 ++++++++++++++++++++ .../java/cuchaz/enigma/analysis/ParsedJar.java | 129 --------------------- .../cuchaz/enigma/analysis/index/JarIndex.java | 22 ++-- 3 files changed, 139 insertions(+), 139 deletions(-) create mode 100644 src/main/java/cuchaz/enigma/analysis/ClassCache.java delete mode 100644 src/main/java/cuchaz/enigma/analysis/ParsedJar.java (limited to 'src/main/java/cuchaz/enigma/analysis') diff --git a/src/main/java/cuchaz/enigma/analysis/ClassCache.java b/src/main/java/cuchaz/enigma/analysis/ClassCache.java new file mode 100644 index 0000000..0bd78b3 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/ClassCache.java @@ -0,0 +1,127 @@ +package cuchaz.enigma.analysis; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableSet; +import cuchaz.enigma.CompiledSource; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public final class ClassCache implements AutoCloseable, CompiledSource { + private final FileSystem fileSystem; + private final ImmutableSet classNames; + + private final Cache nodeCache = CacheBuilder.newBuilder() + .maximumSize(128) + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(); + + private ClassCache(FileSystem fileSystem, ImmutableSet classNames) { + this.fileSystem = fileSystem; + this.classNames = classNames; + } + + public static ClassCache of(Path jarPath) throws IOException { + FileSystem fileSystem = FileSystems.newFileSystem(jarPath, null); + ImmutableSet classNames = collectClassNames(fileSystem); + + return new ClassCache(fileSystem, classNames); + } + + private static ImmutableSet collectClassNames(FileSystem fileSystem) throws IOException { + ImmutableSet.Builder classNames = ImmutableSet.builder(); + for (Path root : fileSystem.getRootDirectories()) { + Files.walk(root).map(Path::toString) + .forEach(path -> { + if (path.endsWith(".class")) { + String name = path.substring(1, path.length() - ".class".length()); + classNames.add(name); + } + }); + } + + return classNames.build(); + } + + @Nullable + @Override + public ClassNode getClassNode(String name) { + if (!classNames.contains(name)) { + return null; + } + + try { + return nodeCache.get(name, () -> parseNode(name)); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + private ClassNode parseNode(String name) throws IOException { + ClassReader reader = getReader(name); + + ClassNode node = new ClassNode(); + + LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Opcodes.ASM5, node); + reader.accept(visitor, 0); + + return node; + } + + private ClassReader getReader(String name) throws IOException { + Path path = fileSystem.getPath(name + ".class"); + + byte[] bytes = Files.readAllBytes(path); + return new ClassReader(bytes); + } + + public int getClassCount() { + return classNames.size(); + } + + public void visit(Supplier visitorSupplier, int readFlags) { + for (String className : classNames) { + ClassVisitor visitor = visitorSupplier.get(); + + ClassNode cached = nodeCache.getIfPresent(className); + if (cached != null) { + cached.accept(visitor); + continue; + } + + try { + ClassReader reader = getReader(className); + reader.accept(visitor, readFlags); + } catch (IOException e) { + System.out.println("Failed to visit class " + className); + e.printStackTrace(); + } + } + } + + @Override + public void close() throws IOException { + this.fileSystem.close(); + } + + public JarIndex index(ProgressListener progress) { + JarIndex index = JarIndex.empty(); + index.indexJar(this, progress); + return index; + } +} diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java deleted file mode 100644 index ddcda3e..0000000 --- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.analysis; - -import com.google.common.io.ByteStreams; -import cuchaz.enigma.CompiledSource; -import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; - -import javax.annotation.Nullable; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarInputStream; - -public class ParsedJar implements CompiledSource { - private final Map classBytes; - private final Map nodeCache = new HashMap<>(); - - public ParsedJar(JarFile jar) throws IOException { - Map uClassBytes = new LinkedHashMap<>(); - try { - // get the jar entries that correspond to classes - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - // is this a class file? - if (entry.getName().endsWith(".class")) { - try (InputStream input = new BufferedInputStream(jar.getInputStream(entry))) { - String path = entry.getName().substring(0, entry.getName().length() - ".class".length()); - uClassBytes.put(path, ByteStreams.toByteArray(input)); - } - } - } - } finally { - jar.close(); - classBytes = Collections.unmodifiableMap(uClassBytes); - } - } - - public ParsedJar(JarInputStream jar) throws IOException { - Map uClassBytes = new LinkedHashMap<>(); - try { - // get the jar entries that correspond to classes - JarEntry entry; - while ((entry = jar.getNextJarEntry()) != null) { - // is this a class file? - if (entry.getName().endsWith(".class")) { - String path = entry.getName().substring(0, entry.getName().length() - ".class".length()); - uClassBytes.put(path, ByteStreams.toByteArray(jar)); - jar.closeEntry(); - } - } - } finally { - jar.close(); - classBytes = Collections.unmodifiableMap(uClassBytes); - } - } - - public void visitReader(Function visitorFunction, int options) { - for (String s : classBytes.keySet()) { - ClassNode nodeCached = nodeCache.get(s); - if (nodeCached != null) { - nodeCached.accept(visitorFunction.apply(s)); - } else { - new ClassReader(classBytes.get(s)).accept(visitorFunction.apply(s), options); - } - } - } - - public void visitNode(Consumer consumer) { - for (String s : classBytes.keySet()) { - consumer.accept(getClassNode(s)); - } - } - - public int getClassCount() { - return classBytes.size(); - } - - @Nullable - @Override - public ClassNode getClassNode(String name) { - return nodeCache.computeIfAbsent(name, (n) -> { - byte[] bytes = classBytes.get(name); - if (bytes == null) { - return null; - } - - ClassReader reader = new ClassReader(bytes); - ClassNode node = new ClassNode(); - - LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Opcodes.ASM5, node); - reader.accept(visitor, 0); - - return node; - }); - } - - public List getClassEntries() { - List entries = new ArrayList<>(classBytes.size()); - for (String s : classBytes.keySet()) { - entries.add(new ClassEntry(s)); - } - return entries; - } - - public Map getClassDataMap() { - return classBytes; - } -} diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index fd4e618..300425b 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java @@ -13,7 +13,8 @@ package cuchaz.enigma.analysis.index; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import cuchaz.enigma.analysis.ParsedJar; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.analysis.ClassCache; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.IndexEntryResolver; import cuchaz.enigma.translation.representation.Lambda; @@ -23,7 +24,6 @@ import org.objectweb.asm.Opcodes; import java.util.Arrays; import java.util.Collection; -import java.util.function.Consumer; public class JarIndex implements JarIndexer { private final EntryIndex entryIndex; @@ -56,23 +56,25 @@ public class JarIndex implements JarIndexer { return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); } - public void indexJar(ParsedJar jar, Consumer progress) { - progress.accept("Indexing entries (1/4)"); - jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); + public void indexJar(ClassCache classCache, ProgressListener progress) { + progress.init(4, "Indexing jar"); - progress.accept("Indexing entry references (2/4)"); - jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); + progress.step(1, "Entries"); + classCache.visit(() -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); - progress.accept("Finding bridge methods (3/4)"); + progress.step(2, "Entry references"); + classCache.visit(() -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES); + + progress.step(3, "Bridge methods"); bridgeMethodIndex.findBridgeMethods(); - progress.accept("Processing index (4/4)"); + progress.step(4, "Processing"); processIndex(this); } @Override public void processIndex(JarIndex index) { - indexers.forEach(indexer -> indexer.processIndex(index)); + indexers.parallelStream().forEach(indexer -> indexer.processIndex(index)); } @Override -- cgit v1.2.3