diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index a12abbbd3..b95820650 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -43,6 +43,7 @@ import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.debug.DebugInfoBuilder; import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; +import org.teavm.backend.wasm.generate.SourceFileResolver; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmGenerationContext; @@ -209,6 +210,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private boolean hasThreads; private WasmRuntimeType runtimeType = WasmRuntimeType.TEAVM; private ReportingWasmBinaryStatsCollector statsCollector; + private SourceFileResolver sourceFileResolver; @Override public void setController(TeaVMTargetController controller) { @@ -309,6 +311,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { this.runtimeType = runtimeType; } + public void setSourceFileResolver(SourceFileResolver sourceFileResolver) { + this.sourceFileResolver = sourceFileResolver; + } + @Override public WasmRuntimeType getRuntimeType() { return runtimeType; @@ -476,7 +482,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { var names = new NameProviderWithSpecialNames(new WasmNameProvider(), controller.getUnprocessedClassSource()); var metadataRequirements = new ClassMetadataRequirements(controller.getDependencyInfo()); - var dwarfGenerator = debugging ? new DwarfGenerator() : null; + var dwarfGenerator = debugging ? new DwarfGenerator(sourceFileResolver) : null; if (dwarfGenerator != null) { dwarfGenerator.begin(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DirectorySourceFileResolver.java b/core/src/main/java/org/teavm/backend/wasm/generate/DirectorySourceFileResolver.java new file mode 100644 index 000000000..1aed41077 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DirectorySourceFileResolver.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generate; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DirectorySourceFileResolver implements SourceFileResolver { + private List directories; + private Map cache = new HashMap<>(); + + public DirectorySourceFileResolver(List directories) { + this.directories = List.copyOf(directories); + } + + @Override + public String resolveFile(String file) { + return cache.computeIfAbsent(file, f -> { + for (var dir : directories) { + var candidate = new File(dir, f); + if (candidate.isFile()) { + return new Wrapper(candidate.getAbsolutePath()); + } + } + return new Wrapper(null); + }).value; + } + + private static class Wrapper { + final String value; + + Wrapper(String value) { + this.value = value; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java index 3bc4a33f9..fc190c75d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java @@ -42,9 +42,13 @@ public class DwarfGenerator { private DwarfPlaceholder endOfSection; public final DwarfStrings strings = new DwarfStrings(); private DwarfStrings lineStrings = new DwarfStrings(); - private DwarfLinesGenerator lines = new DwarfLinesGenerator(lineStrings); + private DwarfLinesGenerator lines; private Marker highPcMarker; + public DwarfGenerator(SourceFileResolver sourceFileResolver) { + lines = new DwarfLinesGenerator(lineStrings, sourceFileResolver); + } + public void begin() { endOfSection = infoWriter.placeholder(4); lines.begin(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java index 5c459cd8f..fbdecd20e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java @@ -26,6 +26,8 @@ import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_COPY; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_SET_FILE; import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; +import java.util.HashMap; +import java.util.Map; import org.teavm.backend.wasm.blob.Blob; import org.teavm.backend.wasm.blob.Marker; @@ -37,6 +39,7 @@ class DwarfLinesGenerator { Blob blob = new Blob(); private DwarfStrings strings; + private SourceFileResolver sourceFileResolver; private Blob instructionsBlob = new Blob(); private Marker unitLengthMarker; private Marker headerLengthMarker; @@ -48,9 +51,11 @@ class DwarfLinesGenerator { private int file = 1; private int line = 1; private boolean sequenceStarted; + private Map resolvedFileMap = new HashMap<>(); - DwarfLinesGenerator(DwarfStrings strings) { + DwarfLinesGenerator(DwarfStrings strings, SourceFileResolver sourceFileResolver) { this.strings = strings; + this.sourceFileResolver = sourceFileResolver; } void begin() { @@ -138,6 +143,7 @@ class DwarfLinesGenerator { } private int fileRef(String path) { + path = resolvePath(path); var ref = fileIndexes.getOrDefault(path, -1); if (ref < 0) { var nameIndex = path.lastIndexOf('/') + 1; @@ -154,6 +160,16 @@ class DwarfLinesGenerator { return ref; } + private String resolvePath(String path) { + if (sourceFileResolver == null) { + return path; + } + return resolvedFileMap.computeIfAbsent(path, p -> { + var result = sourceFileResolver.resolveFile(p); + return result != null ? result : p; + }); + } + private int dirRef(String path) { var ref = dirIndexes.getOrDefault(path, -1); if (ref < 0) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/SourceFileResolver.java b/core/src/main/java/org/teavm/backend/wasm/generate/SourceFileResolver.java new file mode 100644 index 000000000..2e59a9514 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/SourceFileResolver.java @@ -0,0 +1,20 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generate; + +public interface SourceFileResolver { + String resolveFile(String file); +} diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index cfcd522ea..52305de71 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -61,5 +61,28 @@ tasks.test { systemProperty("teavm.junit.c.compiler", providers.gradleProperty("teavm.tests.c.compiler") .orElse("compile-c-unix-fast.sh").get()) + jvmArgumentProviders += object : CommandLineArgumentProvider { + override fun asArguments(): Iterable { + val dependencies = configurations.testRuntimeClasspath.get() + .incoming.resolutionResult.allDependencies + .asSequence() + .filterIsInstance() + .map { it.requested } + .filterIsInstance() + .map { project.rootProject.project(it.projectPath) } + val projects = dependencies + project + val dirs = projects.map { it.layout.projectDirectory }.flatMap { + sequenceOf( + it.dir("src/main/java"), + it.dir("src/test/java") + ) + } + val result = dirs + .map { it.asFile.absolutePath } + .joinToString(File.pathSeparator) + return listOf("-Dteavm.junit.sourceDirs=$result") + } + } + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) } \ No newline at end of file diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java index 9edb775bf..dc81833de 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.StringTokenizer; import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -61,6 +62,7 @@ import org.teavm.backend.c.generate.CNameProvider; import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.wasm.WasmRuntimeType; import org.teavm.backend.wasm.WasmTarget; +import org.teavm.backend.wasm.generate.DirectorySourceFileResolver; import org.teavm.callgraph.CallGraph; import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformationBuilder; @@ -117,6 +119,7 @@ public class TeaVMTestRunner extends Runner implements Filterable { private static final String MINIFIED = "teavm.junit.minified"; private static final String OPTIMIZED = "teavm.junit.optimized"; private static final String FAST_ANALYSIS = "teavm.junit.fastAnalysis"; + private static final String SOURCE_DIRS = "teavm.junit.sourceDirs"; private Class testClass; private boolean isWholeClassCompilation; @@ -1125,6 +1128,20 @@ public class TeaVMTestRunner extends Runner implements Filterable { Supplier targetSupplier = () -> { WasmTarget target = new WasmTarget(); target.setRuntimeType(runtimeType); + var sourceDirs = System.getProperty(SOURCE_DIRS); + if (sourceDirs != null) { + var dirs = new ArrayList(); + for (var tokenizer = new StringTokenizer(sourceDirs, Character.toString(File.pathSeparatorChar)); + tokenizer.hasMoreTokens();) { + var dir = new File(tokenizer.nextToken()); + if (dir.isDirectory()) { + dirs.add(dir); + } + } + if (!dirs.isEmpty()) { + target.setSourceFileResolver(new DirectorySourceFileResolver(dirs)); + } + } return target; }; return compile(configuration, targetSupplier, TestNativeEntryPoint.class.getName(), path,