Provide more information in class header

This commit is contained in:
Alexey Andreev 2016-08-12 21:32:32 +03:00
parent fe5aca5139
commit 850609bb72
5 changed files with 261 additions and 43 deletions

View 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;
}
}
}

View File

@ -19,6 +19,38 @@ import org.teavm.interop.Structure;
public class RuntimeClass extends Structure { public class RuntimeClass extends Structure {
public static int INITIALIZED = 1; 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 size;
public int flags; 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);
}
} }

View File

@ -44,6 +44,7 @@ import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
@ -115,9 +116,14 @@ public class WasmTarget implements TeaVMTarget {
@Override @Override
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) { public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
int address = 256; int address = 256;
WasmModule module = new WasmModule();
WasmFunction initFunction = new WasmFunction("__start__");
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes); 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()) { for (String className : classes.getClassNames()) {
classGenerator.addClass(className); classGenerator.addClass(className);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
@ -132,7 +138,6 @@ public class WasmTarget implements TeaVMTarget {
context.addIntrinsic(new WasmRuntimeIntrinsic()); context.addIntrinsic(new WasmRuntimeIntrinsic());
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator); WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
WasmModule module = new WasmModule();
module.setMemorySize(64); module.setMemorySize(64);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
@ -175,8 +180,6 @@ public class WasmTarget implements TeaVMTarget {
return; return;
} }
WasmFunction initFunction = new WasmFunction("__start__");
classGenerator.contributeToInitializer(initFunction.getBody(), module);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassReader cls = classes.get(className); ClassReader cls = classes.get(className);
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) { 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(); WasmRenderer renderer = new WasmRenderer();
renderer.render(module); renderer.render(module);

View File

@ -17,6 +17,9 @@ package org.teavm.wasm.generate;
import com.carrotsearch.hppc.ObjectIntMap; import com.carrotsearch.hppc.ObjectIntMap;
import com.carrotsearch.hppc.ObjectIntOpenHashMap; import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -30,10 +33,11 @@ import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable; import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableEntry; import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.model.classes.VirtualTableProvider; 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.WasmExpression;
import org.teavm.wasm.model.expression.WasmInt32Constant; import org.teavm.wasm.model.expression.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype; import org.teavm.wasm.model.expression.WasmInt32Subtype;
@ -43,12 +47,18 @@ public class WasmClassGenerator {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private int address; private int address;
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>(); 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 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.classSource = classSource;
this.vtableProvider = vtableProvider; this.vtableProvider = vtableProvider;
this.address = address; this.tagRegistry = tagRegistry;
this.initializer = initializer;
} }
public void addClass(String className) { public void addClass(String className) {
@ -58,6 +68,7 @@ public class WasmClassGenerator {
ClassReader cls = classSource.get(className); ClassReader cls = classSource.get(className);
ClassBinaryData binaryData = new ClassBinaryData(); ClassBinaryData binaryData = new ClassBinaryData();
binaryData.name = className;
binaryDataMap.put(className, binaryData); binaryDataMap.put(className, binaryData);
calculateLayout(cls, binaryData); calculateLayout(cls, binaryData);
@ -65,49 +76,71 @@ public class WasmClassGenerator {
return; return;
} }
binaryData.start = align(address, 8); address = align(address, 8);
binaryData.vtable = vtableProvider.lookup(className); binaryData.start = address;
int vtableSize = binaryData.vtable != null ? binaryData.vtable.getEntries().size() : 0; contributeToInitializer(binaryData);
binaryData.end = binaryData.start + 8 + vtableSize * 4;
address = binaryData.end;
} }
public int getAddress() { public int getAddress() {
return address; return address;
} }
public void contributeToInitializer(List<WasmExpression> initializer, WasmModule module) { public void setAddress(int address) {
Map<MethodReference, Integer> functions = new HashMap<>(); this.address = address;
}
for (ClassBinaryData binaryData : binaryDataMap.values()) { public List<String> getFunctionTable() {
if (binaryData.start < 0) { return functionTable;
continue; }
}
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) { private void contributeToInitializer(ClassBinaryData binaryData) {
for (VirtualTableEntry vtableEntry : binaryData.vtable.getEntries().values()) { contributeInt32Value(binaryData.start + RuntimeClass.SIZE_OFFSET, binaryData.size);
index = new WasmInt32Constant(binaryData.start + 8 + vtableEntry.getIndex() * 4);
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));
return result;
});
}
WasmExpression methodIndexExpr = new WasmInt32Constant(methodIndex); List<TagRegistry.Range> ranges = tagRegistry.getRanges(binaryData.name);
initializer.add(new WasmStoreInt32(4, index, methodIndexExpr, WasmInt32Subtype.INT32)); 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 = functionTable.size();
functionTable.add(WasmMangling.mangleMethod(implementor));
return result;
});
} }
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) { public int getClassPointer(String className) {
@ -182,11 +215,9 @@ public class WasmClassGenerator {
} }
private class ClassBinaryData { private class ClassBinaryData {
int start; String name;
int end;
VirtualTable vtable;
int size; int size;
int start;
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>(); ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
} }
} }

View File

@ -615,7 +615,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod()); VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, 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); methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32);
WasmIndirectCall call = new WasmIndirectCall(methodIndex); WasmIndirectCall call = new WasmIndirectCall(methodIndex);