Skip to content

Commit

Permalink
Use source file mapping for all compilation providers
Browse files Browse the repository at this point in the history
All compilation providers should be setting the source file attribute
for use in debugging, there is no need to limit this to Java. This should
allow for deletions of Kotlin classes to be handled correctly.
  • Loading branch information
stuartwdouglas committed Mar 3, 2025
1 parent 5d094fa commit c61a3e2
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.asm.ClassReader;

import io.quarkus.paths.PathCollection;

public interface CompilationProvider extends Closeable {
Expand All @@ -28,7 +32,18 @@ default Set<String> handledSourcePaths() {

void compile(Set<File> files, Context context);

Path getSourcePath(Path classFilePath, PathCollection sourcePaths, String classesPath);
default Path getSourcePath(Path classFilePath, PathCollection sourcePaths, String classesPath) {
Path sourceFilePath;
final RuntimeUpdatesClassVisitor visitor = new RuntimeUpdatesClassVisitor(sourcePaths, classesPath);
try (final InputStream inputStream = Files.newInputStream(classFilePath)) {
final ClassReader reader = new ClassReader(inputStream);
reader.accept(visitor, 0);
sourceFilePath = visitor.getSourceFileForClass(classFilePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sourceFilePath;
}

@Override
default void close() throws IOException {
Expand Down Expand Up @@ -159,5 +174,7 @@ public boolean ignoreModuleInfo() {
public File getGeneratedSourcesDirectory() {
return generatedSourcesDirectory;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
Expand All @@ -20,14 +16,10 @@
import javax.tools.ToolProvider;

import org.jboss.logging.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;

import io.quarkus.deployment.dev.filesystem.QuarkusFileManager;
import io.quarkus.deployment.dev.filesystem.ReloadableFileManager;
import io.quarkus.deployment.dev.filesystem.StaticFileManager;
import io.quarkus.gizmo.Gizmo;
import io.quarkus.paths.PathCollection;

public class JavaCompilationProvider implements CompilationProvider {

Expand Down Expand Up @@ -115,20 +107,6 @@ public void compile(Set<File> filesToCompile, CompilationProvider.Context contex
}
}

@Override
public Path getSourcePath(Path classFilePath, PathCollection sourcePaths, String classesPath) {
Path sourceFilePath;
final RuntimeUpdatesClassVisitor visitor = new RuntimeUpdatesClassVisitor(sourcePaths, classesPath);
try (final InputStream inputStream = Files.newInputStream(classFilePath)) {
final ClassReader reader = new ClassReader(inputStream);
reader.accept(visitor, 0);
sourceFilePath = visitor.getSourceFileForClass(classFilePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sourceFilePath;
}

@Override
public void close() throws IOException {
if (this.fileManager != null) {
Expand All @@ -154,36 +132,4 @@ private String extractCompilationErrorMessage(final DiagnosticCollector<JavaFile
diagnosticsCollector.getDiagnostics().forEach(diagnostic -> builder.append("\n").append(diagnostic));
return String.format("\u001B[91mCompilation Failed:%s\u001b[0m", builder);
}

private static class RuntimeUpdatesClassVisitor extends ClassVisitor {
private final PathCollection sourcePaths;
private final String classesPath;
private String sourceFile;

public RuntimeUpdatesClassVisitor(PathCollection sourcePaths, String classesPath) {
super(Gizmo.ASM_API_VERSION);
this.sourcePaths = sourcePaths;
this.classesPath = classesPath;
}

@Override
public void visitSource(String source, String debug) {
this.sourceFile = source;
}

public Path getSourceFileForClass(final Path classFilePath) {
for (Path sourcesDir : sourcePaths) {
final Path classesDir = Paths.get(classesPath);
final StringBuilder sourceRelativeDir = new StringBuilder();
sourceRelativeDir.append(classesDir.relativize(classFilePath.getParent()));
sourceRelativeDir.append(File.separator);
sourceRelativeDir.append(sourceFile);
final Path sourceFilePath = sourcesDir.resolve(Path.of(sourceRelativeDir.toString()));
if (Files.exists(sourceFilePath)) {
return sourceFilePath;
}
}
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.deployment.dev;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.objectweb.asm.ClassVisitor;

import io.quarkus.gizmo.Gizmo;
import io.quarkus.paths.PathCollection;

public class RuntimeUpdatesClassVisitor extends ClassVisitor {
private final PathCollection sourcePaths;
private final String classesPath;
private String sourceFile;

public RuntimeUpdatesClassVisitor(PathCollection sourcePaths, String classesPath) {
super(Gizmo.ASM_API_VERSION);
this.sourcePaths = sourcePaths;
this.classesPath = classesPath;
}

@Override
public void visitSource(String source, String debug) {
this.sourceFile = source;
}

public Path getSourceFileForClass(final Path classFilePath) {
for (Path sourcesDir : sourcePaths) {
final Path classesDir = Paths.get(classesPath);
final StringBuilder sourceRelativeDir = new StringBuilder();
sourceRelativeDir.append(classesDir.relativize(classFilePath.getParent()));
sourceRelativeDir.append(File.separator);
sourceRelativeDir.append(sourceFile);
final Path sourceFilePath = sourcesDir.resolve(Path.of(sourceRelativeDir.toString()));
if (Files.exists(sourceFilePath)) {
return sourceFilePath;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.quarkus.kotlin.deployment;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -22,7 +21,6 @@
import org.jetbrains.kotlin.config.Services;

import io.quarkus.deployment.dev.CompilationProvider;
import io.quarkus.paths.PathCollection;

public class KotlinCompilationProvider implements CompilationProvider {

Expand Down Expand Up @@ -103,12 +101,6 @@ public void compile(Set<File> filesToCompile, Context context) {
}
}

@Override
public Path getSourcePath(Path classFilePath, PathCollection sourcePaths, String classesPath) {
// return same class so it is not removed
return classFilePath;
}

private static class SimpleKotlinCompilerMessageCollector implements MessageCollector {

private final List<String> errors = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@ protected void testDevMode() throws Exception {
ImmutableMap.of("return \"hello\"", "return \"" + uuid + "\""));

assertUpdatedResponseContains("/hello", uuid);

delete("src/main/kotlin/org/acme/GreetingResource.kt");

assertStatusCode("/hello", 404);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import org.wildfly.common.Assert;

import io.quarkus.gradle.BuildResult;
import io.quarkus.gradle.QuarkusGradleWrapperTestBase;
Expand Down Expand Up @@ -180,6 +181,12 @@ protected void replace(String srcFile, Map<String, String> tokens) {
}
}

protected void delete(String srcFile) {
final File source = new File(getProjectDir(), srcFile);
assertThat(source).exists();
Assert.assertTrue(source.delete());
}

protected void assertUpdatedResponseContains(String path, String value) {
assertUpdatedResponseContains(path, value, devModeTimeoutSeconds(), TimeUnit.SECONDS);
}
Expand All @@ -198,4 +205,11 @@ protected void assertUpdatedResponseContains(String path, String value, long wai
.pollDelay(100, TimeUnit.MILLISECONDS)
.atMost(waitAtMost, timeUnit).until(() -> getHttpResponse(path, waitAtMost, timeUnit).contains(value));
}

protected void assertStatusCode(String path, int code) {
await()
.pollDelay(100, TimeUnit.MILLISECONDS)
.atMost(devModeTimeoutSeconds(), TimeUnit.SECONDS)
.until(() -> Assert.assertTrue(devModeClient.getStrictHttpResponse(path, code)));
}
}

0 comments on commit c61a3e2

Please sign in to comment.