Skip to content

Commit

Permalink
Merge branch 'jdk22' into dev4
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Apr 27, 2024
2 parents 3dc0f9a + 4a6cc16 commit c9fa994
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 116 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
java-version: [ 17 ]
java-version: [ 22 ]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand All @@ -33,7 +33,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
java-version: 22
check-latest: true
# The project version extract NEEDS to have the gradle wrapper already downloaded.
# So we have a dummy step here just to initialize it.
Expand Down Expand Up @@ -107,7 +107,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
java-version: 22
# The project version extract NEEDS to have the gradle wrapper already downloaded.
# So we have a dummy step here just to initialize it.
- name: Download Gradle wrapper
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ subprojects {
// gradlew -q javaToolchains - see the list of detected toolchains.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(22)
}
}

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jlinker = "1.0.7"
jphantom = "1.4.4"
junit = "5.10.2"
jsvg = "1.4.0"
llzip = "2.3.0"
llzip = "2.5.0"
logback-classic = { strictly = "1.4.11" } # newer releases break in jar releases
mapping-io = "0.5.1"
mockito = "5.11.0"
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https://services.gradle.org/distributions-snapshots/gradle-8.9-20240426001649+0000-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package software.coley.recaf.info.properties.builtin;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import software.coley.recaf.info.Info;
import software.coley.recaf.info.properties.BasicProperty;
import software.coley.recaf.info.properties.Property;

