mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-09 00:14:10 -08:00
Provide more information in class header
This commit is contained in:
parent
fe5aca5139
commit
850609bb72
147
core/src/main/java/org/teavm/model/classes/TagRegistry.java
Normal file
147
core/src/main/java/org/teavm/model/classes/TagRegistry.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright 2016 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.model.classes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
|
||||
public class TagRegistry {
|
||||
private Map<String, List<Range>> ranges = new HashMap<>();
|
||||
|
||||
public TagRegistry(ListableClassReaderSource classSource) {
|
||||
List<String> roots = new ArrayList<>();
|
||||
Map<String, Set<String>> implementedBy = new HashMap<>();
|
||||
|
||||
Map<String, List<String>> hierarchy = new HashMap<>();
|
||||
for (String className : classSource.getClassNames()) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null || cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||
continue;
|
||||
}
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
String topmostImplementor = findTopmostImplementor(classSource, className, iface);
|
||||
markImplementor(classSource, topmostImplementor, iface, implementedBy);
|
||||
}
|
||||
if (cls.getParent() == null || cls.getParent().equals(cls.getName())) {
|
||||
roots.add(className);
|
||||
} else {
|
||||
hierarchy.computeIfAbsent(cls.getParent(), key -> new ArrayList<>()).add(className);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Range> simpleRanges = new HashMap<>();
|
||||
int current = 0;
|
||||
for (String root : roots) {
|
||||
assignRange(current, hierarchy, root, simpleRanges);
|
||||
++current;
|
||||
}
|
||||
|
||||
for (String className : classSource.getClassNames()) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null) {
|
||||
continue;
|
||||
}
|
||||
if (cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||
Set<String> implementorRoots = implementedBy.get(cls.getName());
|
||||
if (implementorRoots != null) {
|
||||
List<Range> ifaceRanges = implementorRoots.stream()
|
||||
.map(implementor -> simpleRanges.get(implementor))
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(range -> range.lower))
|
||||
.collect(Collectors.toList());
|
||||
if (!ifaceRanges.isEmpty()) {
|
||||
ranges.put(className, ifaceRanges);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Range simpleRange = simpleRanges.get(className);
|
||||
if (simpleRange != null) {
|
||||
ranges.put(className, new ArrayList<>(Arrays.asList(simpleRange)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String findTopmostImplementor(ClassReaderSource classSource, String className, String ifaceName) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
|
||||
String candidate = findTopmostImplementor(classSource, cls.getParent(), ifaceName);
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return cls.getInterfaces().contains(ifaceName) ? className : null;
|
||||
}
|
||||
|
||||
private void markImplementor(ClassReaderSource classSource, String className, String ifaceName,
|
||||
Map<String, Set<String>> implementedBy) {
|
||||
if (!implementedBy.computeIfAbsent(ifaceName, key -> new HashSet<>()).add(className)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassReader iface = classSource.get(ifaceName);
|
||||
if (iface == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String superIface : iface.getInterfaces()) {
|
||||
markImplementor(classSource, className, superIface, implementedBy);
|
||||
}
|
||||
}
|
||||
|
||||
private int assignRange(int start, Map<String, List<String>> hierarhy, String className,
|
||||
Map<String, Range> ranges) {
|
||||
int end = start + 1;
|
||||
for (String childClass : hierarhy.getOrDefault(className, Collections.emptyList())) {
|
||||
end = assignRange(end, hierarhy, childClass, ranges);
|
||||
}
|
||||
++end;
|
||||
ranges.put(className, new Range(start, end));
|
||||
return end;
|
||||
}
|
||||
|
||||
public List<Range> getRanges(String className) {
|
||||
return new ArrayList<>(ranges.getOrDefault(className, Collections.emptyList()));
|
||||
}
|
||||
|
||||
public static class Range {
|
||||
public int lower;
|
||||
public int upper;
|
||||
|
||||
private Range(int lower, int upper) {
|
||||
this.lower = lower;
|
||||
this.upper = upper;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,38 @@ import org.teavm.interop.Structure;
|
|||
|
||||
public class RuntimeClass extends Structure {
|
||||
public static int INITIALIZED = 1;
|
||||
|
||||
public static int SIZE_OFFSET = 0;
|
||||
public static int FLAGS_OFFSET = 4;
|
||||
public static int LOWER_TAG_OFFSET = 8;
|
||||
public static int UPPER_TAG_OFFSET = 12;
|
||||
public static int EXCLUDED_RANGE_COUNT_OFFSET = 16;
|
||||
public static int EXCLUDED_RANGE_ADDRESS_OFFSET = 20;
|
||||
public static int CANARY_OFFSET = 24;
|
||||
public static int VIRTUAL_TABLE_OFFSET = 28;
|
||||
|
||||
public static int ARRAY_CLASS = -1;
|
||||
public static int BOOLEAN_CLASS = -2;
|
||||
public static int BYTE_CLASS = -3;
|
||||
public static int SHORT_CLASS = -4;
|
||||
public static int CHAR_CLASS = -5;
|
||||
public static int INT_CLASS = -6;
|
||||
public static int LONG_CLASS = -7;
|
||||
public static int FLOAT_CLASS = -8;
|
||||
public static int DOUBLE_CLASS = -9;
|
||||
|
||||
public int size;
|
||||
public int flags;
|
||||
public int lowerTag;
|
||||
public int upperTag;
|
||||
public int excludedRangeCount;
|
||||
public int excludedRangesAddress;
|
||||
|
||||
public static int computeCanary(int size, int lowerTag, int upperTag) {
|
||||
return size ^ (lowerTag << 8) ^ (lowerTag >>> 24) ^ (upperTag << 24) ^ (lowerTag >>> 8);
|
||||
}
|
||||
|
||||
public int computeCanary() {
|
||||
return computeCanary(size, lowerTag, upperTag);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.teavm.model.MethodHolder;
|
|||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
|
@ -115,9 +116,14 @@ public class WasmTarget implements TeaVMTarget {
|
|||
@Override
|
||||
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
|
||||
int address = 256;
|
||||
WasmModule module = new WasmModule();
|
||||
WasmFunction initFunction = new WasmFunction("__start__");
|
||||
|
||||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, address);
|
||||
TagRegistry tagRegistry = new TagRegistry(classes);
|
||||
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, tagRegistry,
|
||||
initFunction.getBody());
|
||||
classGenerator.setAddress(address);
|
||||
for (String className : classes.getClassNames()) {
|
||||
classGenerator.addClass(className);
|
||||
if (controller.wasCancelled()) {
|
||||
|
@ -132,7 +138,6 @@ public class WasmTarget implements TeaVMTarget {
|
|||
context.addIntrinsic(new WasmRuntimeIntrinsic());
|
||||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
|
||||
|
||||
WasmModule module = new WasmModule();
|
||||
module.setMemorySize(64);
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
|
@ -175,8 +180,6 @@ public class WasmTarget implements TeaVMTarget {
|
|||
return;
|
||||
}
|
||||
|
||||
WasmFunction initFunction = new WasmFunction("__start__");
|
||||
classGenerator.contributeToInitializer(initFunction.getBody(), module);
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassReader cls = classes.get(className);
|
||||
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) {
|
||||
|
@ -199,6 +202,10 @@ public class WasmTarget implements TeaVMTarget {
|
|||
}
|
||||
}
|
||||
|
||||
for (String function : classGenerator.getFunctionTable()) {
|
||||
module.getFunctionTable().add(module.getFunctions().get(function));
|
||||
}
|
||||
|
||||
WasmRenderer renderer = new WasmRenderer();
|
||||
renderer.render(module);
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ package org.teavm.wasm.generate;
|
|||
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
@ -30,10 +33,11 @@ import org.teavm.model.FieldReader;
|
|||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTable;
|
||||
import org.teavm.model.classes.VirtualTableEntry;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.wasm.model.WasmModule;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.wasm.model.expression.WasmInt32Subtype;
|
||||
|
@ -43,12 +47,18 @@ public class WasmClassGenerator {
|
|||
private ClassReaderSource classSource;
|
||||
private int address;
|
||||
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
|
||||
private List<WasmExpression> initializer;
|
||||
private Map<MethodReference, Integer> functions = new HashMap<>();
|
||||
private List<String> functionTable = new ArrayList<>();
|
||||
private VirtualTableProvider vtableProvider;
|
||||
private TagRegistry tagRegistry;
|
||||
|
||||
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider, int address) {
|
||||
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider,
|
||||
TagRegistry tagRegistry, List<WasmExpression> initializer) {
|
||||
this.classSource = classSource;
|
||||
this.vtableProvider = vtableProvider;
|
||||
this.address = address;
|
||||
this.tagRegistry = tagRegistry;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
public void addClass(String className) {
|
||||
|
@ -58,6 +68,7 @@ public class WasmClassGenerator {
|
|||
|
||||
ClassReader cls = classSource.get(className);
|
||||
ClassBinaryData binaryData = new ClassBinaryData();
|
||||
binaryData.name = className;
|
||||
binaryDataMap.put(className, binaryData);
|
||||
|
||||
calculateLayout(cls, binaryData);
|
||||
|
@ -65,49 +76,71 @@ public class WasmClassGenerator {
|
|||
return;
|
||||
}
|
||||
|
||||
binaryData.start = align(address, 8);
|
||||
binaryData.vtable = vtableProvider.lookup(className);
|
||||
int vtableSize = binaryData.vtable != null ? binaryData.vtable.getEntries().size() : 0;
|
||||
binaryData.end = binaryData.start + 8 + vtableSize * 4;
|
||||
|
||||
address = binaryData.end;
|
||||
address = align(address, 8);
|
||||
binaryData.start = address;
|
||||
contributeToInitializer(binaryData);
|
||||
}
|
||||
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void contributeToInitializer(List<WasmExpression> initializer, WasmModule module) {
|
||||
Map<MethodReference, Integer> functions = new HashMap<>();
|
||||
|
||||
for (ClassBinaryData binaryData : binaryDataMap.values()) {
|
||||
if (binaryData.start < 0) {
|
||||
continue;
|
||||
public void setAddress(int address) {
|
||||
this.address = address;
|
||||
}
|
||||
WasmExpression index = new WasmInt32Constant(binaryData.start);
|
||||
WasmExpression size = new WasmInt32Constant(binaryData.size);
|
||||
initializer.add(new WasmStoreInt32(4, index, size, WasmInt32Subtype.INT32));
|
||||
|
||||
if (binaryData.vtable != null) {
|
||||
for (VirtualTableEntry vtableEntry : binaryData.vtable.getEntries().values()) {
|
||||
index = new WasmInt32Constant(binaryData.start + 8 + vtableEntry.getIndex() * 4);
|
||||
public List<String> getFunctionTable() {
|
||||
return functionTable;
|
||||
}
|
||||
|
||||
private void contributeToInitializer(ClassBinaryData binaryData) {
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.SIZE_OFFSET, binaryData.size);
|
||||
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(binaryData.name);
|
||||
int lower = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
||||
int upper = ranges.stream().mapToInt(range -> range.upper).max().orElse(0);
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.LOWER_TAG_OFFSET, lower);
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.UPPER_TAG_OFFSET, upper);
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.CANARY_OFFSET,
|
||||
RuntimeClass.computeCanary(binaryData.size, lower, upper));
|
||||
|
||||
address = binaryData.start + RuntimeClass.VIRTUAL_TABLE_OFFSET;
|
||||
VirtualTable vtable = vtableProvider.lookup(binaryData.name);
|
||||
if (vtable != null) {
|
||||
for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) {
|
||||
int methodIndex;
|
||||
if (vtableEntry.getImplementor() == null) {
|
||||
methodIndex = -1;
|
||||
} else {
|
||||
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
|
||||
int result = module.getFunctionTable().size();
|
||||
String name = WasmMangling.mangleMethod(implementor);
|
||||
module.getFunctionTable().add(module.getFunctions().get(name));
|
||||
int result = functionTable.size();
|
||||
functionTable.add(WasmMangling.mangleMethod(implementor));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
WasmExpression methodIndexExpr = new WasmInt32Constant(methodIndex);
|
||||
initializer.add(new WasmStoreInt32(4, index, methodIndexExpr, WasmInt32Subtype.INT32));
|
||||
contributeInt32Value(address, methodIndex);
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.EXCLUDED_RANGE_COUNT_OFFSET, ranges.size() - 1);
|
||||
|
||||
if (ranges.size() > 1) {
|
||||
contributeInt32Value(binaryData.start + RuntimeClass.EXCLUDED_RANGE_ADDRESS_OFFSET, address);
|
||||
|
||||
Collections.sort(ranges, Comparator.comparingInt(range -> range.lower));
|
||||
for (int i = 1; i < ranges.size(); ++i) {
|
||||
contributeInt32Value(address, ranges.get(i - 1).upper);
|
||||
contributeInt32Value(address + 4, ranges.get(i).lower);
|
||||
address += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void contributeInt32Value(int index, int value) {
|
||||
initializer.add(new WasmStoreInt32(4, new WasmInt32Constant(index), new WasmInt32Constant(value),
|
||||
WasmInt32Subtype.INT32));
|
||||
}
|
||||
|
||||
public int getClassPointer(String className) {
|
||||
|
@ -182,11 +215,9 @@ public class WasmClassGenerator {
|
|||
}
|
||||
|
||||
private class ClassBinaryData {
|
||||
int start;
|
||||
int end;
|
||||
|
||||
VirtualTable vtable;
|
||||
String name;
|
||||
int size;
|
||||
int start;
|
||||
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -615,7 +615,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
|
||||
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
classIndex, new WasmInt32Constant(vtableEntry.getIndex() * 4 + 8));
|
||||
classIndex, new WasmInt32Constant(vtableEntry.getIndex() * 4
|
||||
+ RuntimeClass.VIRTUAL_TABLE_OFFSET));
|
||||
methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32);
|
||||
|
||||
WasmIndirectCall call = new WasmIndirectCall(methodIndex);
|
||||
|
|
Loading…
Reference in New Issue
Block a user