From fed889cb9e5456ec881a0eb70cd5e11f9e3ea00f Mon Sep 17 00:00:00 2001 From: Glavo Date: Wed, 17 Jan 2024 11:30:49 +0800 Subject: [PATCH] Reimplement ModuleInfoReader.readModuleName --- .../glavo/japp/packer/ModuleInfoReader.java | 19 ++++++---- .../compressor/classfile/ClassFileReader.java | 23 ++++++++++- .../japp/packer/ModuleInfoReaderTest.java | 38 ++++++++++++++++++- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/glavo/japp/packer/ModuleInfoReader.java b/src/main/java/org/glavo/japp/packer/ModuleInfoReader.java index 2ac3155..785d0c1 100644 --- a/src/main/java/org/glavo/japp/packer/ModuleInfoReader.java +++ b/src/main/java/org/glavo/japp/packer/ModuleInfoReader.java @@ -15,9 +15,12 @@ */ package org.glavo.japp.packer; +import org.glavo.japp.packer.compressor.classfile.ClassFileReader; + import java.io.IOException; import java.io.InputStream; import java.lang.module.ModuleDescriptor; +import java.nio.ByteBuffer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,11 +35,10 @@ public static String deriveAutomaticModuleName(String jarFileName) { throw new IllegalArgumentException(jarFileName); } - int end = jarFileName.length() - ".jar".length(); - - int start; + final int end = jarFileName.length() - ".jar".length(); - for (start = 0; start < end; start++) { + int start = 0; + for (; start < end; start++) { if (jarFileName.charAt(start) != '.') { break; } @@ -46,7 +48,7 @@ public static String deriveAutomaticModuleName(String jarFileName) { throw new IllegalArgumentException(jarFileName); } - String name = jarFileName.substring(start, jarFileName.length() - ".jar".length()); + String name = jarFileName.substring(start, end); // find first occurrence of -${NUMBER}. or -${NUMBER}$ Matcher matcher = DASH_VERSION.matcher(name); @@ -67,7 +69,10 @@ public static String deriveAutomaticModuleName(String jarFileName) { } public static String readModuleName(InputStream moduleInfo) throws IOException { - // TODO: Support Java 8 - return ModuleDescriptor.read(moduleInfo).name(); + String moduleName = new ClassFileReader(ByteBuffer.wrap(moduleInfo.readAllBytes())).getModuleName(); + if (moduleName == null) { + throw new IOException(); + } + return moduleName; } } diff --git a/src/main/java/org/glavo/japp/packer/compressor/classfile/ClassFileReader.java b/src/main/java/org/glavo/japp/packer/compressor/classfile/ClassFileReader.java index 6365406..a9010e5 100644 --- a/src/main/java/org/glavo/japp/packer/compressor/classfile/ClassFileReader.java +++ b/src/main/java/org/glavo/japp/packer/compressor/classfile/ClassFileReader.java @@ -20,10 +20,11 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import static org.glavo.japp.classfile.ClassFile.*; -final class ClassFileReader { +public final class ClassFileReader { private final ByteBuffer buffer; final byte[] tags; final int[] positions; @@ -39,7 +40,11 @@ final class ClassFileReader { final int thisClass; final int superClass; - ClassFileReader(ByteBuffer buffer) throws IOException { + private String moduleName; + + public ClassFileReader(ByteBuffer buffer) throws IOException { + assert buffer.order() == ByteOrder.BIG_ENDIAN; + this.buffer = buffer; int magic = buffer.getInt(); @@ -280,6 +285,16 @@ private void scanAttributes() throws IOException { } break; } + case "Module": { + int moduleNameIndex = readU2(); + skip(attributeLength - 2); + + if (tags[moduleNameIndex] == CONSTANT_Module) { + int stringIndex = Short.toUnsignedInt(buffer.getShort(positions[moduleNameIndex])); + moduleName = getString(stringIndex); + } + break; + } default: skip(attributeLength); } @@ -356,4 +371,8 @@ private void scanElementValue() throws IOException { throw new IOException(String.format("Unknown element value: 0x%02x", tag)); } } + + public String getModuleName() { + return moduleName; + } } diff --git a/src/test/java/org/glavo/japp/packer/ModuleInfoReaderTest.java b/src/test/java/org/glavo/japp/packer/ModuleInfoReaderTest.java index a405cd5..9d409ea 100644 --- a/src/test/java/org/glavo/japp/packer/ModuleInfoReaderTest.java +++ b/src/test/java/org/glavo/japp/packer/ModuleInfoReaderTest.java @@ -15,12 +15,23 @@ */ package org.glavo.japp.packer; +import com.github.luben.zstd.Zstd; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.stream.Stream; +import java.util.zip.ZipFile; import static org.junit.jupiter.api.Assertions.assertEquals; public final class ModuleInfoReaderTest { - @Test public void deriveAutomaticModuleNameTest() { assertEquals("a", ModuleInfoReader.deriveAutomaticModuleName("a.jar")); @@ -30,4 +41,29 @@ public void deriveAutomaticModuleNameTest() { assertEquals("a.b", ModuleInfoReader.deriveAutomaticModuleName("a-b-0.1.0.jar")); assertEquals("a.b", ModuleInfoReader.deriveAutomaticModuleName("a--b-0.1.0.jar")); } + + static Stream readModuleNameTestArguments() { + return Map.of( + "org.junit.jupiter.api", Test.class, + "com.github.luben.zstd_jni", Zstd.class + ).entrySet().stream().map(entry -> { + + byte[] moduleInfo; + try (ZipFile zipFile = new ZipFile(new File(entry.getValue().getProtectionDomain().getCodeSource().getLocation().toURI()))) { + try (InputStream inputStream = zipFile.getInputStream(zipFile.getEntry("module-info.class"))) { + moduleInfo = inputStream.readAllBytes(); + } + } catch (Exception e) { + throw new AssertionError(e); + } + + return Arguments.of(entry.getKey(), moduleInfo); + }); + } + + @ParameterizedTest + @MethodSource("readModuleNameTestArguments") + public void readModuleNameTest(String moduleName, byte[] moduleInfo) throws IOException { + assertEquals(moduleName, ModuleInfoReader.readModuleName(new ByteArrayInputStream(moduleInfo))); + } }