/**
* Built in property to track data appearing before the ZIP header in an archive.
*
* @author Matt Coley
*/
public class ZipPrefixDataProperty extends BasicProperty<byte[]> {
public static final String KEY = "zip-prefix-data";

/**
* @param data
* Optional data.
*/
public ZipPrefixDataProperty(@Nullable byte[] data) {
super(KEY, data);
}

/**
* @param info
* Info instance.
*
* @return Optional data.
* {@code null} when no property value is assigned.
*/
@Nullable
public static byte[] get(@Nonnull Info info) {
Property<byte[]> property = info.getProperty(KEY);
if (property != null) {
return property.value();
}
return null;
}

/**
* @param info
* Info instance.
* @param value
* Optional data.
*/
public static void set(@Nonnull Info info, @Nonnull byte[] value) {
info.setProperty(new ZipPrefixDataProperty(value));
}

/**
* @param info
* Info instance.
*/
public static void remove(@Nonnull Info info) {
info.removeProperty(KEY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import software.coley.lljzip.format.model.CentralDirectoryFileHeader;
import software.coley.lljzip.format.model.ZipArchive;
import software.coley.lljzip.util.ExtraFieldTime;
import software.coley.lljzip.util.MemorySegmentUtil;
import software.coley.recaf.analytics.logging.Logging;
import software.coley.recaf.info.*;
import software.coley.recaf.info.builder.FileInfoBuilder;
Expand All @@ -21,6 +22,7 @@

import java.io.File;
import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
Expand Down Expand Up @@ -125,9 +127,23 @@ private WorkspaceFileResource handleZip(WorkspaceFileResourceBuilder builder, Zi
NavigableMap<Integer, JvmClassBundle> versionedJvmClassBundles = new TreeMap<>();
Map<String, WorkspaceFileResource> embeddedResources = new HashMap<>();

// Read ZIP entries
// Read ZIP
boolean isAndroid = zipInfo.getName().toLowerCase().endsWith(".apk");
ZipArchive archive = config.mapping().apply(source.readAll());

// Sanity check, if there's data at the head of the file AND its otherwise empty its probably junk.
MemorySegment prefixData = archive.getPrefixData();
if (prefixData != null && archive.getEnd() != null && archive.getParts().size() == 1) {
// We'll throw as the caller should catch this case and handle it based on their needs.
throw new IOException("Content matched ZIP header but had no file entries");
}

// Record prefix data to attribute held by the zip file info.
if (prefixData != null) {
ZipPrefixDataProperty.set(zipInfo, MemorySegmentUtil.toByteArray(prefixData));
}

// Build model from the contained files in the ZIP
archive.getLocalFiles().forEach(header -> {
LocalFileHeaderSource headerSource = new LocalFileHeaderSource(header, isAndroid);
String entryName = header.getFileNameAsString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import jakarta.annotation.Nonnull;
import software.coley.recaf.info.*;
import software.coley.recaf.info.properties.builtin.*;
import software.coley.recaf.services.workspace.WorkspaceManager;
import software.coley.recaf.util.Unchecked;
import software.coley.recaf.util.ZipCreationUtils;
import software.coley.recaf.services.workspace.WorkspaceManager;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.AndroidClassBundle;
import software.coley.recaf.workspace.model.bundle.JvmClassBundle;
Expand All @@ -18,6 +18,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
Expand Down Expand Up @@ -136,6 +137,7 @@ private class WorkspaceExporterImpl implements WorkspaceExporter {
private final Map<String, Long> modifyTimes = new HashMap<>();
private final Map<String, Long> createTimes = new HashMap<>();
private final Map<String, Long> accessTimes = new HashMap<>();
private byte[] prefix;

@Override
public void export(@Nonnull Workspace workspace) throws IOException {
Expand Down Expand Up @@ -163,7 +165,12 @@ public void export(@Nonnull Workspace workspace) throws IOException {
});

// Write buffer to path
Files.write(path, zipBuilder.bytes());
if (prefix != null) {
Files.write(path, prefix);
Files.write(path, zipBuilder.bytes(), StandardOpenOption.APPEND);
} else {
Files.write(path, zipBuilder.bytes());
}
break;
case DIRECTORY:
for (Map.Entry<String, byte[]> entry : contents.entrySet()) {
Expand All @@ -186,12 +193,19 @@ public void export(@Nonnull Workspace workspace) throws IOException {
* Workspace to pull data from.
*/
private void populate(@Nonnull Workspace workspace) {
// If shading libs, they go first so the primary content will be the authoritative copy for
// any duplicate paths held by both resources.
if (bundleSupporting) {
for (WorkspaceResource supportingResource : workspace.getSupportingResources()) {
mapInto(contents, supportingResource);
}
}
mapInto(contents, workspace.getPrimaryResource());
WorkspaceResource primary = workspace.getPrimaryResource();
mapInto(contents, primary);

// If the resource had prefix data, get it here so that we can write it back later.
if (primary instanceof WorkspaceFileResource resource)
prefix = ZipPrefixDataProperty.get(resource.getFileInfo());
}

/**
Expand Down
27 changes: 20 additions & 7 deletions recaf-core/src/main/java/software/coley/recaf/util/IOUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import software.coley.recaf.util.threading.ThreadLocals;

import java.io.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;

Expand Down Expand Up @@ -126,7 +139,7 @@ public static byte[] toByteArray(InputStream in, byte[] buf) throws IOException
*/
@Nonnull
public static byte[] toByteArray(InputStream in) throws IOException {
return toByteArray(in, ThreadLocals.getByteBuffer());
return toByteArray(in, newByteBuffer());
}

/**
Expand Down Expand Up @@ -445,7 +458,7 @@ public static void copy(InputStream in, OutputStream out, byte[] buf) throws IOE
* @see IOUtil#newByteBuffer()
*/
public static void copy(InputStream in, OutputStream out) throws IOException {
copy(in, out, ThreadLocals.getByteBuffer());
copy(in, out, newByteBuffer());
}

/**
Expand Down Expand Up @@ -616,7 +629,7 @@ public static void copy(URL url, OutputStream out,
int connectionTimeoutMillis,
int readTimeoutMillis)
throws IOException {
copy(url, out, ThreadLocals.getByteBuffer(), connectionTimeoutMillis, readTimeoutMillis);
copy(url, out, newByteBuffer(), connectionTimeoutMillis, readTimeoutMillis);
}

/**
Expand Down Expand Up @@ -669,7 +682,7 @@ public static void copy(URL url, Path path,
int connectionTimeoutMillis,
int readTimeoutMillis)
throws IOException {
copy(url, path, ThreadLocals.getByteBuffer(), connectionTimeoutMillis, readTimeoutMillis);
copy(url, path, newByteBuffer(), connectionTimeoutMillis, readTimeoutMillis);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package software.coley.recaf.util.io;

import software.coley.lljzip.util.ByteData;
import software.coley.recaf.util.ReflectUtil;

import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.function.Consumer;
Expand Down Expand Up @@ -101,7 +101,7 @@ public static ByteSource forPath(Path path) {
*
* @return New byte source.
*/
public static ByteSource forZip(ByteData data) {
return new ByteDataSource(data);
public static ByteSource forMemorySegment(MemorySegment data) {
return new MemorySegmentDataSource(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import jakarta.annotation.Nonnull;
import software.coley.lljzip.format.compression.ZipCompressions;
import software.coley.lljzip.format.model.LocalFileHeader;
import software.coley.lljzip.util.ByteData;
import software.coley.lljzip.util.ByteDataUtil;
import software.coley.lljzip.util.MemorySegmentUtil;

import java.io.IOException;
import java.io.InputStream;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;

/**
* Byte source from {@link LocalFileHeader}.
Expand All @@ -17,7 +18,7 @@
public final class LocalFileHeaderSource implements ByteSource {
private final LocalFileHeader fileHeader;
private final boolean isAndroid;
private ByteData decompressed;
private MemorySegment decompressed;

public LocalFileHeaderSource(LocalFileHeader fileHeader) {
this(fileHeader, false);
Expand All @@ -31,26 +32,24 @@ public LocalFileHeaderSource(LocalFileHeader fileHeader, boolean isAndroid) {
@Nonnull
@Override
public byte[] readAll() throws IOException {
return ByteDataUtil.toByteArray(decompress());
return MemorySegmentUtil.toByteArray(decompress());
}

@Nonnull
@Override
public byte[] peek(int count) throws IOException {
ByteData data = decompress();
long length = data.length();
MemorySegment data = decompress();
long length = data.byteSize();
if (length < count)
count = (int) length;
byte[] bytes = new byte[count];
data.get(0L, bytes, 0, count);
return bytes;
return data.asSlice(0, count).toArray(ValueLayout.JAVA_BYTE);
}

@Nonnull
@Override
public InputStream openStream() throws IOException {
// Delegate to byte source
return ByteSources.forZip(decompress()).openStream();
return ByteSources.forMemorySegment(decompress()).openStream();
}

/**
Expand All @@ -61,12 +60,12 @@ public InputStream openStream() throws IOException {
*/
public boolean isEmpty() throws IOException {
if (fileHeader.getCompressionMethod() == ZipCompressions.STORED)
return fileHeader.getFileData().length() == 0;
return decompress().length() == 0;
return fileHeader.getFileData().byteSize() == 0;
return decompress().byteSize() == 0;
}

private ByteData decompress() throws IOException {
ByteData decompressed = this.decompressed;
private MemorySegment decompress() throws IOException {
MemorySegment decompressed = this.decompressed;
if (decompressed == null) {
// From: https://cs.android.com/android/_/android/platform/frameworks/base/+/b3559643b946829933a76ed45750d13edfefad30:tools/aapt/ZipFile.cpp;l=436
// - If the compression mode given fails, it will get treated as STORED as a fallback
Expand Down
Loading

0 comments on commit c9fa994

Please sign in to comment.