mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Wasm: support class layout in debug information
This commit is contained in:
parent
506a9bd8c5
commit
7b3905246b
|
@ -552,6 +552,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
|
|
||||||
var writer = new WasmBinaryWriter();
|
var writer = new WasmBinaryWriter();
|
||||||
var debugBuilder = debugging ? new DebugInfoBuilder() : null;
|
var debugBuilder = debugging ? new DebugInfoBuilder() : null;
|
||||||
|
if (debugBuilder != null) {
|
||||||
|
classGenerator.writeDebug(debugBuilder.classLayout());
|
||||||
|
}
|
||||||
var renderer = new WasmBinaryRenderer(
|
var renderer = new WasmBinaryRenderer(
|
||||||
writer, version, obfuscated, dwarfGenerator, dwarfClassGen,
|
writer, version, obfuscated, dwarfGenerator, dwarfClassGen,
|
||||||
debugBuilder != null ? debugBuilder.lines() : null,
|
debugBuilder != null ? debugBuilder.lines() : null,
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.debug;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.debug.info.FieldType;
|
||||||
|
import org.teavm.model.PrimitiveType;
|
||||||
|
|
||||||
|
public interface DebugClassLayout {
|
||||||
|
void startClass(String name, int parent, int address, int size);
|
||||||
|
|
||||||
|
void instanceField(String name, int offset, FieldType type);
|
||||||
|
|
||||||
|
void staticField(String name, int offset, FieldType type);
|
||||||
|
|
||||||
|
void endClass();
|
||||||
|
|
||||||
|
void writeInterface(String name, int address);
|
||||||
|
|
||||||
|
void writePrimitive(PrimitiveType type, int address);
|
||||||
|
|
||||||
|
void writeArray(int itemType, int address);
|
||||||
|
|
||||||
|
void writeUnknown(int address);
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* 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.debug;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.debug.info.FieldType;
|
||||||
|
import org.teavm.model.PrimitiveType;
|
||||||
|
|
||||||
|
public class DebugClassLayoutBuilder extends DebugSectionBuilder implements DebugClassLayout {
|
||||||
|
private DebugClasses classes;
|
||||||
|
private DebugStrings strings;
|
||||||
|
private int currentIndex;
|
||||||
|
private int currentAddress;
|
||||||
|
private ClassPhase phase = ClassPhase.NO_CLASS;
|
||||||
|
private int lastFieldOffset;
|
||||||
|
|
||||||
|
public DebugClassLayoutBuilder(DebugClasses classes, DebugStrings strings) {
|
||||||
|
super(DebugConstants.SECTION_CLASS_LAYOUT);
|
||||||
|
this.classes = classes;
|
||||||
|
this.strings = strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startClass(String name, int parent, int address, int size) {
|
||||||
|
blob.writeByte(parent >= 0 ? DebugConstants.CLASS_CLASS : DebugConstants.CLASS_ROOT);
|
||||||
|
blob.writeLEB(classes.classPtr(name));
|
||||||
|
if (parent >= 0) {
|
||||||
|
blob.writeSLEB(currentIndex - parent);
|
||||||
|
}
|
||||||
|
writeAddress(address);
|
||||||
|
blob.writeLEB(size);
|
||||||
|
lastFieldOffset = 0;
|
||||||
|
phase = ClassPhase.STATIC_FIELDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void instanceField(String name, int offset, FieldType type) {
|
||||||
|
if (phase == ClassPhase.STATIC_FIELDS) {
|
||||||
|
blob.writeByte(DebugConstants.FIELD_END_SEQUENCE);
|
||||||
|
lastFieldOffset = 0;
|
||||||
|
phase = ClassPhase.INSTANCE_FIELDS;
|
||||||
|
}
|
||||||
|
writeFieldType(type);
|
||||||
|
blob.writeLEB(strings.stringPtr(name));
|
||||||
|
blob.writeSLEB(offset - lastFieldOffset);
|
||||||
|
lastFieldOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void staticField(String name, int offset, FieldType type) {
|
||||||
|
writeFieldType(type);
|
||||||
|
blob.writeLEB(strings.stringPtr(name));
|
||||||
|
blob.writeSLEB(offset - lastFieldOffset);
|
||||||
|
lastFieldOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFieldType(FieldType type) {
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_BOOLEAN);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_BYTE);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_SHORT);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_CHAR);
|
||||||
|
break;
|
||||||
|
case INT:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_INT);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_LONG);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_FLOAT);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_DOUBLE);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_OBJECT);
|
||||||
|
break;
|
||||||
|
case ADDRESS:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_ADDRESS);
|
||||||
|
break;
|
||||||
|
case UNDEFINED:
|
||||||
|
blob.writeByte(DebugConstants.FIELD_UNDEFINED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endClass() {
|
||||||
|
if (phase == ClassPhase.STATIC_FIELDS) {
|
||||||
|
blob.writeByte(DebugConstants.FIELD_END);
|
||||||
|
}
|
||||||
|
if (phase == ClassPhase.INSTANCE_FIELDS) {
|
||||||
|
blob.writeByte(DebugConstants.FIELD_END_SEQUENCE);
|
||||||
|
}
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeInterface(String name, int address) {
|
||||||
|
blob.writeByte(DebugConstants.CLASS_INTERFACE);
|
||||||
|
blob.writeLEB(classes.classPtr(name));
|
||||||
|
writeAddress(address);
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writePrimitive(PrimitiveType type, int address) {
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_BOOLEAN);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_BYTE);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_SHORT);
|
||||||
|
break;
|
||||||
|
case CHARACTER:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_CHAR);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_INT);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_LONG);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_FLOAT);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
blob.writeLEB(DebugConstants.CLASS_DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
writeAddress(address);
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeArray(int itemType, int address) {
|
||||||
|
blob.writeByte(DebugConstants.CLASS_ARRAY);
|
||||||
|
blob.writeSLEB(currentIndex - itemType);
|
||||||
|
writeAddress(address);
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeUnknown(int address) {
|
||||||
|
blob.writeByte(DebugConstants.CLASS_UNKNOWN);
|
||||||
|
writeAddress(address);
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeAddress(int address) {
|
||||||
|
blob.writeSLEB(address - currentAddress);
|
||||||
|
currentAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ClassPhase {
|
||||||
|
NO_CLASS,
|
||||||
|
STATIC_FIELDS,
|
||||||
|
INSTANCE_FIELDS
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,10 +26,39 @@ public final class DebugConstants {
|
||||||
public static final int LOC_PTR = 4;
|
public static final int LOC_PTR = 4;
|
||||||
public static final int LOC_USER = 10;
|
public static final int LOC_USER = 10;
|
||||||
|
|
||||||
|
public static final int CLASS_ROOT = 0;
|
||||||
|
public static final int CLASS_CLASS = 1;
|
||||||
|
public static final int CLASS_INTERFACE = 2;
|
||||||
|
public static final int CLASS_ARRAY = 3;
|
||||||
|
public static final int CLASS_BOOLEAN = 4;
|
||||||
|
public static final int CLASS_BYTE = 5;
|
||||||
|
public static final int CLASS_SHORT = 6;
|
||||||
|
public static final int CLASS_CHAR = 7;
|
||||||
|
public static final int CLASS_INT = 8;
|
||||||
|
public static final int CLASS_LONG = 9;
|
||||||
|
public static final int CLASS_FLOAT = 10;
|
||||||
|
public static final int CLASS_DOUBLE = 11;
|
||||||
|
public static final int CLASS_UNKNOWN = 12;
|
||||||
|
|
||||||
|
public static final int FIELD_END = 0;
|
||||||
|
public static final int FIELD_END_SEQUENCE = 1;
|
||||||
|
public static final int FIELD_BOOLEAN = 2;
|
||||||
|
public static final int FIELD_BYTE = 3;
|
||||||
|
public static final int FIELD_SHORT = 4;
|
||||||
|
public static final int FIELD_CHAR = 5;
|
||||||
|
public static final int FIELD_INT = 6;
|
||||||
|
public static final int FIELD_LONG = 7;
|
||||||
|
public static final int FIELD_FLOAT = 8;
|
||||||
|
public static final int FIELD_DOUBLE = 9;
|
||||||
|
public static final int FIELD_OBJECT = 10;
|
||||||
|
public static final int FIELD_ADDRESS = 11;
|
||||||
|
public static final int FIELD_UNDEFINED = 12;
|
||||||
|
|
||||||
public static final String SECTION_STRINGS = "teavm_str";
|
public static final String SECTION_STRINGS = "teavm_str";
|
||||||
public static final String SECTION_FILES = "teavm_file";
|
public static final String SECTION_FILES = "teavm_file";
|
||||||
public static final String SECTION_PACKAGES = "teavm_pkg";
|
public static final String SECTION_PACKAGES = "teavm_pkg";
|
||||||
public static final String SECTION_CLASSES = "teavm_cls";
|
public static final String SECTION_CLASSES = "teavm_cls";
|
||||||
|
public static final String SECTION_CLASS_LAYOUT = "teavm_cll";
|
||||||
public static final String SECTION_METHODS = "teavm_mtd";
|
public static final String SECTION_METHODS = "teavm_mtd";
|
||||||
public static final String SECTION_LINES = "teavm_line";
|
public static final String SECTION_LINES = "teavm_line";
|
||||||
public static final String SECTION_VARIABLES = "teavm_var";
|
public static final String SECTION_VARIABLES = "teavm_var";
|
||||||
|
|
|
@ -27,6 +27,7 @@ public class DebugInfoBuilder {
|
||||||
private DebugMethodsBuilder methods;
|
private DebugMethodsBuilder methods;
|
||||||
private DebugVariablesBuilder variables;
|
private DebugVariablesBuilder variables;
|
||||||
private DebugLinesBuilder lines;
|
private DebugLinesBuilder lines;
|
||||||
|
private DebugClassLayoutBuilder classLayout;
|
||||||
|
|
||||||
public DebugInfoBuilder() {
|
public DebugInfoBuilder() {
|
||||||
strings = new DebugStringsBuilder();
|
strings = new DebugStringsBuilder();
|
||||||
|
@ -36,6 +37,7 @@ public class DebugInfoBuilder {
|
||||||
methods = new DebugMethodsBuilder(classes, strings);
|
methods = new DebugMethodsBuilder(classes, strings);
|
||||||
variables = new DebugVariablesBuilder(strings);
|
variables = new DebugVariablesBuilder(strings);
|
||||||
lines = new DebugLinesBuilder(files, methods);
|
lines = new DebugLinesBuilder(files, methods);
|
||||||
|
classLayout = new DebugClassLayoutBuilder(classes, strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugStrings strings() {
|
public DebugStrings strings() {
|
||||||
|
@ -66,6 +68,10 @@ public class DebugInfoBuilder {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DebugClassLayout classLayout() {
|
||||||
|
return classLayout;
|
||||||
|
}
|
||||||
|
|
||||||
public List<WasmCustomSection> build() {
|
public List<WasmCustomSection> build() {
|
||||||
var result = new ArrayList<WasmCustomSection>();
|
var result = new ArrayList<WasmCustomSection>();
|
||||||
addSection(result, strings);
|
addSection(result, strings);
|
||||||
|
@ -75,6 +81,7 @@ public class DebugInfoBuilder {
|
||||||
addSection(result, methods);
|
addSection(result, methods);
|
||||||
addSection(result, variables);
|
addSection(result, variables);
|
||||||
addSection(result, lines);
|
addSection(result, lines);
|
||||||
|
addSection(result, classLayout);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public interface ArrayLayout extends TypeLayout {
|
||||||
|
TypeLayout elementType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default TypeLayoutKind kind() {
|
||||||
|
return TypeLayoutKind.ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.debug.info;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface ClassLayout extends TypeLayout {
|
||||||
|
ClassInfo classRef();
|
||||||
|
|
||||||
|
ClassLayout superclass();
|
||||||
|
|
||||||
|
Collection<? extends FieldInfo> instanceFields();
|
||||||
|
|
||||||
|
Collection<? extends FieldInfo> staticFields();
|
||||||
|
|
||||||
|
int size();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default TypeLayoutKind kind() {
|
||||||
|
return TypeLayoutKind.CLASS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntObjectHashMap;
|
||||||
|
import com.carrotsearch.hppc.IntObjectMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class ClassLayoutInfo {
|
||||||
|
private IntObjectMap<TypeLayout> layoutByAddress;
|
||||||
|
|
||||||
|
public abstract List<? extends TypeLayout> types();
|
||||||
|
|
||||||
|
public TypeLayout find(int address) {
|
||||||
|
if (layoutByAddress == null) {
|
||||||
|
layoutByAddress = new IntObjectHashMap<>();
|
||||||
|
for (var typeLayout : types()) {
|
||||||
|
layoutByAddress.put(typeLayout.address(), typeLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return layoutByAddress.get(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dump(PrintStream out) {
|
||||||
|
var indexes = new ObjectIntHashMap<TypeLayout>();
|
||||||
|
for (var i = 0; i < types().size(); ++i) {
|
||||||
|
indexes.put(types().get(i), i);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < types().size(); ++i) {
|
||||||
|
out.print("#" + i + ": ");
|
||||||
|
var type = types().get(i);
|
||||||
|
out.println(type.kind().name().toLowerCase());
|
||||||
|
out.println(" address: " + Integer.toHexString(type.address()));
|
||||||
|
switch (type.kind()) {
|
||||||
|
case CLASS:
|
||||||
|
dumpClass(out, indexes, (ClassLayout) type);
|
||||||
|
break;
|
||||||
|
case INTERFACE:
|
||||||
|
dumpInterface(out, (InterfaceLayout) type);
|
||||||
|
break;
|
||||||
|
case ARRAY:
|
||||||
|
dumpArray(out, indexes, (ArrayLayout) type);
|
||||||
|
break;
|
||||||
|
case PRIMITIVE:
|
||||||
|
dumpPrimitive(out, (PrimitiveLayout) type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpClass(PrintStream out, ObjectIntMap<TypeLayout> indexes, ClassLayout cls) {
|
||||||
|
out.println(" name: " + cls.classRef().fullName());
|
||||||
|
out.println(" size: " + cls.size());
|
||||||
|
if (cls.superclass() != null) {
|
||||||
|
out.println(" superclass: #" + indexes.get(cls.superclass()));
|
||||||
|
}
|
||||||
|
if (!cls.staticFields().isEmpty()) {
|
||||||
|
out.println(" static fields:");
|
||||||
|
dumpFields(out, cls.staticFields());
|
||||||
|
}
|
||||||
|
if (!cls.instanceFields().isEmpty()) {
|
||||||
|
out.println(" instance fields:");
|
||||||
|
dumpFields(out, cls.instanceFields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpFields(PrintStream out, Collection<? extends FieldInfo> fields) {
|
||||||
|
for (var field : fields) {
|
||||||
|
out.println(" " + field.name() + ": ");
|
||||||
|
out.println(" offset: " + Integer.toHexString(field.address()));
|
||||||
|
out.println(" type: " + field.type().name().toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpInterface(PrintStream out, InterfaceLayout cls) {
|
||||||
|
out.println(" name: " + cls.classRef().fullName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpArray(PrintStream out, ObjectIntMap<TypeLayout> indexes, ArrayLayout array) {
|
||||||
|
out.println(" element: #" + indexes.get(array.elementType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpPrimitive(PrintStream out, PrimitiveLayout primitive) {
|
||||||
|
out.println(" primitive type: " + primitive.primitiveType().name().toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,12 +21,15 @@ public class DebugInfo {
|
||||||
private VariablesInfo variables;
|
private VariablesInfo variables;
|
||||||
private LineInfo lines;
|
private LineInfo lines;
|
||||||
private ControlFlowInfo controlFlow;
|
private ControlFlowInfo controlFlow;
|
||||||
|
private ClassLayoutInfo classLayoutInfo;
|
||||||
private int offset;
|
private int offset;
|
||||||
|
|
||||||
public DebugInfo(VariablesInfo variables, LineInfo lines, ControlFlowInfo controlFlow, int offset) {
|
public DebugInfo(VariablesInfo variables, LineInfo lines, ControlFlowInfo controlFlow,
|
||||||
|
ClassLayoutInfo classLayoutInfo, int offset) {
|
||||||
this.variables = variables;
|
this.variables = variables;
|
||||||
this.lines = lines;
|
this.lines = lines;
|
||||||
this.controlFlow = controlFlow;
|
this.controlFlow = controlFlow;
|
||||||
|
this.classLayoutInfo = classLayoutInfo;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +49,10 @@ public class DebugInfo {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassLayoutInfo classLayoutInfo() {
|
||||||
|
return classLayoutInfo;
|
||||||
|
}
|
||||||
|
|
||||||
public void dump(PrintStream out) {
|
public void dump(PrintStream out) {
|
||||||
if (offset != 0) {
|
if (offset != 0) {
|
||||||
out.println("Code section offset: " + Integer.toHexString(offset));
|
out.println("Code section offset: " + Integer.toHexString(offset));
|
||||||
|
@ -62,5 +69,9 @@ public class DebugInfo {
|
||||||
out.println("VARIABLES");
|
out.println("VARIABLES");
|
||||||
variables.dump(out);
|
variables.dump(out);
|
||||||
}
|
}
|
||||||
|
if (classLayoutInfo != null) {
|
||||||
|
out.println("CLASS LAYOUT:");
|
||||||
|
classLayoutInfo.dump(out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class FieldInfo {
|
||||||
|
public abstract int address();
|
||||||
|
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public abstract FieldType type();
|
||||||
|
}
|
|
@ -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.debug.info;
|
||||||
|
|
||||||
|
public enum FieldType {
|
||||||
|
BOOLEAN,
|
||||||
|
BYTE,
|
||||||
|
SHORT,
|
||||||
|
CHAR,
|
||||||
|
INT,
|
||||||
|
LONG,
|
||||||
|
FLOAT,
|
||||||
|
DOUBLE,
|
||||||
|
OBJECT,
|
||||||
|
ADDRESS,
|
||||||
|
UNDEFINED
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public interface InterfaceLayout extends TypeLayout {
|
||||||
|
ClassInfo classRef();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default TypeLayoutKind kind() {
|
||||||
|
return TypeLayoutKind.INTERFACE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
import org.teavm.model.PrimitiveType;
|
||||||
|
|
||||||
|
public interface PrimitiveLayout extends TypeLayout {
|
||||||
|
PrimitiveType primitiveType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default TypeLayoutKind kind() {
|
||||||
|
return TypeLayoutKind.PRIMITIVE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.debug.info;
|
||||||
|
|
||||||
|
public interface TypeLayout {
|
||||||
|
TypeLayoutKind kind();
|
||||||
|
|
||||||
|
int address();
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public enum TypeLayoutKind {
|
||||||
|
CLASS,
|
||||||
|
INTERFACE,
|
||||||
|
ARRAY,
|
||||||
|
PRIMITIVE,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public interface UnknownLayout extends TypeLayout {
|
||||||
|
@Override
|
||||||
|
default TypeLayoutKind kind() {
|
||||||
|
return TypeLayoutKind.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,400 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ArrayLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ClassInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ClassLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ClassLayoutInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.FieldInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.FieldType;
|
||||||
|
import org.teavm.backend.wasm.debug.info.InterfaceLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.PrimitiveLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.TypeLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.UnknownLayout;
|
||||||
|
import org.teavm.model.PrimitiveType;
|
||||||
|
|
||||||
|
public class DebugClassLayoutParser extends DebugSectionParser {
|
||||||
|
private DebugStringParser strings;
|
||||||
|
private DebugClassParser classes;
|
||||||
|
private ArrayList<TypeLayoutImpl> types = new ArrayList<>();
|
||||||
|
private List<List<Consumer<TypeLayoutImpl>>> forwardReferences = new ArrayList<>();
|
||||||
|
private int lastAddress;
|
||||||
|
private int lastFieldOffset;
|
||||||
|
private ClassLayoutInfoImpl classLayoutInfo;
|
||||||
|
|
||||||
|
public DebugClassLayoutParser(DebugStringParser strings, DebugClassParser classes) {
|
||||||
|
super(DebugConstants.SECTION_CLASS_LAYOUT, strings, classes);
|
||||||
|
this.strings = strings;
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
while (ptr < data.length) {
|
||||||
|
var kind = data[ptr++];
|
||||||
|
switch (kind) {
|
||||||
|
case DebugConstants.CLASS_ROOT:
|
||||||
|
parseRootClass();
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_CLASS:
|
||||||
|
parseClass();
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_INTERFACE:
|
||||||
|
parseInterface();
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_ARRAY:
|
||||||
|
parseArray();
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_BOOLEAN:
|
||||||
|
parsePrimitive(PrimitiveType.BOOLEAN);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_BYTE:
|
||||||
|
parsePrimitive(PrimitiveType.BYTE);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_SHORT:
|
||||||
|
parsePrimitive(PrimitiveType.SHORT);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_CHAR:
|
||||||
|
parsePrimitive(PrimitiveType.CHARACTER);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_INT:
|
||||||
|
parsePrimitive(PrimitiveType.INTEGER);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_LONG:
|
||||||
|
parsePrimitive(PrimitiveType.LONG);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_FLOAT:
|
||||||
|
parsePrimitive(PrimitiveType.FLOAT);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_DOUBLE:
|
||||||
|
parsePrimitive(PrimitiveType.DOUBLE);
|
||||||
|
break;
|
||||||
|
case DebugConstants.CLASS_UNKNOWN:
|
||||||
|
parseUnknown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types.trimToSize();
|
||||||
|
classLayoutInfo = new ClassLayoutInfoImpl(Collections.unmodifiableList(types));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassLayoutInfo getInfo() {
|
||||||
|
return classLayoutInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseRootClass() {
|
||||||
|
var classPtr = classes.getClass(readLEB());
|
||||||
|
var address = readAddress();
|
||||||
|
var size = readLEB();
|
||||||
|
var type = new ClassLayoutImpl(address, classPtr, size);
|
||||||
|
addType(type);
|
||||||
|
parseClassFields(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseClass() {
|
||||||
|
var classPtr = classes.getClass(readLEB());
|
||||||
|
var superclassIndex = types.size() - readSignedLEB();
|
||||||
|
var address = readAddress();
|
||||||
|
var size = readLEB();
|
||||||
|
var type = new ClassLayoutImpl(address, classPtr, size);
|
||||||
|
addType(type);
|
||||||
|
ref(superclassIndex, superclass -> type.superclass = (ClassLayoutImpl) superclass);
|
||||||
|
parseClassFields(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseClassFields(ClassLayoutImpl type) {
|
||||||
|
lastFieldOffset = 0;
|
||||||
|
var staticFields = new ArrayList<FieldInfoImpl>();
|
||||||
|
while (true) {
|
||||||
|
var fieldType = data[ptr++];
|
||||||
|
if (fieldType == DebugConstants.FIELD_END) {
|
||||||
|
staticFields.trimToSize();
|
||||||
|
type.staticFields = staticFields.isEmpty() ? Collections.emptyList()
|
||||||
|
: Collections.unmodifiableList(staticFields);
|
||||||
|
type.instanceFields = Collections.emptyList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fieldType == DebugConstants.FIELD_END_SEQUENCE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
staticFields.add(parseField(fieldType));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFieldOffset = 0;
|
||||||
|
var instanceFields = new ArrayList<FieldInfoImpl>();
|
||||||
|
while (true) {
|
||||||
|
var fieldType = data[ptr++];
|
||||||
|
if (fieldType == DebugConstants.FIELD_END || fieldType == DebugConstants.FIELD_END_SEQUENCE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instanceFields.add(parseField(fieldType));
|
||||||
|
}
|
||||||
|
|
||||||
|
staticFields.trimToSize();
|
||||||
|
instanceFields.trimToSize();
|
||||||
|
type.staticFields = staticFields.isEmpty() ? Collections.emptyList()
|
||||||
|
: Collections.unmodifiableList(staticFields);
|
||||||
|
type.instanceFields = instanceFields.isEmpty() ? Collections.emptyList()
|
||||||
|
: Collections.unmodifiableList(instanceFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldInfoImpl parseField(byte type) {
|
||||||
|
FieldType fieldType;
|
||||||
|
switch (type) {
|
||||||
|
case DebugConstants.FIELD_BOOLEAN:
|
||||||
|
fieldType = FieldType.BOOLEAN;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_BYTE:
|
||||||
|
fieldType = FieldType.BYTE;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_SHORT:
|
||||||
|
fieldType = FieldType.SHORT;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_CHAR:
|
||||||
|
fieldType = FieldType.CHAR;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_INT:
|
||||||
|
fieldType = FieldType.INT;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_LONG:
|
||||||
|
fieldType = FieldType.LONG;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_FLOAT:
|
||||||
|
fieldType = FieldType.FLOAT;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_DOUBLE:
|
||||||
|
fieldType = FieldType.DOUBLE;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_OBJECT:
|
||||||
|
fieldType = FieldType.OBJECT;
|
||||||
|
break;
|
||||||
|
case DebugConstants.FIELD_ADDRESS:
|
||||||
|
fieldType = FieldType.ADDRESS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fieldType = FieldType.UNDEFINED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var name = strings.getString(readLEB());
|
||||||
|
var offset = lastFieldOffset + readSignedLEB();
|
||||||
|
lastFieldOffset = offset;
|
||||||
|
return new FieldInfoImpl(offset, name, fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseInterface() {
|
||||||
|
var classRef = classes.getClass(readLEB());
|
||||||
|
var address = readAddress();
|
||||||
|
addType(new InterfaceLayoutImpl(address, classRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseArray() {
|
||||||
|
var elementPtr = types.size() - readSignedLEB();
|
||||||
|
var address = readAddress();
|
||||||
|
var type = new ArrayLayoutImpl(address);
|
||||||
|
addType(type);
|
||||||
|
ref(elementPtr, element -> type.elementType = element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parsePrimitive(PrimitiveType primitiveType) {
|
||||||
|
var address = readAddress();
|
||||||
|
addType(new PrimitiveLayoutImpl(address, primitiveType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseUnknown() {
|
||||||
|
var address = readAddress();
|
||||||
|
addType(new UnknownLayoutImpl(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readAddress() {
|
||||||
|
var result = readSignedLEB() + lastAddress;
|
||||||
|
lastAddress = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addType(TypeLayoutImpl type) {
|
||||||
|
var index = types.size();
|
||||||
|
types.add(type);
|
||||||
|
if (index < forwardReferences.size()) {
|
||||||
|
var refs = forwardReferences.get(index);
|
||||||
|
forwardReferences.set(index, null);
|
||||||
|
for (var ref : refs) {
|
||||||
|
ref.accept(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ref(int index, Consumer<TypeLayoutImpl> handler) {
|
||||||
|
if (index < types.size()) {
|
||||||
|
handler.accept(types.get(index));
|
||||||
|
} else {
|
||||||
|
if (index >= forwardReferences.size()) {
|
||||||
|
forwardReferences.addAll(Collections.nCopies(index + 1 - forwardReferences.size(), null));
|
||||||
|
}
|
||||||
|
var refs = forwardReferences.get(index);
|
||||||
|
if (refs == null) {
|
||||||
|
refs = new ArrayList<>();
|
||||||
|
forwardReferences.set(index, refs);
|
||||||
|
}
|
||||||
|
refs.add(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static abstract class TypeLayoutImpl implements TypeLayout {
|
||||||
|
private int address;
|
||||||
|
|
||||||
|
private TypeLayoutImpl(int address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int address() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassLayoutImpl extends TypeLayoutImpl implements ClassLayout {
|
||||||
|
private ClassInfo classRef;
|
||||||
|
private List<? extends FieldInfoImpl> instanceFields;
|
||||||
|
private List<? extends FieldInfoImpl> staticFields;
|
||||||
|
private ClassLayoutImpl superclass;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
ClassLayoutImpl(int address, ClassInfo classRef, int size) {
|
||||||
|
super(address);
|
||||||
|
this.classRef = classRef;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassInfo classRef() {
|
||||||
|
return classRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLayout superclass() {
|
||||||
|
return superclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends FieldInfo> instanceFields() {
|
||||||
|
return instanceFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends FieldInfo> staticFields() {
|
||||||
|
return staticFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldInfoImpl extends FieldInfo {
|
||||||
|
private int address;
|
||||||
|
private String name;
|
||||||
|
private FieldType type;
|
||||||
|
|
||||||
|
FieldInfoImpl(int address, String name, FieldType type) {
|
||||||
|
this.address = address;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int address() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldType type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InterfaceLayoutImpl extends TypeLayoutImpl implements InterfaceLayout {
|
||||||
|
private ClassInfo classRef;
|
||||||
|
|
||||||
|
InterfaceLayoutImpl(int address, ClassInfo classRef) {
|
||||||
|
super(address);
|
||||||
|
this.classRef = classRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassInfo classRef() {
|
||||||
|
return classRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ArrayLayoutImpl extends TypeLayoutImpl implements ArrayLayout {
|
||||||
|
private TypeLayoutImpl elementType;
|
||||||
|
|
||||||
|
ArrayLayoutImpl(int address) {
|
||||||
|
super(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeLayout elementType() {
|
||||||
|
return elementType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PrimitiveLayoutImpl extends TypeLayoutImpl implements PrimitiveLayout {
|
||||||
|
private PrimitiveType primitiveType;
|
||||||
|
|
||||||
|
PrimitiveLayoutImpl(int address, PrimitiveType primitiveType) {
|
||||||
|
super(address);
|
||||||
|
this.primitiveType = primitiveType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrimitiveType primitiveType() {
|
||||||
|
return primitiveType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UnknownLayoutImpl extends TypeLayoutImpl implements UnknownLayout {
|
||||||
|
UnknownLayoutImpl(int address) {
|
||||||
|
super(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassLayoutInfoImpl extends ClassLayoutInfo {
|
||||||
|
private List<TypeLayoutImpl> types;
|
||||||
|
|
||||||
|
ClassLayoutInfoImpl(List<TypeLayoutImpl> types) {
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends TypeLayout> types() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ public class DebugInfoParser extends ModuleParser {
|
||||||
private DebugLinesParser lines;
|
private DebugLinesParser lines;
|
||||||
private DebugVariablesParser variables;
|
private DebugVariablesParser variables;
|
||||||
private ControlFlowInfo controlFlow;
|
private ControlFlowInfo controlFlow;
|
||||||
|
private DebugClassLayoutParser classLayoutInfo;
|
||||||
private int offset;
|
private int offset;
|
||||||
|
|
||||||
public DebugInfoParser(AsyncInputStream reader) {
|
public DebugInfoParser(AsyncInputStream reader) {
|
||||||
|
@ -44,6 +45,7 @@ public class DebugInfoParser extends ModuleParser {
|
||||||
var methods = addSection(new DebugMethodParser(strings, classes));
|
var methods = addSection(new DebugMethodParser(strings, classes));
|
||||||
variables = addSection(new DebugVariablesParser(strings));
|
variables = addSection(new DebugVariablesParser(strings));
|
||||||
lines = addSection(new DebugLinesParser(files, methods));
|
lines = addSection(new DebugLinesParser(files, methods));
|
||||||
|
classLayoutInfo = addSection(new DebugClassLayoutParser(strings, classes));
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends DebugSectionParser> T addSection(T section) {
|
private <T extends DebugSectionParser> T addSection(T section) {
|
||||||
|
@ -52,7 +54,8 @@ public class DebugInfoParser extends ModuleParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugInfo getDebugInfo() {
|
public DebugInfo getDebugInfo() {
|
||||||
return new DebugInfo(variables.getVariablesInfo(), lines.getLineInfo(), controlFlow, offset);
|
return new DebugInfo(variables.getVariablesInfo(), lines.getLineInfo(), controlFlow,
|
||||||
|
classLayoutInfo.getInfo(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.teavm.backend.wasm.binary.DataPrimitives;
|
||||||
import org.teavm.backend.wasm.binary.DataStructure;
|
import org.teavm.backend.wasm.binary.DataStructure;
|
||||||
import org.teavm.backend.wasm.binary.DataType;
|
import org.teavm.backend.wasm.binary.DataType;
|
||||||
import org.teavm.backend.wasm.binary.DataValue;
|
import org.teavm.backend.wasm.binary.DataValue;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugClassLayout;
|
||||||
|
import org.teavm.backend.wasm.debug.info.FieldType;
|
||||||
import org.teavm.common.IntegerArray;
|
import org.teavm.common.IntegerArray;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Function;
|
import org.teavm.interop.Function;
|
||||||
|
@ -415,16 +417,20 @@ public class WasmClassGenerator {
|
||||||
if (type instanceof ValueType.Primitive) {
|
if (type instanceof ValueType.Primitive) {
|
||||||
return false;
|
return false;
|
||||||
} else if (type instanceof ValueType.Object) {
|
} else if (type instanceof ValueType.Object) {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
var className = ((ValueType.Object) type).getClassName();
|
||||||
return !characteristics.isStructure(className)
|
return isManagedClass(className);
|
||||||
&& !characteristics.isFunction(className)
|
|
||||||
&& !characteristics.isResource(className)
|
|
||||||
&& !className.equals(Address.class.getName());
|
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isManagedClass(String className) {
|
||||||
|
return !characteristics.isStructure(className)
|
||||||
|
&& !characteristics.isFunction(className)
|
||||||
|
&& !characteristics.isResource(className)
|
||||||
|
&& !className.equals(Address.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
private void fillVirtualTable(VirtualTable vtable, DataValue array) {
|
private void fillVirtualTable(VirtualTable vtable, DataValue array) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
List<VirtualTable> tables = new ArrayList<>();
|
List<VirtualTable> tables = new ArrayList<>();
|
||||||
|
@ -693,6 +699,105 @@ public class WasmClassGenerator {
|
||||||
return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
|
return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeDebug(DebugClassLayout debug) {
|
||||||
|
var list = new ArrayList<>(binaryDataMap.values());
|
||||||
|
var indexes = new ObjectIntHashMap<ValueType>();
|
||||||
|
for (var i = 0; i < list.size(); ++i) {
|
||||||
|
indexes.put(list.get(i).type, i);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < list.size(); ++i) {
|
||||||
|
var data = list.get(i);
|
||||||
|
if (data.type instanceof ValueType.Primitive) {
|
||||||
|
debug.writePrimitive(((ValueType.Primitive) data.type).getKind(), data.start);
|
||||||
|
} else if (data.type instanceof ValueType.Array) {
|
||||||
|
var itemType = ((ValueType.Array) data.type).getItemType();
|
||||||
|
debug.writeArray(indexes.get(itemType), data.start);
|
||||||
|
} else if (data.type instanceof ValueType.Object) {
|
||||||
|
var className = ((ValueType.Object) data.type).getClassName();
|
||||||
|
if (isManagedClass(className)) {
|
||||||
|
var parent = data.cls.getParent() != null
|
||||||
|
? indexes.get(ValueType.object(data.cls.getParent()))
|
||||||
|
: -1;
|
||||||
|
if (data.isInferface) {
|
||||||
|
debug.writeInterface(className, data.start);
|
||||||
|
} else {
|
||||||
|
debug.startClass(className, parent, data.start, data.size);
|
||||||
|
var fields = getFieldsWithOffset(data);
|
||||||
|
for (var entry : fields) {
|
||||||
|
if (entry.field.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
debug.staticField(entry.field.getName(), entry.offset,
|
||||||
|
asDebugType(entry.field.getType()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var entry : fields) {
|
||||||
|
if (!entry.field.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
debug.instanceField(entry.field.getName(), entry.offset,
|
||||||
|
asDebugType(entry.field.getType()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug.endClass();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug.writeUnknown(data.start);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug.writeUnknown(data.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FieldWithOffset> getFieldsWithOffset(ClassBinaryData data) {
|
||||||
|
var result = new ArrayList<FieldWithOffset>();
|
||||||
|
for (var field : data.fieldLayout) {
|
||||||
|
var fieldReader = data.cls.getField(field.key);
|
||||||
|
result.add(new FieldWithOffset(fieldReader, field.value));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldWithOffset {
|
||||||
|
private FieldReader field;
|
||||||
|
private int offset;
|
||||||
|
|
||||||
|
FieldWithOffset(FieldReader field, int offset) {
|
||||||
|
this.field = field;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldType asDebugType(ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return FieldType.BOOLEAN;
|
||||||
|
case BYTE:
|
||||||
|
return FieldType.BYTE;
|
||||||
|
case SHORT:
|
||||||
|
return FieldType.SHORT;
|
||||||
|
case CHARACTER:
|
||||||
|
return FieldType.CHAR;
|
||||||
|
case INTEGER:
|
||||||
|
return FieldType.INT;
|
||||||
|
case LONG:
|
||||||
|
return FieldType.LONG;
|
||||||
|
case FLOAT:
|
||||||
|
return FieldType.FLOAT;
|
||||||
|
case DOUBLE:
|
||||||
|
return FieldType.DOUBLE;
|
||||||
|
default:
|
||||||
|
return FieldType.UNDEFINED;
|
||||||
|
}
|
||||||
|
} else if (type instanceof ValueType.Object) {
|
||||||
|
if (isManagedClass(((ValueType.Object) type).getClassName())) {
|
||||||
|
return FieldType.OBJECT;
|
||||||
|
} else {
|
||||||
|
return FieldType.ADDRESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return FieldType.OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class ClassBinaryData {
|
static class ClassBinaryData {
|
||||||
ValueType type;
|
ValueType type;
|
||||||
int size;
|
int size;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user