Wasm: initial skeleton implementation of DWARF generator

This commit is contained in:
Alexey Andreev 2022-11-14 17:28:12 +01:00
parent a543b91b84
commit c5011ebf69
15 changed files with 776 additions and 3 deletions

View File

@ -31,6 +31,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory; import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory;
@ -39,6 +40,7 @@ import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames; import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames;
import org.teavm.backend.lowlevel.transform.CoroutineTransformation; import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.generate.DwarfGenerator;
import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmDependencyListener;
import org.teavm.backend.wasm.generate.WasmGenerationContext; import org.teavm.backend.wasm.generate.WasmGenerationContext;
@ -76,6 +78,7 @@ import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactory;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic;
import org.teavm.backend.wasm.model.WasmCustomSection;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmMemorySegment;
@ -491,6 +494,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
classGenerator, stringPool, obfuscated); classGenerator, stringPool, obfuscated);
context.addIntrinsic(exceptionHandlingIntrinsic); context.addIntrinsic(exceptionHandlingIntrinsic);
var dwarfGenerator = debugging ? new DwarfGenerator() : null;
if (dwarfGenerator != null) {
dwarfGenerator.begin();
}
var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter, var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter,
asyncMethods::contains); asyncMethods::contains);
@ -539,7 +546,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
var writer = new WasmBinaryWriter(); var writer = new WasmBinaryWriter();
var renderer = new WasmBinaryRenderer(writer, version, obfuscated); var renderer = new WasmBinaryRenderer(writer, version, obfuscated);
renderer.render(module); renderer.render(module, buildDwarf(dwarfGenerator));
try (OutputStream output = buildTarget.createResource(outputName)) { try (OutputStream output = buildTarget.createResource(outputName)) {
output.write(writer.getData()); output.write(writer.getData());
@ -558,6 +565,16 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
} }
private Supplier<Collection<? extends WasmCustomSection>> buildDwarf(DwarfGenerator generator) {
if (generator == null) {
return null;
}
return () -> {
generator.end();
return generator.createSections();
};
}
private WasmFunction createStartFunction(NameProvider names) { private WasmFunction createStartFunction(NameProvider names) {
var function = new WasmFunction("teavm_start"); var function = new WasmFunction("teavm_start");
function.setExportName("start"); function.setExportName("start");

View File

@ -0,0 +1,33 @@
/*
* Copyright 2022 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.dwarf;
import java.util.function.Consumer;
import org.teavm.backend.wasm.dwarf.blob.Blob;
public class DwarfAbbreviation {
int tag;
boolean hasChildren;
Consumer<Blob> writer;
int index;
int count;
DwarfAbbreviation(int tag, boolean hasChildren, Consumer<Blob> writer) {
this.tag = tag;
this.hasChildren = hasChildren;
this.writer = writer;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2022 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.dwarf;
public final class DwarfConstants {
public static final int DWARF_VERSION = 5;
public static final int DW_UT_COMPILE = 0x01;
public static final int DW_TAG_COMPILE_UNIT = 0x11;
public static final int DW_CHILDREN_YES = 1;
public static final int DW_CHILDREN_NO = 0;
private DwarfConstants() {
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright 2022 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.dwarf;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_CHILDREN_NO;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_CHILDREN_YES;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import org.teavm.backend.wasm.dwarf.blob.BinaryDataConsumer;
import org.teavm.backend.wasm.dwarf.blob.Blob;
import org.teavm.backend.wasm.dwarf.blob.Marker;
public class DwarfInfoWriter {
private Blob output = new Blob();
private List<DwarfAbbreviation> abbreviations = new ArrayList<>();
private List<Placement> placements = new ArrayList<>();
private Marker placeholderMarker;
public DwarfInfoWriter write(byte[] data) {
output.write(data);
return this;
}
public DwarfInfoWriter write(byte[] data, int offset, int limit) {
output.write(data, offset, limit);
return this;
}
public DwarfInfoWriter writeInt(int value) {
output.writeInt(value);
return this;
}
public DwarfInfoWriter writeShort(int value) {
output.writeShort(value);
return this;
}
public DwarfInfoWriter writeByte(int value) {
output.write((byte) value);
return this;
}
public DwarfInfoWriter writeLEB(int value) {
output.writeLEB(value);
return this;
}
public DwarfAbbreviation abbreviation(int tag, boolean hasChildren, Consumer<Blob> blob) {
var abbr = new DwarfAbbreviation(tag, hasChildren, blob);
abbreviations.add(abbr);
return abbr;
}
public DwarfInfoWriter tag(DwarfAbbreviation abbreviation) {
placements.add(new Placement(output.ptr()) {
@Override
void write(Blob blob) {
blob.writeLEB(abbreviation.index);
}
});
abbreviation.count++;
return this;
}
public DwarfInfoWriter emptyTag() {
output.write((byte) 0);
return this;
}
public DwarfPlaceholder placeholder(int size) {
return new DwarfPlaceholder(size);
}
public DwarfInfoWriter ref(DwarfPlaceholder placeholder, DwarfPlaceholderWriter writer) {
placements.add(new Placement(output.ptr()) {
@Override
void write(Blob blob) {
if (placeholder.ptr >= 0) {
placeholderMarker.update();
writer.write(blob, placeholder.ptr);
placeholderMarker.rewind();
blob.skip(placeholder.size);
} else {
placeholder.addForwardRef(writer, blob.marker());
blob.skip(placeholder.size);
}
}
});
return this;
}
public DwarfInfoWriter mark(DwarfPlaceholder placeholder) {
placements.add(new Placement(output.ptr()) {
@Override
void write(Blob blob) {
if (placeholder.ptr >= 0) {
throw new IllegalStateException();
}
placeholder.ptr = blob.ptr();
if (placeholder.forwardReferences != null) {
placeholderMarker.update();
for (var forwardRef : placeholder.forwardReferences) {
forwardRef.marker.rewind();
forwardRef.writer.write(blob, placeholder.ptr);
}
placeholder.forwardReferences = null;
placeholderMarker.rewind();
}
}
});
return this;
}
public void buildAbbreviations(Blob target) {
var orderedAbbreviations = new ArrayList<>(abbreviations);
orderedAbbreviations.sort(Comparator.comparingInt(a -> -a.count));
var sz = orderedAbbreviations.size();
while (sz > 0 && orderedAbbreviations.get(sz - 1).count == 0) {
--sz;
}
for (var i = 0; i < sz; ++i) {
var abbrev = orderedAbbreviations.get(i);
abbrev.index = i + 1;
target.writeLEB(abbrev.index).writeLEB(abbrev.tag)
.writeByte(abbrev.hasChildren ? DW_CHILDREN_YES : DW_CHILDREN_NO);
abbrev.writer.accept(target);
target.writeByte(0).writeByte(0);
}
target.writeByte(0);
}
public void build(Blob target) {
placeholderMarker = target.marker();
this.targetBlob = target;
var reader = output.newReader(targetBlobWritingConsumer);
for (var placement : placements) {
reader.advance(placement.offset);
placement.write(target);
}
reader.advance(output.size());
this.targetBlob = null;
placeholderMarker = null;
}
private Blob targetBlob;
private BinaryDataConsumer targetBlobWritingConsumer = (data, offset, limit) ->
targetBlob.write(data, offset, limit);
private static abstract class Placement {
int offset;
Placement(int offset) {
this.offset = offset;
}
abstract void write(Blob blob);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2022 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.dwarf;
import java.util.ArrayList;
import java.util.List;
import org.teavm.backend.wasm.dwarf.blob.Marker;
public class DwarfPlaceholder {
int ptr = -1;
final int size;
List<ForwardRef> forwardReferences;
DwarfPlaceholder(int size) {
this.size = size;
}
void addForwardRef(DwarfPlaceholderWriter writer, Marker marker) {
if (forwardReferences == null) {
forwardReferences = new ArrayList<>();
forwardReferences.add(new ForwardRef(writer, marker));
}
}
static class ForwardRef {
final DwarfPlaceholderWriter writer;
final Marker marker;
ForwardRef(DwarfPlaceholderWriter writer, Marker marker) {
this.writer = writer;
this.marker = marker;
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2022 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.dwarf;
import org.teavm.backend.wasm.dwarf.blob.Blob;
public interface DwarfPlaceholderWriter {
void write(Blob blob, int actualValue);
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2022 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.dwarf.blob;
public interface BinaryDataConsumer {
void accept(byte[] data, int offset, int limit);
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2022 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.dwarf.blob;
import java.util.ArrayList;
import java.util.List;
public class Blob {
private byte[] buffer = new byte[16];
byte[] currentChunk = new byte[4096];
List<byte[]> data = new ArrayList<>();
int chunkIndex;
int posInChunk;
int ptr;
private int size;
public Blob() {
data.add(currentChunk);
}
public Blob write(byte[] bytes) {
return write(bytes, 0, bytes.length);
}
public Blob write(byte[] bytes, int offset, int limit) {
if (offset == limit) {
return this;
}
if (offset + 1 == limit) {
write(bytes[offset]);
return this;
}
while (offset < limit) {
var remaining = Math.min(limit - offset, currentChunk.length - posInChunk);
System.arraycopy(bytes, offset, currentChunk, posInChunk, remaining);
posInChunk += remaining;
offset += remaining;
ptr += remaining;
nextChunkIfNeeded();
}
size = Math.max(size, ptr);
return this;
}
public Blob skip(int count) {
while (count > 0) {
var remaining = Math.min(count, currentChunk.length - posInChunk);
posInChunk += remaining;
ptr += remaining;
count -= remaining;
nextChunkIfNeeded();
}
size = Math.max(size, ptr);
return this;
}
public Blob write(byte b) {
currentChunk[posInChunk++] = b;
ptr++;
nextChunkIfNeeded();
size = Math.max(size, ptr);
return this;
}
public Blob writeInt(int value) {
var buffer = this.buffer;
buffer[0] = (byte) value;
buffer[1] = (byte) (value >>> 8);
buffer[2] = (byte) (value >>> 16);
buffer[3] = (byte) (value >>> 24);
return write(buffer, 0, 4);
}
public Blob writeShort(int value) {
var buffer = this.buffer;
buffer[0] = (byte) value;
buffer[1] = (byte) (value >>> 8);
return write(buffer, 0, 2);
}
public Blob writeByte(int value) {
return write((byte) value);
}
public Blob writeLEB(int value) {
var ptr = 0;
var buffer = this.buffer;
while ((value & 0x7F) != value) {
buffer[ptr++] = (byte) ((value & 0x7F) | 0x80);
value >>= 7;
}
buffer[ptr++] = (byte) (value & 0x7F);
return write(buffer, 0, ptr);
}
private void nextChunkIfNeeded() {
if (posInChunk < currentChunk.length) {
return;
}
posInChunk = 0;
if (++chunkIndex >= data.size()) {
currentChunk = new byte[currentChunk.length];
data.add(currentChunk);
} else {
currentChunk = data.get(chunkIndex);
}
}
public int chunkCount() {
return data.size() + 1;
}
public BlobReader newReader(BinaryDataConsumer consumer) {
return new BlobReader(this, consumer);
}
public Marker marker() {
return new Marker(this, chunkIndex, posInChunk, ptr);
}
public byte[] chunkAt(int index) {
return index < data.size() ? data.get(index) : currentChunk;
}
public int ptr() {
return ptr;
}
public int size() {
return size;
}
public byte[] toArray() {
var result = new byte[size];
var ptr = 0;
for (var chunk : data) {
int bytesToCopy = Math.min(chunk.length, size - ptr);
if (bytesToCopy > 0) {
System.arraycopy(chunk, 0, result, ptr, bytesToCopy);
ptr += bytesToCopy;
}
}
return result;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2022 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.dwarf.blob;
public class BlobReader {
private Blob output;
private BinaryDataConsumer consumer;
private int ptr;
private int currentChunk;
private int offsetInChunk;
BlobReader(Blob output, BinaryDataConsumer consumer) {
this.output = output;
this.consumer = consumer;
}
public int position() {
return ptr;
}
public void advance(int to) {
if (to < ptr || to > output.size()) {
throw new IllegalArgumentException();
}
if (to == ptr) {
return;
}
var ptr = this.ptr;
var currentChunk = this.currentChunk;
var offsetInChunk = this.offsetInChunk;
while (ptr < to) {
var chunk = output.chunkAt(currentChunk);
var limit = Math.min(ptr + chunk.length, to);
var bytesToWrite = limit - ptr;
consumer.accept(chunk, offsetInChunk, offsetInChunk + bytesToWrite);
offsetInChunk += bytesToWrite;
ptr += bytesToWrite;
if (offsetInChunk == chunk.length) {
offsetInChunk = 0;
currentChunk++;
}
}
this.ptr = ptr;
this.currentChunk = currentChunk;
this.offsetInChunk = offsetInChunk;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2022 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.dwarf.blob;
public class Marker {
private Blob blob;
private int chunkIndex;
private int posInChunk;
private int ptr;
Marker(Blob blob, int chunkIndex, int posInChunk, int ptr) {
this.blob = blob;
this.chunkIndex = chunkIndex;
this.posInChunk = posInChunk;
this.ptr = ptr;
}
public void rewind() {
blob.chunkIndex = chunkIndex;
blob.posInChunk = posInChunk;
blob.ptr = ptr;
blob.currentChunk = blob.data.get(chunkIndex);
}
public void update() {
chunkIndex = blob.chunkIndex;
posInChunk = blob.posInChunk;
ptr = blob.ptr;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2022 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 static org.teavm.backend.wasm.dwarf.DwarfConstants.DWARF_VERSION;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_COMPILE_UNIT;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_UT_COMPILE;
import java.util.Arrays;
import java.util.Collection;
import org.teavm.backend.wasm.dwarf.DwarfInfoWriter;
import org.teavm.backend.wasm.dwarf.DwarfPlaceholder;
import org.teavm.backend.wasm.dwarf.blob.Blob;
import org.teavm.backend.wasm.model.WasmCustomSection;
public class DwarfGenerator {
private DwarfInfoWriter infoWriter = new DwarfInfoWriter();
private DwarfPlaceholder endOfSection;
public void begin() {
endOfSection = infoWriter.placeholder(4);
emitUnitHeader();
compilationUnit();
}
private void emitUnitHeader() {
// unit_length
infoWriter.ref(endOfSection, (blob, ptr) -> {
int size = ptr - blob.ptr() - 4;
blob.writeInt(size);
});
// version
infoWriter.writeShort(DWARF_VERSION);
// unit_type
infoWriter.writeByte(DW_UT_COMPILE);
// address_size
infoWriter.writeByte(4);
// debug_abbrev_offset
infoWriter.writeInt(0);
}
private void compilationUnit() {
infoWriter.tag(infoWriter.abbreviation(DW_TAG_COMPILE_UNIT, true, data -> { }));
}
public void end() {
closeTag(); // compilation unit
infoWriter.mark(endOfSection);
}
private void closeTag() {
infoWriter.writeByte(0);
}
public Collection<? extends WasmCustomSection> createSections() {
var abbreviations = new Blob();
infoWriter.buildAbbreviations(abbreviations);
var info = new Blob();
infoWriter.build(info);
return Arrays.asList(
new WasmCustomSection(".debug_abbrev", abbreviations.toArray()),
new WasmCustomSection(".debug_info", info.toArray())
);
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2022 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.model;
public class WasmCustomSection {
private String name;
private byte[] data;
WasmModule module;
public WasmCustomSection(String name, byte[] data) {
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public byte[] getData() {
return data;
}
}

View File

@ -29,6 +29,8 @@ public class WasmModule {
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions); private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
private List<WasmFunction> functionTable = new ArrayList<>(); private List<WasmFunction> functionTable = new ArrayList<>();
private WasmFunction startFunction; private WasmFunction startFunction;
private Map<String, WasmCustomSection> customSections = new LinkedHashMap<>();
private Map<String, WasmCustomSection> readonlyCustomSections = Collections.unmodifiableMap(customSections);
public void add(WasmFunction function) { public void add(WasmFunction function) {
if (functions.containsKey(function.getName())) { if (functions.containsKey(function.getName())) {
@ -53,6 +55,30 @@ public class WasmModule {
return readonlyFunctions; return readonlyFunctions;
} }
public void add(WasmCustomSection customSection) {
if (customSections.containsKey(customSection.getName())) {
throw new IllegalArgumentException("Custom section " + customSection.getName()
+ " already defined in this module");
}
if (customSection.module != null) {
throw new IllegalArgumentException("Given custom section is already registered in another module");
}
customSections.put(customSection.getName(), customSection);
customSection.module = this;
}
public void remove(WasmCustomSection customSection) {
if (customSection.module != this) {
return;
}
customSection.module = null;
customSections.remove(customSection.getName());
}
public Map<? extends String, ? extends WasmCustomSection> getCustomSections() {
return readonlyCustomSections;
}
public List<WasmFunction> getFunctionTable() { public List<WasmFunction> getFunctionTable() {
return functionTable; return functionTable;
} }

View File

@ -17,10 +17,13 @@ package org.teavm.backend.wasm.render;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.backend.wasm.model.WasmCustomSection;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmMemorySegment;
@ -58,6 +61,10 @@ public class WasmBinaryRenderer {
} }
public void render(WasmModule module) { public void render(WasmModule module) {
render(module, Collections::emptyList);
}
public void render(WasmModule module, Supplier<Collection<? extends WasmCustomSection>> customSectionSupplier) {
output.writeInt32(0x6d736100); output.writeInt32(0x6d736100);
switch (version) { switch (version) {
case V_0x1: case V_0x1:
@ -78,6 +85,7 @@ public class WasmBinaryRenderer {
if (!obfuscated) { if (!obfuscated) {
renderNames(module); renderNames(module);
} }
renderCustomSections(module, customSectionSupplier);
} }
private void renderSignatures(WasmModule module) { private void renderSignatures(WasmModule module) {
@ -354,6 +362,22 @@ public class WasmBinaryRenderer {
writeSection(SECTION_UNKNOWN, "name", section.getData()); writeSection(SECTION_UNKNOWN, "name", section.getData());
} }
private void renderCustomSections(WasmModule module,
Supplier<Collection<? extends WasmCustomSection>> sectionSupplier) {
for (var customSection : module.getCustomSections().values()) {
renderCustomSection(customSection);
}
if (sectionSupplier != null) {
for (var customSection : sectionSupplier.get()) {
renderCustomSection(customSection);
}
}
}
private void renderCustomSection(WasmCustomSection customSection) {
writeSection(SECTION_UNKNOWN, customSection.getName(), customSection.getData());
}
static class LocalEntry { static class LocalEntry {
WasmType type; WasmType type;
int count = 1; int count = 1;

View File

@ -31,9 +31,9 @@ TeaVM.wasm = function() {
lineBuffer += String.fromCharCode(charCode); lineBuffer += String.fromCharCode(charCode);
} }
} }
function putwchars(buffer, count) { function putwchars(controller, buffer, count) {
let instance = controller.instance; let instance = controller.instance;
let memory = instance.exports.memory.buffer; let memory = new Int8Array(instance.exports.memory.buffer);
for (let i = 0; i < count; ++i) { for (let i = 0; i < count; ++i) {
// TODO: support UTF-8 // TODO: support UTF-8
putwchar(memory[buffer++]); putwchar(memory[buffer++]);