mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Wasm: initial skeleton implementation of DWARF generator
This commit is contained in:
parent
a543b91b84
commit
c5011ebf69
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
158
core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java
Normal file
158
core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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++]);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user