Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public Builder withInterpretedFunctions(Set<Integer> interpretedFunctions) {
return this;
}

public Builder withUseDebugNames(boolean useDebugNames) {
compilerBuilder.withUseDebugNames(useDebugNames);
return this;
}

public Builder withCache(Cache cache) {
this.cache = cache;
return this;
Expand Down
57 changes: 42 additions & 15 deletions compiler/src/main/java/run/endive/compiler/internal/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static run.endive.compiler.internal.CompilerUtil.emitInvokeVirtual;
import static run.endive.compiler.internal.CompilerUtil.emitJvmToLong;
import static run.endive.compiler.internal.CompilerUtil.emitLongToJvm;
import static run.endive.compiler.internal.CompilerUtil.extractFuncId;
import static run.endive.compiler.internal.CompilerUtil.hasTooManyParameters;
import static run.endive.compiler.internal.CompilerUtil.internalClassName;
import static run.endive.compiler.internal.CompilerUtil.jvmReturnType;
Expand Down Expand Up @@ -90,6 +91,7 @@
import run.endive.wasm.types.ExternalType;
import run.endive.wasm.types.FunctionBody;
import run.endive.wasm.types.FunctionType;
import run.endive.wasm.types.NameCustomSection;
import run.endive.wasm.types.OpCode;
import run.endive.wasm.types.ValType;

Expand Down Expand Up @@ -161,6 +163,7 @@ public final class Compiler {
private final boolean[] tailCallTypes;
private final boolean moduleHasTailCalls;
private final boolean moduleHasObjectRefs;
private final NameCustomSection debugNameSection;
private boolean useBridgeClasses;
private IntFunction<String> callIndirectClassResolver;

Expand All @@ -170,7 +173,8 @@ private Compiler(
int maxFunctionsPerClass,
InterpreterFallback interpreterFallback,
Set<Integer> interpretedFunctions,
Supplier<ClassCollector> classCollectorFactory) {
Supplier<ClassCollector> classCollectorFactory,
boolean useDebugNames) {
this.className = requireNonNull(className, "className");
this.module = requireNonNull(module, "module");
this.analyzer = new WasmAnalyzer(module);
Expand Down Expand Up @@ -202,6 +206,7 @@ private Compiler(
this.functionTypes.stream()
.anyMatch(ft -> ft.hasObjectRefParams() || ft.hasObjectRefReturns());
this.maxFunctionsPerClass = maxFunctionsPerClass;
this.debugNameSection = useDebugNames ? module.nameSection() : null;
}

private Set<Integer> collectCallRefTypeIds() {
Expand Down Expand Up @@ -229,6 +234,7 @@ public static final class Builder {
private InterpreterFallback interpreterFallback;
private Set<Integer> interpretedFunctions;
private Supplier<ClassCollector> classCollectorFactory;
private boolean useDebugNames;

private Builder(WasmModule module) {
this.module = module;
Expand Down Expand Up @@ -259,6 +265,11 @@ public Builder withClassCollectorFactory(Supplier<ClassCollector> classCollector
return this;
}

public Builder withUseDebugNames(boolean useDebugNames) {
this.useDebugNames = useDebugNames;
return this;
}

public Compiler build() {
var className = this.className;
if (className == null) {
Expand All @@ -280,7 +291,8 @@ public Compiler build() {
maxFunctionsPerClass,
interpreterFallback,
interpretedFunctions,
classCollectorFactory);
classCollectorFactory,
useDebugNames);
}
}

Expand Down Expand Up @@ -351,10 +363,8 @@ private void compileExtraClasses() {
break;
} catch (MethodTooLargeException e) {
String methodName = e.getMethodName();
if (methodName.startsWith("func_")) {
// Add the method to interpreted function list... and try again.
var funcId = Integer.parseInt(methodName.substring("func_".length()));

int funcId = extractFuncId(methodName);
if (funcId >= 0) {
String functionDescription = "WASM function index: " + funcId;
if (module.nameSection() != null) {
String name = module.nameSection().nameOfFunction(funcId);
Expand Down Expand Up @@ -496,7 +506,7 @@ private Consumer<ClassVisitor> emitFunctionGroup(int start, int end, String inte
if (i < functionImports) {
emitFunction(
classWriter,
methodNameForFunc(funcId),
methodNameForFunc(funcId, debugNameSection),
methodTypeFor(type),
true,
asm -> compileHostFunction(funcId, type, asm));
Expand All @@ -507,7 +517,7 @@ private Consumer<ClassVisitor> emitFunctionGroup(int start, int end, String inte

emitFunction(
classWriter,
methodNameForFunc(funcId),
methodNameForFunc(funcId, debugNameSection),
methodTypeFor(type),
true,
asm ->
Expand Down Expand Up @@ -689,8 +699,8 @@ private boolean isFuncTypeMatch(int expectedTypeId, int funcIdx, FunctionType ex
private static RuntimeException handleMethodTooLarge(
MethodTooLargeException e, WasmModule module) {
String name = e.getMethodName();
if (name.startsWith("func_") && module.nameSection() != null) {
int funcId = Integer.parseInt(name.split("_", -1)[1]);
int funcId = extractFuncId(name);
if (funcId >= 0 && module.nameSection() != null) {
String function = module.nameSection().nameOfFunction(funcId);
if (function != null) {
name += " (" + function + ")";
Expand Down Expand Up @@ -1392,7 +1402,11 @@ private void compileCallFunction(int funcId, FunctionType type, InstructionAdapt
asm.load(0, OBJECT_TYPE);

emitInvokeFunction(
asm, internalClassName(classNameForFuncGroup(className, funcId)), funcId, type);
asm,
internalClassName(classNameForFuncGroup(className, funcId)),
funcId,
type,
debugNameSection);

// box the result into long[]
Class<?> returnType = jvmReturnType(type);
Expand Down Expand Up @@ -1482,7 +1496,11 @@ private void compileCallWithRefsFunction(
asm.load(0, OBJECT_TYPE);

emitInvokeFunction(
asm, internalClassName(classNameForFuncGroup(className, funcId)), funcId, type);
asm,
internalClassName(classNameForFuncGroup(className, funcId)),
funcId,
type,
debugNameSection);

// Build CallResult from the function's JVM return value
Class<?> returnType = jvmReturnType(type);
Expand Down Expand Up @@ -1681,7 +1699,11 @@ private void compileCallIndirect(
// return func_0(a, b, memory, callerInstance);
asm.mark(labels[i]);
emitInvokeFunction(
asm, classNameForFuncGroup(internalClassName, keys[i]), keys[i], type);
asm,
classNameForFuncGroup(internalClassName, keys[i]),
keys[i],
type,
debugNameSection);
asm.areturn(getType(jvmReturnType(type)));
}

Expand Down Expand Up @@ -1829,7 +1851,11 @@ private void compileCallIndirectApply(
// return func_0(a, b, memory, callerInstance);
asm.mark(labels[i]);
emitInvokeFunction(
asm, classNameForFuncGroup(internalClassName, keys[i]), keys[i], type);
asm,
classNameForFuncGroup(internalClassName, keys[i]),
keys[i],
type,
debugNameSection);
asm.areturn(getType(jvmReturnType(type)));
asm.areturn(OBJECT_TYPE);
}
Expand Down Expand Up @@ -2105,7 +2131,8 @@ private void compileFunction(
tailCallFunctions,
tailCallTypes,
useBridgeClasses ? callIndirectClassResolver : typeId -> internalClassName,
analysis.maxTempSlots());
analysis.maxTempSlots(),
debugNameSection != null);

int localsCount = type.params().size();
if (hasTooManyParameters(type)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import run.endive.wasm.types.ExternalType;
import run.endive.wasm.types.FunctionBody;
import run.endive.wasm.types.FunctionType;
import run.endive.wasm.types.NameCustomSection;
import run.endive.wasm.types.TagImport;
import run.endive.wasm.types.ValType;
import run.endive.wasm.types.Value;
Expand Down Expand Up @@ -282,11 +283,15 @@ public static void emitInvokeVirtual(MethodVisitor asm, Method method) {
}

public static void emitInvokeFunction(
MethodVisitor asm, String internalClassName, int funcId, FunctionType functionType) {
MethodVisitor asm,
String internalClassName,
int funcId,
FunctionType functionType,
NameCustomSection nameSection) {
asm.visitMethodInsn(
Opcodes.INVOKESTATIC,
internalClassName,
methodNameForFunc(funcId),
methodNameForFunc(funcId, nameSection),
methodTypeFor(functionType).toMethodDescriptorString(),
false);
}
Expand All @@ -298,10 +303,45 @@ public static String valueMethodName(List<ValType> types) {
.collect(joining("_"));
}

public static String methodNameForFunc(int funcId) {
public static String methodNameForFunc(int funcId, NameCustomSection nameSection) {
if (nameSection != null) {
String name = nameSection.nameOfFunction(funcId);
if (name != null && !name.isEmpty()) {
String sanitized = sanitizeWasmName(name);
if (!sanitized.isEmpty()) {
return sanitized + "_" + funcId;
}
}
}
return "func_" + funcId;
}

static String sanitizeWasmName(String name) {
StringBuilder sb = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
// see https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.2.2 for reference
if (c == '.' || c == ';' || c == '[' || c == '/' || c == '<' || c == '>') {
sb.append('_');
} else {
sb.append(c);
}
}
return sb.toString();
}

static int extractFuncId(String methodName) {
int lastUnderscore = methodName.lastIndexOf('_');
if (lastUnderscore < 0) {
return -1;
}
try {
return Integer.parseInt(methodName.substring(lastUnderscore + 1));
} catch (NumberFormatException e) {
return -1;
}
}

static String callMethodName(int funcId) {
return "call_" + funcId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import run.endive.wasm.WasmModule;
import run.endive.wasm.types.FunctionBody;
import run.endive.wasm.types.FunctionType;
import run.endive.wasm.types.NameCustomSection;
import run.endive.wasm.types.TypeSection;
import run.endive.wasm.types.ValType;

Expand All @@ -33,6 +34,7 @@ final class Context {
private final int tempSlot;
private final int trySaveBaseSlot;
private final IntFunction<String> callIndirectClassResolver;
private final boolean useDebugNames;

public Context(
WasmModule module,
Expand All @@ -46,7 +48,8 @@ public Context(
boolean[] tailCallFunctions,
boolean[] tailCallTypes,
IntFunction<String> callIndirectClassResolver,
int maxTempSlots) {
int maxTempSlots,
boolean useDebugNames) {
this.module = module;
this.internalClassName = internalClassName;
this.maxFunctionsPerClass = maxFunctionsPerClass;
Expand All @@ -58,6 +61,7 @@ public Context(
this.tailCallFunctions = tailCallFunctions;
this.tailCallTypes = tailCallTypes;
this.callIndirectClassResolver = callIndirectClassResolver;
this.useDebugNames = useDebugNames;

// compute JVM slot indices for WASM locals
List<Integer> slots = new ArrayList<>(type.params().size() + body.localTypes().size());
Expand Down Expand Up @@ -122,6 +126,10 @@ public TypeSection typeSection() {
return module.typeSection();
}

public NameCustomSection nameSection() {
return useDebugNames ? module.nameSection() : null;
}

public int getId() {
return funcId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,8 @@ public static void CALL(Context ctx, CompilerInstruction ins, InstructionAdapter
asm,
ctx.classNameForFuncGroup(ctx.internalClassName(), funcId),
funcId,
functionType);
functionType,
ctx.nameSection());

if (ctx.needsTailCallCheck(funcId)) {
emitTailCallCheck(ctx, asm, functionType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package run.endive.compiler.internal;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static run.endive.compiler.internal.CompilerUtil.extractFuncId;
import static run.endive.compiler.internal.CompilerUtil.methodNameForFunc;
import static run.endive.compiler.internal.CompilerUtil.sanitizeWasmName;

import org.junit.jupiter.api.Test;

public class CompilerUtilTest {

@Test
public void methodNameWithoutNameSection() {
assertEquals("func_0", methodNameForFunc(0, null));
assertEquals("func_42", methodNameForFunc(42, null));
}

@Test
public void sanitizeReplacesIllegalChars() {
assertEquals("foo", sanitizeWasmName("foo"));
assertEquals("a_b_c", sanitizeWasmName("a.b/c"));
assertEquals("a_b_c_d_e_f", sanitizeWasmName("a.b;c[d<e>f"));
}

@Test
public void sanitizePreservesUnderscoresAndDashes() {
assertEquals("my_func", sanitizeWasmName("my_func"));
assertEquals("my-func", sanitizeWasmName("my-func"));
}

@Test
public void extractFuncIdFromSimpleName() {
assertEquals(0, extractFuncId("func_0"));
assertEquals(42, extractFuncId("func_42"));
}

@Test
public void extractFuncIdFromNamedMethod() {
assertEquals(0, extractFuncId("foo_0"));
assertEquals(5, extractFuncId("my_func_5"));
}

@Test
public void extractFuncIdReturnsNegativeForInvalid() {
assertEquals(-1, extractFuncId("nounderscore"));
assertEquals(-1, extractFuncId("func_abc"));
}
}
Loading
Loading