diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmDebugInfoLevel.java b/core/src/main/java/org/teavm/backend/wasm/WasmDebugInfoLevel.java index 6f36b923a..a525c13db 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmDebugInfoLevel.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmDebugInfoLevel.java @@ -16,7 +16,6 @@ package org.teavm.backend.wasm; public enum WasmDebugInfoLevel { - NONE, DEOBFUSCATION, FULL } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index 7d19c91db..63b806445 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -76,8 +76,9 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion(); private boolean strict; private boolean obfuscated; - private WasmDebugInfoLocation debugLocation; - private WasmDebugInfoLevel debugLevel; + private boolean debugInfo; + private WasmDebugInfoLocation debugLocation = WasmDebugInfoLocation.EXTERNAL; + private WasmDebugInfoLevel debugLevel = WasmDebugInfoLevel.FULL; private List intrinsicFactories = new ArrayList<>(); private Map customIntrinsics = new HashMap<>(); private List customTypeMapperFactories = new ArrayList<>(); @@ -94,11 +95,15 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { this.strict = strict; } - public void setDebugLevel(WasmDebugInfoLevel debugLevel) { + public void setDebugInfo(boolean debug) { + this.debugInfo = debug; + } + + public void setDebugInfoLevel(WasmDebugInfoLevel debugLevel) { this.debugLevel = debugLevel; } - public void setDebugLocation(WasmDebugInfoLocation debugLocation) { + public void setDebugInfoLocation(WasmDebugInfoLocation debugLocation) { this.debugLocation = debugLocation; } @@ -331,7 +336,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { GCDebugInfoBuilder debugInfoBuilder) throws IOException { var binaryWriter = new WasmBinaryWriter(); DebugLines debugLines = null; - if (debugLevel != WasmDebugInfoLevel.NONE) { + if (debugInfo) { debugLines = debugInfoBuilder.lines(); } var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated, @@ -353,7 +358,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { if (debugLocation == WasmDebugInfoLocation.EXTERNAL) { var debugInfoData = ExternalDebugFile.write(debugInfoBuilder.build()); if (debugInfoData != null) { - try (var output = buildTarget.createResource(outputName + ".tdbg")) { + try (var output = buildTarget.createResource(outputName + ".teadbg")) { output.write(debugInfoData); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/ExternalDebugFile.java b/core/src/main/java/org/teavm/backend/wasm/debug/ExternalDebugFile.java index 7ef7b7a39..b8a64521b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/debug/ExternalDebugFile.java +++ b/core/src/main/java/org/teavm/backend/wasm/debug/ExternalDebugFile.java @@ -16,10 +16,15 @@ package org.teavm.backend.wasm.debug; import java.util.List; +import java.util.function.Function; import org.teavm.backend.wasm.model.WasmCustomSection; +import org.teavm.backend.wasm.parser.AddressListener; +import org.teavm.backend.wasm.parser.WasmBinaryReader; import org.teavm.backend.wasm.render.WasmBinaryWriter; public final class ExternalDebugFile { + private static final int SIGNATURE = 0x67626474; + private ExternalDebugFile() { } @@ -28,8 +33,8 @@ public final class ExternalDebugFile { return null; } var writer = new WasmBinaryWriter(); - writer.writeInt32(0x67626474); - writer.writeInt32(1); + writer.writeInt32(SIGNATURE); + writer.writeLEB(1); for (var section : sections) { var data = section.getData(); writer.writeAsciiString(section.getName()); @@ -38,4 +43,29 @@ public final class ExternalDebugFile { } return writer.getData(); } + + public static boolean read(byte[] data, Function consumer) { + if (data.length < 4) { + return false; + } + var reader = new WasmBinaryReader(AddressListener.EMPTY, data); + var header = reader.readInt32(); + if (header != SIGNATURE) { + return false; + } + var version = reader.readLEB(); + if (version != 1) { + return false; + } + while (reader.ptr < data.length) { + var name = reader.readString(); + var length = reader.readLEB(); + var sectionConsumer = consumer.apply(name); + if (sectionConsumer != null) { + sectionConsumer.accept(data, reader.ptr, reader.ptr + length); + } + reader.ptr += length; + } + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/SectionDataConsumer.java b/core/src/main/java/org/teavm/backend/wasm/debug/SectionDataConsumer.java new file mode 100644 index 000000000..09ca81c6b --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/SectionDataConsumer.java @@ -0,0 +1,20 @@ +/* + * Copyright 2024 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.debug; + +public interface SectionDataConsumer { + void accept(byte[] data, int start, int end); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java b/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java index cd4a6d83e..58de16df2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java @@ -173,6 +173,13 @@ public class WasmBinaryReader { return result; } + public int readInt32() { + return (data[ptr++] & 0xFF) + | ((data[ptr++] & 0xFF) << 8) + | ((data[ptr++] & 0xFF) << 16) + | ((data[ptr++] & 0xFF) << 24); + } + public int readFixedInt() { return ((data[ptr++] & 0xFF) << 24) | ((data[ptr++] & 0xFF) << 16) diff --git a/core/src/main/js/wasm-gc-runtime/runtime.js b/core/src/main/js/wasm-gc-runtime/runtime.js index 21584a9dd..d03021974 100644 --- a/core/src/main/js/wasm-gc-runtime/runtime.js +++ b/core/src/main/js/wasm-gc-runtime/runtime.js @@ -425,6 +425,9 @@ function jsoImports(imports, context) { Object.defineProperty(cls, name, descriptor); }, javaObjectToJS(instance, cls) { + if (instance === null) { + return null; + } let existing = jsWrappers.get(instance); if (typeof existing != "undefined") { let result = existing.deref(); @@ -604,14 +607,18 @@ async function load(path, options) { options.installImports(importObj); } - let [deobfuscatorFactory, { module, instance }] = await Promise.all([ - options.attachStackDeobfuscator ? getDeobfuscator(path, options) : Promise.resolve(null), - WebAssembly.instantiateStreaming(fetch(path), importObj) + let deobfuscatorOptions = options.stackDeobfuscator || {}; + let debugInfoLocation = deobfuscatorOptions.infoLocation || "auto"; + let [deobfuscatorFactory, { module, instance }, debugInfo] = await Promise.all([ + deobfuscatorOptions.enabled ? getDeobfuscator(path, deobfuscatorOptions) : Promise.resolve(null), + WebAssembly.instantiateStreaming(fetch(path), importObj), + fetchExternalDebugInfo(path, debugInfoLocation, deobfuscatorOptions) ]); defaultsResult.supplyExports(instance.exports); if (deobfuscatorFactory) { - let deobfuscator = createDeobfuscator(module, deobfuscatorFactory); + let moduleToPass = debugInfoLocation === "auto" || debugInfoLocation === "embedded" ? module : null; + let deobfuscator = createDeobfuscator(moduleToPass, debugInfo, deobfuscatorFactory); if (deobfuscator !== null) { defaultsResult.supplyStackDeobfuscator(deobfuscator); } @@ -637,7 +644,8 @@ async function getDeobfuscator(path, options) { try { const importObj = {}; const defaultsResult = defaults(importObj, {}); - const { instance } = await WebAssembly.instantiateStreaming(fetch(path + "-deobfuscator.wasm"), importObj); + const deobfuscatorPath = options.path || path + "-deobfuscator.wasm"; + const { instance } = await WebAssembly.instantiateStreaming(fetch(deobfuscatorPath), importObj); defaultsResult.supplyExports(instance.exports) return instance; } catch (e) { @@ -646,16 +654,25 @@ async function getDeobfuscator(path, options) { } } -function createDeobfuscator(module, deobfuscatorFactory) { +function createDeobfuscator(module, externalData, deobfuscatorFactory) { let deobfuscator = null; let deobfuscatorInitialized = false; function ensureDeobfuscator() { if (!deobfuscatorInitialized) { deobfuscatorInitialized = true; - try { - deobfuscator = deobfuscatorFactory.exports.createForModule.value(module); - } catch (e) { - console.warn("Could not load create deobfuscator", e); + if (externalData !== null) { + try { + deobfuscator = deobfuscatorFactory.exports.createFromExternalFile.value(externalData); + } catch (e) { + console.warn("Could not load create deobfuscator", e); + } + } + if (deobfuscator == null && module !== null) { + try { + deobfuscator = deobfuscatorFactory.exports.createForModule.value(module); + } catch (e) { + console.warn("Could not create deobfuscator from module data", e); + } } } } @@ -663,4 +680,19 @@ function createDeobfuscator(module, deobfuscatorFactory) { ensureDeobfuscator(); return deobfuscator !== null ? deobfuscator.deobfuscate(addresses) : []; } +} + +async function fetchExternalDebugInfo(path, debugInfoLocation, options) { + if (!options.enabled) { + return null; + } + if (debugInfoLocation !== "auto" && debugInfoLocation !== "external") { + return null; + } + let location = options.externalInfoPath || path + ".teadbg"; + let response = await fetch(location); + if (!response.ok) { + return null; + } + return new Int8Array(await response.arrayBuffer()); } \ No newline at end of file diff --git a/tests/src/test/java/org/teavm/jso/export/ExportTest.java b/tests/src/test/java/org/teavm/jso/export/ExportTest.java index c8196ab81..6a23e26e2 100644 --- a/tests/src/test/java/org/teavm/jso/export/ExportTest.java +++ b/tests/src/test/java/org/teavm/jso/export/ExportTest.java @@ -171,8 +171,8 @@ public class ExportTest { try { var wasmGCTarget = new WasmGCTarget(); wasmGCTarget.setObfuscated(false); - wasmGCTarget.setDebugLocation(WasmDebugInfoLocation.EMBEDDED); - wasmGCTarget.setDebugLevel(WasmDebugInfoLevel.DEOBFUSCATION); + wasmGCTarget.setDebugInfoLocation(WasmDebugInfoLocation.EMBEDDED); + wasmGCTarget.setDebugInfoLevel(WasmDebugInfoLevel.DEOBFUSCATION); var teavm = new TeaVMBuilder(wasmGCTarget).build(); var outputDir = new File(wasmGCTargetFile, name); teavm.installPlugins(); diff --git a/tools/browser-runner/src/main/resources/test-server/frame.js b/tools/browser-runner/src/main/resources/test-server/frame.js index a4e722c06..59dcd007f 100644 --- a/tools/browser-runner/src/main/resources/test-server/frame.js +++ b/tools/browser-runner/src/main/resources/test-server/frame.js @@ -211,7 +211,9 @@ function launchWasmGCTest(file, argument, callback) { } TeaVM.wasmGC.load(file.path, { - attachStackDeobfuscator: true, + stackDeobfuscator: { + enabled: true + }, installImports: function(o) { o.teavmConsole.putcharStdout = putchar; o.teavmConsole.putcharStderr = putcharStderr; diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index 2308d7684..1aacf1133 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -38,6 +38,8 @@ import org.teavm.backend.c.generate.ShorteningFileNameProvider; import org.teavm.backend.c.generate.SimpleFileNameProvider; import org.teavm.backend.javascript.JSModuleType; import org.teavm.backend.javascript.JavaScriptTarget; +import org.teavm.backend.wasm.WasmDebugInfoLevel; +import org.teavm.backend.wasm.WasmDebugInfoLocation; import org.teavm.backend.wasm.WasmGCTarget; import org.teavm.backend.wasm.WasmRuntimeType; import org.teavm.backend.wasm.WasmTarget; @@ -108,6 +110,8 @@ public class TeaVMTool { private JavaScriptTarget javaScriptTarget; private WasmTarget webAssemblyTarget; private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; + private WasmDebugInfoLocation wasmDebugInfoLocation = WasmDebugInfoLocation.EXTERNAL; + private WasmDebugInfoLevel wasmDebugInfoLevel = WasmDebugInfoLevel.DEOBFUSCATION; private boolean wasmExceptionsUsed; private CTarget cTarget; private Set generatedFiles = new HashSet<>(); @@ -281,6 +285,14 @@ public class TeaVMTool { this.wasmExceptionsUsed = wasmExceptionsUsed; } + public void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation) { + this.wasmDebugInfoLocation = wasmDebugInfoLocation; + } + + public void setWasmDebugInfoLevel(WasmDebugInfoLevel wasmDebugInfoLevel) { + this.wasmDebugInfoLevel = wasmDebugInfoLevel; + } + public void setHeapDump(boolean heapDump) { this.heapDump = heapDump; } @@ -388,6 +400,9 @@ public class TeaVMTool { var target = new WasmGCTarget(); target.setObfuscated(obfuscated); target.setStrict(strict); + target.setDebugInfo(debugInformationGenerated); + target.setDebugInfoLevel(debugInformationGenerated ? WasmDebugInfoLevel.FULL : wasmDebugInfoLevel); + target.setDebugInfoLocation(wasmDebugInfoLocation); return target; } diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java index bdd204f61..2fe510467 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java @@ -18,6 +18,8 @@ package org.teavm.tooling.builder; import java.util.List; import java.util.Properties; import org.teavm.backend.javascript.JSModuleType; +import org.teavm.backend.wasm.WasmDebugInfoLevel; +import org.teavm.backend.wasm.WasmDebugInfoLocation; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.tooling.TeaVMSourceFilePolicy; import org.teavm.tooling.TeaVMTargetType; @@ -83,6 +85,10 @@ public interface BuildStrategy { void setWasmExceptionsUsed(boolean wasmExceptionsUsed); + void setWasmDebugInfoLevel(WasmDebugInfoLevel wasmDebugInfoLevel); + + void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation); + void setMinHeapSize(int minHeapSize); void setMaxHeapSize(int maxHeapSize); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java index dad70e395..9f790dfd1 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java @@ -25,6 +25,8 @@ import java.util.Arrays; import java.util.List; import java.util.Properties; import org.teavm.backend.javascript.JSModuleType; +import org.teavm.backend.wasm.WasmDebugInfoLevel; +import org.teavm.backend.wasm.WasmDebugInfoLocation; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.callgraph.CallGraph; import org.teavm.diagnostics.ProblemProvider; @@ -62,6 +64,8 @@ public class InProcessBuildStrategy implements BuildStrategy { private String[] classesToPreserve = new String[0]; private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; private boolean wasmExceptionsUsed; + private WasmDebugInfoLevel wasmDebugInfoLevel; + private WasmDebugInfoLocation wasmDebugInfoLocation; private int minHeapSize = 4 * 1024 * 1024; private int maxHeapSize = 128 * 1024 * 1024; private final List sourceFileProviders = new ArrayList<>(); @@ -219,6 +223,16 @@ public class InProcessBuildStrategy implements BuildStrategy { this.wasmExceptionsUsed = wasmExceptionsUsed; } + @Override + public void setWasmDebugInfoLevel(WasmDebugInfoLevel wasmDebugInfoLevel) { + this.wasmDebugInfoLevel = wasmDebugInfoLevel; + } + + @Override + public void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation) { + this.wasmDebugInfoLocation = wasmDebugInfoLocation; + } + @Override public void setMinHeapSize(int minHeapSize) { this.minHeapSize = minHeapSize; @@ -273,6 +287,8 @@ public class InProcessBuildStrategy implements BuildStrategy { tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null); tool.setWasmVersion(wasmVersion); tool.setWasmExceptionsUsed(wasmExceptionsUsed); + tool.setWasmDebugInfoLevel(wasmDebugInfoLevel); + tool.setWasmDebugInfoLocation(wasmDebugInfoLocation); tool.setMinHeapSize(minHeapSize); tool.setMaxHeapSize(maxHeapSize); tool.setHeapDump(heapDump); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java index b9c32c305..bf71e65a6 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java @@ -20,6 +20,8 @@ import java.rmi.server.UnicastRemoteObject; import java.util.List; import java.util.Properties; import org.teavm.backend.javascript.JSModuleType; +import org.teavm.backend.wasm.WasmDebugInfoLevel; +import org.teavm.backend.wasm.WasmDebugInfoLocation; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.callgraph.CallGraph; import org.teavm.diagnostics.Problem; @@ -192,6 +194,16 @@ public class RemoteBuildStrategy implements BuildStrategy { request.wasmExceptionsUsed = wasmExceptionsUsed; } + @Override + public void setWasmDebugInfoLevel(WasmDebugInfoLevel wasmDebugInfoLevel) { + request.wasmDebugInfoLevel = wasmDebugInfoLevel; + } + + @Override + public void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation) { + request.wasmDebugInfoLocation = wasmDebugInfoLocation; + } + @Override public void setMinHeapSize(int minHeapSize) { request.minHeapSize = minHeapSize; diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java index b6a46403a..a31bc0c71 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java @@ -162,6 +162,8 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi tool.setStrict(request.strict); tool.setWasmVersion(request.wasmVersion); tool.setWasmExceptionsUsed(request.wasmExceptionsUsed); + tool.setWasmDebugInfoLocation(request.wasmDebugInfoLocation); + tool.setWasmDebugInfoLevel(request.wasmDebugInfoLevel); tool.setMinHeapSize(request.minHeapSize); tool.setMaxHeapSize(request.maxHeapSize); tool.setHeapDump(request.heapDump); diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java index 7e2316443..78b40c20f 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.teavm.backend.javascript.JSModuleType; +import org.teavm.backend.wasm.WasmDebugInfoLevel; +import org.teavm.backend.wasm.WasmDebugInfoLocation; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.tooling.TeaVMSourceFilePolicy; import org.teavm.tooling.TeaVMTargetType; @@ -50,6 +52,8 @@ public class RemoteBuildRequest implements Serializable { public boolean fastDependencyAnalysis; public WasmBinaryVersion wasmVersion; public boolean wasmExceptionsUsed; + public WasmDebugInfoLocation wasmDebugInfoLocation; + public WasmDebugInfoLevel wasmDebugInfoLevel; public int minHeapSize; public int maxHeapSize; public boolean heapDump; diff --git a/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/Compiler.java b/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/Compiler.java index 47b0e03b0..0e4ea727e 100644 --- a/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/Compiler.java +++ b/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/Compiler.java @@ -43,6 +43,7 @@ public final class Compiler { tool.setTargetFileName(args[2]); tool.setObfuscated(true); tool.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED); + tool.setStrict(false); tool.generate(); TeaVMProblemRenderer.describeProblems(tool.getDependencyInfo().getCallGraph(), tool.getProblemProvider(), log); diff --git a/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/DeobfuscatorFactory.java b/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/DeobfuscatorFactory.java index 79f3fc20e..fbad00fc8 100644 --- a/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/DeobfuscatorFactory.java +++ b/tools/deobfuscator-wasm-gc/src/main/java/org/teavm/tooling/deobfuscate/wasmgc/DeobfuscatorFactory.java @@ -15,6 +15,8 @@ */ package org.teavm.tooling.deobfuscate.wasmgc; +import java.util.Arrays; +import org.teavm.backend.wasm.debug.ExternalDebugFile; import org.teavm.backend.wasm.debug.parser.LinesDeobfuscationParser; import org.teavm.jso.JSBody; import org.teavm.jso.JSExport; @@ -42,9 +44,29 @@ public final class DeobfuscatorFactory { } return bytes; }); - return new Deobfuscator(parser.getLineInfo()); + var lineInfo = parser.getLineInfo(); + return lineInfo != null ? new Deobfuscator(parser.getLineInfo()) : null; } @JSBody(params = { "module", "name"}, script = "return WebAssembly.Module.customSections(module, name);") private static native JSArrayReader getSection(JSObject module, String name); + + @JSExport + public static Deobfuscator createFromExternalFile(byte[] data) { + var parser = new LinesDeobfuscationParser(); + var success = ExternalDebugFile.read(data, sectionName -> { + if (!parser.canHandleSection(sectionName)) { + return null; + } + return (bytes, start, end) -> parser.applySection(sectionName, Arrays.copyOfRange(bytes, start, end)); + }); + if (!success) { + return null; + } + var lineInfo = parser.getLineInfo(); + if (lineInfo == null) { + return null; + } + return new Deobfuscator(lineInfo); + } } diff --git a/tools/gradle/src/main/java/org/teavm/gradle/TeaVMExtensionImpl.java b/tools/gradle/src/main/java/org/teavm/gradle/TeaVMExtensionImpl.java index 5c0300646..b15efca44 100644 --- a/tools/gradle/src/main/java/org/teavm/gradle/TeaVMExtensionImpl.java +++ b/tools/gradle/src/main/java/org/teavm/gradle/TeaVMExtensionImpl.java @@ -31,6 +31,8 @@ import org.teavm.gradle.api.TeaVMJSConfiguration; import org.teavm.gradle.api.TeaVMWasiConfiguration; import org.teavm.gradle.api.TeaVMWasmConfiguration; import org.teavm.gradle.api.TeaVMWasmGCConfiguration; +import org.teavm.gradle.api.WasmDebugInfoLevel; +import org.teavm.gradle.api.WasmDebugInfoLocation; class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtension { private TeaVMJSConfiguration js; @@ -111,6 +113,12 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio wasmGC.getCopyRuntime().convention(property("wasm-gc.copyRuntime").map(Boolean::parseBoolean).orElse(true)); wasmGC.getObfuscated().convention(property("wasm-gc.obfuscated").map(Boolean::parseBoolean).orElse(true)); wasmGC.getDisassembly().convention(property("wasm-gc.disassembly").map(Boolean::parseBoolean).orElse(false)); + wasmGC.getDebugInformation().convention(property("wasm-gc.debugInformation").map(Boolean::parseBoolean) + .orElse(false)); + wasmGC.getDebugInfoLocation().convention(property("wasm-gc.debugInformation.location") + .map(v -> WasmDebugInfoLocation.valueOf(v.toUpperCase())).orElse(WasmDebugInfoLocation.EXTERNAL)); + wasmGC.getDebugInfoLevel().convention(property("wasm-gc.debugInformation.level") + .map(v -> WasmDebugInfoLevel.valueOf(v.toUpperCase())).orElse(WasmDebugInfoLevel.DEOBFUSCATION)); } private void setupWasiDefaults() { diff --git a/tools/gradle/src/main/java/org/teavm/gradle/api/TeaVMWasmGCConfiguration.java b/tools/gradle/src/main/java/org/teavm/gradle/api/TeaVMWasmGCConfiguration.java index d2794bc05..bac4bb61c 100644 --- a/tools/gradle/src/main/java/org/teavm/gradle/api/TeaVMWasmGCConfiguration.java +++ b/tools/gradle/src/main/java/org/teavm/gradle/api/TeaVMWasmGCConfiguration.java @@ -27,4 +27,8 @@ public interface TeaVMWasmGCConfiguration extends TeaVMCommonConfiguration, TeaV Property getDisassembly(); Property getTargetFileName(); + + Property getDebugInfoLocation(); + + Property getDebugInfoLevel(); } diff --git a/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLevel.java b/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLevel.java new file mode 100644 index 000000000..9fc3f29ce --- /dev/null +++ b/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLevel.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 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.gradle.api; + +public enum WasmDebugInfoLevel { + DEOBFUSCATION, + FULL +} diff --git a/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLocation.java b/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLocation.java new file mode 100644 index 000000000..7f6330448 --- /dev/null +++ b/tools/gradle/src/main/java/org/teavm/gradle/api/WasmDebugInfoLocation.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 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.gradle.api; + +public enum WasmDebugInfoLocation { + EMBEDDED, + EXTERNAL +} diff --git a/tools/gradle/src/main/java/org/teavm/gradle/tasks/GenerateWasmGCTask.java b/tools/gradle/src/main/java/org/teavm/gradle/tasks/GenerateWasmGCTask.java index f1e180726..18922b242 100644 --- a/tools/gradle/src/main/java/org/teavm/gradle/tasks/GenerateWasmGCTask.java +++ b/tools/gradle/src/main/java/org/teavm/gradle/tasks/GenerateWasmGCTask.java @@ -17,6 +17,8 @@ package org.teavm.gradle.tasks; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; +import org.teavm.gradle.api.WasmDebugInfoLevel; +import org.teavm.gradle.api.WasmDebugInfoLocation; import org.teavm.tooling.TeaVMTargetType; import org.teavm.tooling.builder.BuildStrategy; @@ -24,6 +26,9 @@ public abstract class GenerateWasmGCTask extends TeaVMTask { public GenerateWasmGCTask() { getStrict().convention(true); getObfuscated().convention(true); + getDebugInfo().convention(true); + getDebugInfoLevel().convention(WasmDebugInfoLevel.DEOBFUSCATION); + getDebugInfoLocation().convention(WasmDebugInfoLocation.EXTERNAL); } @Input @@ -32,10 +37,36 @@ public abstract class GenerateWasmGCTask extends TeaVMTask { @Input public abstract Property getObfuscated(); + @Input + public abstract Property getDebugInfo(); + + @Input + public abstract Property getDebugInfoLevel(); + + @Input + public abstract Property getDebugInfoLocation(); + @Override protected void setupBuilder(BuildStrategy builder) { builder.setStrict(getStrict().get()); builder.setObfuscated(getObfuscated().get()); + builder.setDebugInformationGenerated(getDebugInfo().get()); + switch (getDebugInfoLevel().get()) { + case FULL: + builder.setWasmDebugInfoLevel(org.teavm.backend.wasm.WasmDebugInfoLevel.FULL); + break; + case DEOBFUSCATION: + builder.setWasmDebugInfoLevel(org.teavm.backend.wasm.WasmDebugInfoLevel.DEOBFUSCATION); + break; + } + switch (getDebugInfoLocation().get()) { + case EMBEDDED: + builder.setWasmDebugInfoLocation(org.teavm.backend.wasm.WasmDebugInfoLocation.EMBEDDED); + break; + case EXTERNAL: + builder.setWasmDebugInfoLocation(org.teavm.backend.wasm.WasmDebugInfoLocation.EXTERNAL); + break; + } builder.setTargetType(TeaVMTargetType.WEBASSEMBLY_GC); } } diff --git a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java index 6b895e92b..273ba92c3 100644 --- a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java +++ b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java @@ -71,8 +71,9 @@ class WebAssemblyGCPlatformSupport extends TestPlatformSupport { var target = new WasmGCTarget(); target.setObfuscated(false); target.setStrict(true); - target.setDebugLevel(WasmDebugInfoLevel.DEOBFUSCATION); - target.setDebugLocation(WasmDebugInfoLocation.EMBEDDED); + target.setDebugInfo(true); + target.setDebugInfoLevel(WasmDebugInfoLevel.DEOBFUSCATION); + target.setDebugInfoLocation(WasmDebugInfoLocation.EMBEDDED); var sourceDirs = System.getProperty(SOURCE_DIRS); if (sourceDirs != null) { var dirs = new ArrayList(); diff --git a/tools/junit/src/main/resources/teavm-run-test-wasm-gc.html b/tools/junit/src/main/resources/teavm-run-test-wasm-gc.html index e70e638be..7eb2e9072 100644 --- a/tools/junit/src/main/resources/teavm-run-test-wasm-gc.html +++ b/tools/junit/src/main/resources/teavm-run-test-wasm-gc.html @@ -25,7 +25,9 @@