C/Wasm: don't generate names for classes when possible

This commit is contained in:
Alexey Andreev 2019-10-23 14:11:24 +03:00
parent f7620704fc
commit c78874f426
11 changed files with 230 additions and 99 deletions

View File

@ -34,6 +34,8 @@ public class ClassDependencyListener implements DependencyPlugin {
});
break;
case "getSimpleNameCacheLowLevel":
case "getCanonicalNameCacheLowLevel":
case "getNameCacheLowLevel":
method.getResult().propagate(agent.getType("java.lang.String"));
break;
}

View File

@ -106,7 +106,21 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@Unmanaged
public String getName() {
if (PlatformDetector.isLowLevel()) {
return Platform.getName(platformClass);
String result = getNameCache(this);
if (result == null) {
result = Platform.getName(platformClass);
if (result == null) {
if (isArray()) {
TClass<?> componentType = getComponentType();
String componentName = componentType.getName();
if (componentName != null) {
result = componentType.isArray() ? "[" + componentName : "[L" + componentName + ";";
}
}
}
setNameCache(this, result);
}
return result;
} else {
if (name == null) {
name = Platform.getName(platformClass);
@ -167,6 +181,27 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
self.simpleNameCache = object;
}
@DelegateTo("getNameCacheLowLevel")
private static String getNameCache(TClass<?> self) {
return self.name;
}
@Unmanaged
@PluggableDependency(ClassDependencyListener.class)
private static RuntimeObject getNameCacheLowLevel(RuntimeClass self) {
return self.nameCache;
}
@DelegateTo("setNameCacheLowLevel")
private static void setNameCache(TClass<?> self, String value) {
self.name = value;
}
@Unmanaged
private static void setNameCacheLowLevel(RuntimeClass self, RuntimeObject object) {
self.nameCache = object;
}
public String getCanonicalName() {
String result = getCanonicalNameCache();
if (result == null) {

View File

@ -64,7 +64,8 @@ public class CNameProvider extends LowLevelNameProvider {
preserveFieldNames(RuntimeClass.class.getName(), "size", "flags", "tag", "canary", "name", "itemType",
"arrayType", "isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
"superinterfaces", "simpleNameCache", "declaringClass", "enclosingClass", "canonicalName");
"superinterfaces", "simpleNameCache", "declaringClass", "enclosingClass", "canonicalName",
"nameCache");
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");
preserveFieldNames(RuntimeReference.class.getName(), "queue", "object", "next");
preserveFieldNames(RuntimeReferenceQueue.class.getName(), "first", "last");

View File

@ -58,6 +58,7 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableEntry;
@ -104,6 +105,7 @@ public class ClassGenerator {
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor();
private List<CallSiteDescriptor> callSites;
private ClassMetadataRequirements metadataRequirements;
public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler,
CacheStatus cacheStatus) {
@ -111,6 +113,7 @@ public class ClassGenerator {
this.tagRegistry = tagRegistry;
this.decompiler = decompiler;
this.cacheStatus = cacheStatus;
metadataRequirements = new ClassMetadataRequirements(context.getDependencies());
}
public void setAstCache(MethodNodeCache astCache) {
@ -778,7 +781,10 @@ public class ClassGenerator {
itemTypeExpr = "NULL";
}
int nameRef = context.getStringPool().getStringIndex(nameOfType(type));
String metadataName = nameOfType(type);
String nameRef = metadataName != null
? "(TeaVM_Object**) &TEAVM_GET_STRING(" + context.getStringPool().getStringIndex(metadataName) + ")"
: "NULL";
String superTypeFunction = context.getNames().forSupertypeFunction(type);
ValueType arrayType = ValueType.arrayOf(type);
@ -802,7 +808,7 @@ public class ClassGenerator {
codeWriter.println(".flags = " + flags + ",");
codeWriter.println(".tag = " + tag + ",");
codeWriter.println(".canary = 0,");
codeWriter.println(".name = (TeaVM_Object**) &TEAVM_GET_STRING(" + nameRef + "),");
codeWriter.println(".name = " + nameRef + ",");
codeWriter.println(".simpleName = " + simpleName + ",");
codeWriter.println(".arrayType = " + arrayTypeExpr + ",");
codeWriter.println(".itemType = " + itemTypeExpr + ",");
@ -1168,7 +1174,7 @@ public class ClassGenerator {
return true;
}
public static String nameOfType(ValueType type) {
public String nameOfType(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
@ -1191,16 +1197,28 @@ public class ClassGenerator {
throw new AssertionError();
}
} else if (type instanceof ValueType.Array) {
if (isArrayOfPrimitives(type)) {
return type.toString().replace('/', '.');
} else {
return null;
}
} else if (type == ValueType.VOID) {
return "void";
} else if (type instanceof ValueType.Object) {
return ((ValueType.Object) type).getClassName();
String name = ((ValueType.Object) type).getClassName();
return metadataRequirements.getInfo(name).name() ? name : null;
} else {
throw new AssertionError();
}
}
private static boolean isArrayOfPrimitives(ValueType type) {
while (type instanceof ValueType.Array) {
type = ((ValueType.Array) type).getItemType();
}
return type instanceof ValueType.Primitive || type == ValueType.VOID;
}
private void generateIsSupertypeFunction(ValueType type) {
String name = context.getNames().forSupertypeFunction(type);
headerWriter.println("extern int32_t " + name + "(TeaVM_Class*);");

View File

@ -62,8 +62,11 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
printFieldAccess(context, invocation, SUPERCLASS_FIELD);
break;
case "getName":
context.writer().print("*");
context.writer().print("(");
printFieldAccess(context, invocation, NAME_FIELD);
context.writer().print(" ? *");
printFieldAccess(context, invocation, NAME_FIELD);
context.writer().print(" : NULL)");
break;
case "getSimpleName":
context.writer().print("(");

View File

@ -45,7 +45,6 @@ import org.teavm.common.ServiceRepository;
import org.teavm.debugging.information.DebugInformationEmitter;
import org.teavm.debugging.information.DummyDebugInformationEmitter;
import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
@ -57,6 +56,7 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.vm.RenderingException;
import org.teavm.vm.TeaVMProgressFeedback;
@ -475,17 +475,17 @@ public class Renderer implements RenderingManager {
return;
}
Map<String, RequiredClassMetadata> classesRequiringName = findRequiredClassMetadata();
ClassMetadataRequirements metadataRequirements = new ClassMetadataRequirements(context.getDependencyInfo());
int start = writer.getOffset();
try {
writer.append("$rt_packages([");
ObjectIntMap<String> packageIndexes = generatePackageMetadata(classes, classesRequiringName);
ObjectIntMap<String> packageIndexes = generatePackageMetadata(classes, metadataRequirements);
writer.append("]);").newLine();
for (int i = 0; i < classes.size(); i += 50) {
int j = Math.min(i + 50, classes.size());
renderClassMetadataPortion(classes.subList(i, j), packageIndexes, classesRequiringName);
renderClassMetadataPortion(classes.subList(i, j), packageIndexes, metadataRequirements);
}
} catch (IOException e) {
@ -496,7 +496,7 @@ public class Renderer implements RenderingManager {
}
private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes,
Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
ClassMetadataRequirements metadataRequirements) throws IOException {
writer.append("$rt_metadata([");
boolean first = true;
for (PreparedClass cls : classes) {
@ -507,8 +507,8 @@ public class Renderer implements RenderingManager {
debugEmitter.emitClass(cls.getName());
writer.appendClass(cls.getName()).append(",").ws();
RequiredClassMetadata requiredMetadata = metadataRequirements.get(cls.getName());
if (requiredMetadata != null && requiredMetadata.name) {
ClassMetadataRequirements.Info requiredMetadata = metadataRequirements.getInfo(cls.getName());
if (requiredMetadata.name()) {
String className = cls.getName();
int dotIndex = className.lastIndexOf('.') + 1;
String packageName = className.substring(0, dotIndex);
@ -540,24 +540,24 @@ public class Renderer implements RenderingManager {
writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws();
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
if (requiredMetadata == null || !(requiredMetadata.enclosingClass
|| requiredMetadata.declaringClass || requiredMetadata.simpleName)) {
if (!requiredMetadata.enclosingClass() && !requiredMetadata.declaringClass()
&& !requiredMetadata.simpleName()) {
writer.append("0");
} else {
writer.append('[');
if (requiredMetadata.enclosingClass && cls.getClassHolder().getOwnerName() != null) {
if (requiredMetadata.enclosingClass() && cls.getClassHolder().getOwnerName() != null) {
writer.appendClass(cls.getClassHolder().getOwnerName());
} else {
writer.append('0');
}
writer.append(',');
if (requiredMetadata.declaringClass && cls.getClassHolder().getDeclaringClassName() != null) {
if (requiredMetadata.declaringClass() && cls.getClassHolder().getDeclaringClassName() != null) {
writer.appendClass(cls.getClassHolder().getDeclaringClassName());
} else {
writer.append('0');
}
writer.append(',');
if (requiredMetadata.simpleName && cls.getClassHolder().getSimpleName() != null) {
if (requiredMetadata.simpleName() && cls.getClassHolder().getSimpleName() != null) {
writer.append("\"").append(RenderingUtil.escapeString(cls.getClassHolder().getSimpleName()))
.append("\"");
} else {
@ -590,13 +590,13 @@ public class Renderer implements RenderingManager {
}
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes,
Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
ClassMetadataRequirements metadataRequirements) throws IOException {
PackageNode root = new PackageNode(null);
for (PreparedClass classNode : classes) {
String className = classNode.getName();
RequiredClassMetadata requiredMetadata = metadataRequirements.get(className);
if (requiredMetadata == null || !requiredMetadata.name) {
ClassMetadataRequirements.Info requiredMetadata = metadataRequirements.getInfo(className);
if (!requiredMetadata.name()) {
continue;
}
@ -655,65 +655,6 @@ public class Renderer implements RenderingManager {
}
}
private Map<String, RequiredClassMetadata> findRequiredClassMetadata() {
Map<String, RequiredClassMetadata> requirements = new HashMap<>();
MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getName", String.class));
if (getNameMethod != null) {
addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes());
}
MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getSimpleName", String.class));
if (getSimpleNameMethod != null) {
String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes();
addClassesRequiringName(requirements, classNames);
for (String className : classNames) {
RequiredClassMetadata requiredMetadata = requirements.computeIfAbsent(className,
k -> new RequiredClassMetadata());
requiredMetadata.simpleName = true;
requiredMetadata.enclosingClass = true;
}
}
MethodDependencyInfo getDeclaringClassMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getDeclaringClass", Class.class));
if (getDeclaringClassMethod != null) {
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) {
requirements.computeIfAbsent(className, k -> new RequiredClassMetadata()).declaringClass = true;
}
}
MethodDependencyInfo getEnclosingClassMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getEnclosingClass", Class.class));
if (getEnclosingClassMethod != null) {
String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) {
requirements.computeIfAbsent(className, k -> new RequiredClassMetadata()).enclosingClass = true;
}
}
return requirements;
}
private void addClassesRequiringName(Map<String, RequiredClassMetadata> target, String[] source) {
for (String typeName : source) {
if (typeName.startsWith("[")) {
if (!typeName.endsWith(";")) {
continue;
}
int index = 0;
while (typeName.charAt(index) == '[') {
++index;
}
typeName = typeName.substring(index, typeName.length() - 1).replace('/', '.');
}
target.computeIfAbsent(typeName, k -> new RequiredClassMetadata()).name = true;
}
}
private void collectMethodsToCopyFromInterfaces(ClassReader cls, List<MethodReference> targetList) {
Set<MethodDescriptor> implementedMethods = new HashSet<>();
implementedMethods.addAll(targetList.stream().map(method -> method.getDescriptor())
@ -1217,11 +1158,4 @@ public class Renderer implements RenderingManager {
private boolean isVirtual(MethodReference method) {
return context.isVirtual(method);
}
static class RequiredClassMetadata {
boolean name;
boolean simpleName;
boolean declaringClass;
boolean enclosingClass;
}
}

View File

@ -72,6 +72,7 @@ public class WasmClassGenerator {
DataPrimitives.INT, /* tag */
DataPrimitives.INT, /* canary */
DataPrimitives.ADDRESS, /* name */
DataPrimitives.ADDRESS, /* name cache */
DataPrimitives.ADDRESS, /* item type */
DataPrimitives.ADDRESS, /* array type */
DataPrimitives.ADDRESS, /* declaring class */
@ -96,16 +97,16 @@ public class WasmClassGenerator {
private static final int CLASS_TAG = 3;
private static final int CLASS_CANARY = 4;
private static final int CLASS_NAME = 5;
private static final int CLASS_ITEM_TYPE = 6;
private static final int CLASS_ARRAY_TYPE = 7;
private static final int CLASS_DECLARING_CLASS = 8;
private static final int CLASS_ENCLOSING_CLASS = 9;
private static final int CLASS_IS_INSTANCE = 10;
private static final int CLASS_INIT = 11;
private static final int CLASS_PARENT = 12;
private static final int CLASS_ENUM_VALUES = 15;
private static final int CLASS_LAYOUT = 16;
private static final int CLASS_SIMPLE_NAME = 17;
private static final int CLASS_ITEM_TYPE = 7;
private static final int CLASS_ARRAY_TYPE = 8;
private static final int CLASS_DECLARING_CLASS = 9;
private static final int CLASS_ENCLOSING_CLASS = 10;
private static final int CLASS_IS_INSTANCE = 11;
private static final int CLASS_INIT = 12;
private static final int CLASS_PARENT = 13;
private static final int CLASS_ENUM_VALUES = 16;
private static final int CLASS_LAYOUT = 17;
private static final int CLASS_SIMPLE_NAME = 18;
public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource,
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter,

View File

@ -0,0 +1,129 @@
/*
* Copyright 2019 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.analysis;
import java.util.HashMap;
import java.util.Map;
import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.model.MethodReference;
public class ClassMetadataRequirements {
private static final MethodReference GET_NAME_METHOD = new MethodReference(Class.class, "getName", String.class);
private static final MethodReference GET_SIMPLE_NAME_METHOD = new MethodReference(Class.class,
"getSimpleName", String.class);
private static final MethodReference GET_DECLARING_CLASS_METHOD = new MethodReference(Class.class,
"getDeclaringClass", Class.class);
private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class,
"getEnclosingClass", Class.class);
private static final ClassInfo EMPTY_INFO = new ClassInfo();
private Map<String, ClassInfo> requirements = new HashMap<>();
public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD);
if (getNameMethod != null) {
addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes());
}
MethodDependencyInfo getSimpleNameMethod = dependencyInfo.getMethod(GET_SIMPLE_NAME_METHOD);
if (getSimpleNameMethod != null) {
String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes();
addClassesRequiringName(requirements, classNames);
for (String className : classNames) {
ClassInfo classInfo = requirements.computeIfAbsent(className, k -> new ClassInfo());
classInfo.simpleName = true;
classInfo.enclosingClass = true;
}
}
MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD);
if (getDeclaringClassMethod != null) {
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) {
requirements.computeIfAbsent(className, k -> new ClassInfo()).declaringClass = true;
}
}
MethodDependencyInfo getEnclosingClassMethod = dependencyInfo.getMethod(GET_ENCLOSING_CLASS_METHOD);
if (getEnclosingClassMethod != null) {
String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) {
requirements.computeIfAbsent(className, k -> new ClassInfo()).enclosingClass = true;
}
}
}
public Info getInfo(String className) {
ClassInfo result = requirements.get(className);
if (result == null) {
result = EMPTY_INFO;
}
return result;
}
private void addClassesRequiringName(Map<String, ClassInfo> target, String[] source) {
for (String typeName : source) {
if (typeName.startsWith("[")) {
if (!typeName.endsWith(";")) {
continue;
}
int index = 0;
while (typeName.charAt(index) == '[') {
++index;
}
typeName = typeName.substring(index, typeName.length() - 1).replace('/', '.');
}
target.computeIfAbsent(typeName, k -> new ClassInfo()).name = true;
}
}
static class ClassInfo implements Info {
boolean name;
boolean simpleName;
boolean declaringClass;
boolean enclosingClass;
@Override
public boolean name() {
return name;
}
@Override
public boolean simpleName() {
return simpleName;
}
@Override
public boolean declaringClass() {
return declaringClass;
}
@Override
public boolean enclosingClass() {
return enclosingClass;
}
}
public interface Info {
boolean name();
boolean simpleName();
boolean declaringClass();
boolean enclosingClass();
}
}

View File

@ -208,6 +208,9 @@ public final class GC {
if (cls.canonicalName != null) {
mark(cls.canonicalName);
}
if (cls.nameCache != null) {
mark(cls.nameCache);
}
classPtr = classPtr.add(Address.sizeOf());
}
@ -582,6 +585,9 @@ public final class GC {
if (cls.canonicalName != null) {
cls.canonicalName = updatePointer(cls.canonicalName.toAddress()).toStructure();
}
if (cls.nameCache != null) {
cls.nameCache = updatePointer(cls.nameCache.toAddress()).toStructure();
}
classPtr = classPtr.add(Address.sizeOf());
}
}

View File

@ -48,6 +48,7 @@ public class RuntimeClass extends RuntimeObject {
public int tag;
public int canary;
public RuntimeObjectPtr name;
public RuntimeObject nameCache;
public RuntimeClass itemType;
public RuntimeClass arrayType;
public RuntimeClass declaringClass;

View File

@ -28,6 +28,7 @@ typedef struct TeaVM_Class {
int32_t tag;
int32_t canary;
TeaVM_Object** name;
TeaVM_Object* nameCache;
struct TeaVM_Class* itemType;
struct TeaVM_Class* arrayType;
struct TeaVM_Class* declaringClass;