Skip to content

Commit

Permalink
Merge pull request #1513 from stiemannkj1/issue-1512-engine-crashes-o…
Browse files Browse the repository at this point in the history
…n-types-missing-deps

Fixes: #1512 Plugin Engine crashes on types with missing dependencies even when error handlers are disabled.
  • Loading branch information
raphw authored Aug 27, 2023
2 parents b7f94db + addd088 commit 8265ef6
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 6 deletions.
18 changes: 12 additions & 6 deletions byte-buddy-dep/src/main/java/net/bytebuddy/build/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -4890,14 +4890,20 @@ public Dispatcher.Materializable call() {
listener.onError(typeDescription, errored);
return new Dispatcher.Materializable.ForFailedElement(element, typeDescription, errored);
} else if (!applied.isEmpty()) {
DynamicType dynamicType = builder.make(TypeResolutionStrategy.Disabled.INSTANCE, typePool);
listener.onTransformation(typeDescription, applied);
for (Map.Entry<TypeDescription, LoadedTypeInitializer> entry : dynamicType.getLoadedTypeInitializers().entrySet()) {
if (entry.getValue().isAlive()) {
listener.onLiveInitializer(typeDescription, entry.getKey());
try {
DynamicType dynamicType = builder.make(TypeResolutionStrategy.Disabled.INSTANCE, typePool);
listener.onTransformation(typeDescription, applied);
for (Map.Entry<TypeDescription, LoadedTypeInitializer> entry : dynamicType.getLoadedTypeInitializers().entrySet()) {
if (entry.getValue().isAlive()) {
listener.onLiveInitializer(typeDescription, entry.getKey());
}
}
return new Dispatcher.Materializable.ForTransformedElement(dynamicType);
} catch (Throwable throwable) {
errored.add(throwable);
listener.onError(typeDescription, errored);
return new Dispatcher.Materializable.ForFailedElement(element, typeDescription, errored);
}
return new Dispatcher.Materializable.ForTransformedElement(dynamicType);
} else {
listener.onIgnored(typeDescription, ignored);
return new Dispatcher.Materializable.ForRetainedElement(element);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package net.bytebuddy.build;

import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import static org.junit.Assert.assertTrue;

public final class DoesNotCrashWhenTypeMissingDepsTest {
@Rule
public TemporaryFolder tempDir = new TemporaryFolder();

@Test
public void does_not_crash_when_type_is_missing_deps() throws IOException {

File jar = tempDir.newFile("testInput.jar");

try (
FileOutputStream fileOutputStream = new FileOutputStream(jar);
JarOutputStream jarBuilder = new JarOutputStream(fileOutputStream, new Manifest())
) {

addDirs(jarBuilder, DoesNotCrashWhenTypeMissingDepsTest.class);

// Add type with a required dependency, but don't add its dependency
// so building/instrumenting the type fails.
addClass(jarBuilder, TypeWithDep.class);
addClass(jarBuilder, TypeToInstrument.class);
}


String containerTypeName = DoesNotCrashWhenTypeMissingDepsTest.class.getTypeName();

Plugin plugin = new Plugin() {
@Override
public DynamicType.Builder<?> apply(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator
) {
return builder.visit(new AsmVisitorWrapper.ForDeclaredFields().field(target -> true));
}

@Override
public boolean matches(TypeDescription target) {
return (
(containerTypeName + "$" + TypeToInstrument.class.getSimpleName()).equals(target.getTypeName()) ||
(containerTypeName + "$" + TypeWithDep.class.getSimpleName()).equals(target.getTypeName())
);
}

@Override
public void close() {}
};

assertTrue(
new Plugin.Engine.Default()
.withoutErrorHandlers()
.apply(
jar,
tempDir.newFile("testOutput.jar"),
Arrays.asList(() -> plugin)
)
.getFailed()
.containsKey(
TypeDescription.ForLoadedType.of(TypeWithDep.class)));

}

public static final class TypeDep {}

public static final class TypeWithDep {
private static final TypeDep DEP = new TypeDep();
}

public static final class TypeToInstrument {}

private static void addDirs(JarOutputStream jarBuilder, Class<?> aClass) throws IOException {
StringBuilder pathBuilder = new StringBuilder();
String filePath = getFilePath(aClass);
String[] pathSegments = filePath.split("[" + File.separator + "]");

for (int i = 0; i < pathSegments.length - 1; i++) {
pathBuilder.append(pathSegments[i]);
pathBuilder.append("/");
jarBuilder.putNextEntry(new ZipEntry(pathBuilder.toString()));
jarBuilder.closeEntry();
}
}

private static void addClass(JarOutputStream jarBuilder, Class<?> aClass) throws IOException {
String filePath = getFilePath(aClass);
jarBuilder.putNextEntry(new ZipEntry(filePath));

try (InputStream inputStream =
DoesNotCrashWhenTypeMissingDepsTest.class.getClassLoader().getResourceAsStream(filePath)) {

byte[] bytes = new byte[4096];
int bytesRead;

while ((bytesRead = inputStream.read(bytes, 0, bytes.length)) != -1) {
jarBuilder.write(bytes, 0, bytesRead);
}
}

jarBuilder.closeEntry();
}

private static String getFilePath(Class<?> aClass) {
return aClass.getTypeName().replace(".", "/") + ".class";
}
}

0 comments on commit 8265ef6

Please sign in to comment.