Add support for Class.getCanonicalName/getEnclosingClass

This commit is contained in:
Alexey Andreev 2019-10-18 15:10:05 +03:00
parent 28212f70ff
commit 8a91605c56
26 changed files with 478 additions and 151 deletions

View File

@ -23,14 +23,26 @@ import org.teavm.model.ClassReader;
public class DeclaringClassDependencyListener extends AbstractDependencyListener { public class DeclaringClassDependencyListener extends AbstractDependencyListener {
@Override @Override
public void methodReached(DependencyAgent agent, MethodDependency method) { public void methodReached(DependencyAgent agent, MethodDependency method) {
if (method.getReference().getClassName().equals("java.lang.Class") if (method.getReference().getClassName().equals("java.lang.Class")) {
&& method.getReference().getName().equals("getDeclaringClass")) { switch (method.getReference().getName()) {
method.getVariable(0).getClassValueNode().addConsumer(t -> { case "getEnclosingClass":
ClassReader cls = agent.getClassSource().get(t.getName()); method.getVariable(0).getClassValueNode().addConsumer(t -> {
if (cls != null && cls.getOwnerName() != null) { ClassReader cls = agent.getClassSource().get(t.getName());
agent.linkClass(cls.getOwnerName()); if (cls != null && cls.getOwnerName() != null) {
agent.linkClass(cls.getOwnerName());
}
});
break;
case "getDeclaringClass": {
method.getVariable(0).getClassValueNode().addConsumer(t -> {
ClassReader cls = agent.getClassSource().get(t.getName());
if (cls != null && cls.getDeclaringClassName() != null) {
agent.linkClass(cls.getDeclaringClassName());
}
});
break;
} }
}); }
} }
} }
} }

View File

@ -1,56 +0,0 @@
/*
* Copyright 2019 konsoletyper.
*
* 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.classlib.impl;
import java.io.IOException;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodReference;
public class DeclaringClassGenerator implements Generator {
private static final MethodReference METHOD = new MethodReference(Class.class, "getDeclaringClass",
Class.class);
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
writer.append("var p").ws().append("=").ws().append("\"teavm_declaredClass\";").softNewLine();
writer.append("if").ws().append("(!").appendMethodBody(methodRef).append(".initialized").append(")")
.ws().append("{").indent().softNewLine();
MethodDependencyInfo methodDep = context.getDependency().getMethod(METHOD);
if (methodDep != null) {
for (String type : methodDep.getVariable(0).getClassValueNode().getTypes()) {
ClassReader cls = context.getClassSource().get(type);
if (cls != null) {
writer.appendClass(type).append("[p]").ws().append("=").ws();
if (cls.getOwnerName() != null) {
writer.appendClass(cls.getOwnerName());
} else {
writer.append("null");
}
writer.append(";").softNewLine();
}
}
}
writer.appendMethodBody(methodRef).append(".initialized").ws().append("=").ws().append("true;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").append(context.getParameterName(1)).append("[p];").softNewLine();
}
}

View File

@ -144,12 +144,6 @@ public class JCLPlugin implements TeaVMPlugin {
installMetadata(host.getService(MetadataRegistration.class)); installMetadata(host.getService(MetadataRegistration.class));
host.add(new DeclaringClassDependencyListener()); host.add(new DeclaringClassDependencyListener());
TeaVMJavaScriptHost jsExtension = host.getExtension(TeaVMJavaScriptHost.class);
if (jsExtension != null) {
jsExtension.add(new MethodReference(Class.class, "getDeclaringClassImpl", PlatformClass.class,
PlatformClass.class), new DeclaringClassGenerator());
}
} }
private void installMetadata(MetadataRegistration reg) { private void installMetadata(MetadataRegistration reg) {

View File

@ -53,6 +53,7 @@ import org.teavm.runtime.RuntimeObject;
public class TClass<T> extends TObject implements TAnnotatedElement { public class TClass<T> extends TObject implements TAnnotatedElement {
String name; String name;
String simpleName; String simpleName;
String canonicalName;
private PlatformClass platformClass; private PlatformClass platformClass;
private TAnnotation[] annotationsCache; private TAnnotation[] annotationsCache;
private Map<TClass<?>, TAnnotation> annotationsByType; private Map<TClass<?>, TAnnotation> annotationsByType;
@ -115,51 +116,110 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
} }
public String getSimpleName() { public String getSimpleName() {
String simpleName = getSimpleNameCache(); String simpleName = getSimpleNameCache(this);
if (simpleName == null) { if (simpleName == null) {
if (isArray()) { if (isArray()) {
simpleName = getComponentType().getSimpleName() + "[]"; simpleName = getComponentType().getSimpleName() + "[]";
setSimpleNameCache(simpleName); } else if (getEnclosingClass() != null) {
return simpleName; simpleName = Platform.getSimpleName(platformClass);
} if (simpleName == null) {
String name = Platform.getName(platformClass); simpleName = "";
int lastDollar = name.lastIndexOf('$');
if (lastDollar != -1) {
name = name.substring(lastDollar + 1);
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
name = "";
} }
} else { } else {
int lastDot = name.lastIndexOf('.'); String name = Platform.getName(platformClass);
if (lastDot != -1) { int lastDollar = name.lastIndexOf('$');
name = name.substring(lastDot + 1); if (lastDollar != -1) {
name = name.substring(lastDollar + 1);
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
name = "";
}
} else {
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
name = name.substring(lastDot + 1);
}
} }
simpleName = name;
} }
simpleName = name; setSimpleNameCache(this, simpleName);
setSimpleNameCache(simpleName);
} }
return simpleName; return simpleName;
} }
@DelegateTo("getSimpleNameCacheLowLevel") @DelegateTo("getSimpleNameCacheLowLevel")
private String getSimpleNameCache() { private static String getSimpleNameCache(TClass<?> self) {
return simpleName; return self.simpleName;
} }
@Unmanaged @Unmanaged
@PluggableDependency(ClassDependencyListener.class) @PluggableDependency(ClassDependencyListener.class)
private RuntimeObject getSimpleNameCacheLowLevel() { private static RuntimeObject getSimpleNameCacheLowLevel(RuntimeClass self) {
return Address.ofObject(this).<RuntimeClass>toStructure().simpleName; return self.simpleNameCache;
} }
@DelegateTo("setSimpleNameCacheLowLevel") @DelegateTo("setSimpleNameCacheLowLevel")
private void setSimpleNameCache(String value) { private static void setSimpleNameCache(TClass<?> self, String value) {
simpleName = value; self.simpleName = value;
} }
@Unmanaged @Unmanaged
private void setSimpleNameCacheLowLevel(RuntimeObject object) { private static void setSimpleNameCacheLowLevel(RuntimeClass self, RuntimeObject object) {
Address.ofObject(this).<RuntimeClass>toStructure().simpleName = object; self.simpleNameCache = object;
}
public String getCanonicalName() {
String result = getCanonicalNameCache();
if (result == null) {
if (isArray()) {
String componentName = getComponentType().getCanonicalName();
if (componentName == null) {
return null;
}
result = componentName + "[]";
} else if (getEnclosingClass() != null) {
if (getDeclaringClass() == null || isSynthetic()) {
return null;
}
String enclosingName = getDeclaringClass().getCanonicalName();
if (enclosingName == null) {
return null;
}
result = enclosingName + "." + getSimpleName();
} else {
result = getName();
}
setCanonicalNameCache(result);
}
return result;
}
private boolean isSynthetic() {
if (PlatformDetector.isJavaScript()) {
return (platformClass.getMetadata().getAccessLevel() & Flags.SYNTHETIC) != 0;
} else {
return (RuntimeClass.getClass(Address.ofObject(this).toStructure()).flags & RuntimeClass.SYNTHETIC) != 0;
}
}
@DelegateTo("getCanonicalNameCacheLowLevel")
private String getCanonicalNameCache() {
return canonicalName;
}
@Unmanaged
@PluggableDependency(ClassDependencyListener.class)
private RuntimeObject getCanonicalNameCacheLowLevel() {
return Address.ofObject(this).<RuntimeClass>toStructure().canonicalName;
}
@DelegateTo("setCanonicalNameCacheLowLevel")
private void setCanonicalNameCache(String value) {
canonicalName = value;
}
@Unmanaged
private void setCanonicalNameCacheLowLevel(RuntimeObject object) {
Address.ofObject(this).<RuntimeClass>toStructure().canonicalName = object;
} }
public boolean isPrimitive() { public boolean isPrimitive() {
@ -178,6 +238,15 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0; return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
} }
public boolean isLocalClass() {
return (platformClass.getMetadata().getFlags() & Flags.SYNTHETIC) != 0
&& getEnclosingClass() != null;
}
public boolean isMemberClass() {
return getDeclaringClass() != null;
}
@PluggableDependency(ClassGenerator.class) @PluggableDependency(ClassGenerator.class)
public TClass<?> getComponentType() { public TClass<?> getComponentType() {
return getClass(Platform.getArrayItem(platformClass)); return getClass(Platform.getArrayItem(platformClass));
@ -597,11 +666,14 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
} }
public TClass<?> getDeclaringClass() { public TClass<?> getDeclaringClass() {
PlatformClass result = getDeclaringClassImpl(getPlatformClass()); PlatformClass result = Platform.getDeclaringClass(getPlatformClass());
return result != null ? getClass(result) : null;
}
public TClass<?> getEnclosingClass() {
PlatformClass result = Platform.getEnclosingClass(getPlatformClass());
return result != null ? getClass(result) : null; return result != null ? getClass(result) : null;
} }
private static native PlatformClass getDeclaringClassImpl(PlatformClass cls);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <U> TClass<? extends U> asSubclass(TClass<U> clazz) { public <U> TClass<? extends U> asSubclass(TClass<U> clazz) {

View File

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

View File

@ -667,6 +667,9 @@ public class ClassGenerator {
String initFunction = "NULL"; String initFunction = "NULL";
String superinterfaceCount = "0"; String superinterfaceCount = "0";
String superinterfaces = "NULL"; String superinterfaces = "NULL";
String simpleName = null;
String declaringClass = "NULL";
String enclosingClass = "NULL";
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName(); String className = ((ValueType.Object) type).getClassName();
@ -682,8 +685,13 @@ public class ClassGenerator {
} else { } else {
sizeExpr = "0"; sizeExpr = "0";
} }
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { if (cls != null) {
flags |= RuntimeClass.ENUM; if (cls.hasModifier(ElementModifier.ENUM)) {
flags |= RuntimeClass.ENUM;
}
if (cls.hasModifier(ElementModifier.SYNTHETIC)) {
flags |= RuntimeClass.SYNTHETIC;
}
} }
List<TagRegistry.Range> ranges = tagRegistry != null ? tagRegistry.getRanges(className) : null; List<TagRegistry.Range> ranges = tagRegistry != null ? tagRegistry.getRanges(className) : null;
tag = !context.isIncremental() && ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0; tag = !context.isIncremental() && ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
@ -730,6 +738,22 @@ public class ClassGenerator {
break; break;
} }
simpleName = cls.getSimpleName();
if (cls.getDeclaringClassName() != null
&& context.getDependencies().getClass(cls.getDeclaringClassName()) != null) {
declaringClass = "(TeaVM_Class*) &" + context.getNames().forClassInstance(
ValueType.object(cls.getDeclaringClassName()));
includes.includeClass(cls.getDeclaringClassName());
}
if (cls.getOwnerName() != null
&& context.getDependencies().getClass(cls.getOwnerName()) != null) {
enclosingClass = "(TeaVM_Class*) &" + context.getNames().forClassInstance(
ValueType.object(cls.getOwnerName()));
includes.includeClass(cls.getOwnerName());
}
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
includes.includeClass("java.lang.Object"); includes.includeClass("java.lang.Object");
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object")); parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
@ -766,13 +790,20 @@ public class ClassGenerator {
arrayTypeExpr = "NULL"; arrayTypeExpr = "NULL";
} }
if (simpleName == null) {
simpleName = "NULL";
} else {
int simpleNameIndex = context.getStringPool().getStringIndex(simpleName);
simpleName = "(TeaVM_Object**) &TEAVM_GET_STRING(" + simpleNameIndex + ")";
}
includes.includePath("strings.h"); includes.includePath("strings.h");
codeWriter.println(".size = " + sizeExpr + ","); codeWriter.println(".size = " + sizeExpr + ",");
codeWriter.println(".flags = " + flags + ","); codeWriter.println(".flags = " + flags + ",");
codeWriter.println(".tag = " + tag + ","); codeWriter.println(".tag = " + tag + ",");
codeWriter.println(".canary = 0,"); codeWriter.println(".canary = 0,");
codeWriter.println(".name = (TeaVM_Object**) &TEAVM_GET_STRING(" + nameRef + "),"); codeWriter.println(".name = (TeaVM_Object**) &TEAVM_GET_STRING(" + nameRef + "),");
codeWriter.println(".simpleName = NULL,"); codeWriter.println(".simpleName = " + simpleName + ",");
codeWriter.println(".arrayType = " + arrayTypeExpr + ","); codeWriter.println(".arrayType = " + arrayTypeExpr + ",");
codeWriter.println(".itemType = " + itemTypeExpr + ","); codeWriter.println(".itemType = " + itemTypeExpr + ",");
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ","); codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
@ -781,6 +812,8 @@ public class ClassGenerator {
codeWriter.println(".superinterfaces = " + superinterfaces + ","); codeWriter.println(".superinterfaces = " + superinterfaces + ",");
codeWriter.println(".layout = " + layout + ","); codeWriter.println(".layout = " + layout + ",");
codeWriter.println(".enumValues = " + enumConstants + ","); codeWriter.println(".enumValues = " + enumConstants + ",");
codeWriter.println(".declaringClass = " + declaringClass + ",");
codeWriter.println(".enclosingClass = " + enclosingClass + ",");
codeWriter.print(".init = " + initFunction); codeWriter.print(".init = " + initFunction);
if (context.isHeapDump() && type instanceof ValueType.Object) { if (context.isHeapDump() && type instanceof ValueType.Object) {
@ -1161,7 +1194,7 @@ public class ClassGenerator {
throw new AssertionError(); throw new AssertionError();
} }
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
return nameOfType(((ValueType.Array) type).getItemType()) + "[]"; return type.toString().replace('/', '.');
} else if (type == ValueType.VOID) { } else if (type == ValueType.VOID) {
return "void"; return "void";
} else if (type instanceof ValueType.Object) { } else if (type instanceof ValueType.Object) {

View File

@ -802,7 +802,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
private boolean isWrappedNativeCall(MethodReader method) { private boolean isWrappedNativeCall(MethodReader method) {
if (!method.hasModifier(ElementModifier.NATIVE)) { if (!method.hasModifier(ElementModifier.NATIVE)
|| method.getAnnotations().get(DelegateTo.class.getName()) != null) {
return false; return false;
} }
if (method.getAnnotations().get(Variable.class.getName()) != null) { if (method.getAnnotations().get(Variable.class.getName()) != null) {

View File

@ -28,6 +28,12 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
RuntimeClass.class.getName(), "parent"); RuntimeClass.class.getName(), "parent");
private static final FieldReference NAME_FIELD = new FieldReference( private static final FieldReference NAME_FIELD = new FieldReference(
RuntimeClass.class.getName(), "name"); RuntimeClass.class.getName(), "name");
private static final FieldReference SIMPLE_NAME_FIELD = new FieldReference(
RuntimeClass.class.getName(), "simpleName");
private static final FieldReference DECLARING_CLASS_FIELD = new FieldReference(
RuntimeClass.class.getName(), "declaringClass");
private static final FieldReference ENCLOSING_CLASS_FIELD = new FieldReference(
RuntimeClass.class.getName(), "enclosingClass");
@Override @Override
public boolean canHandle(MethodReference method) { public boolean canHandle(MethodReference method) {
@ -38,6 +44,9 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
case "getArrayItem": case "getArrayItem":
case "getSuperclass": case "getSuperclass":
case "getName": case "getName":
case "getSimpleName":
case "getDeclaringClass":
case "getEnclosingClass":
return true; return true;
} }
return false; return false;
@ -56,6 +65,19 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
context.writer().print("*"); context.writer().print("*");
printFieldAccess(context, invocation, NAME_FIELD); printFieldAccess(context, invocation, NAME_FIELD);
break; break;
case "getSimpleName":
context.writer().print("(");
printFieldAccess(context, invocation, SIMPLE_NAME_FIELD);
context.writer().print(" ? *");
printFieldAccess(context, invocation, SIMPLE_NAME_FIELD);
context.writer().print(" : NULL)");
break;
case "getDeclaringClass":
printFieldAccess(context, invocation, DECLARING_CLASS_FIELD);
break;
case "getEnclosingClass":
printFieldAccess(context, invocation, ENCLOSING_CLASS_FIELD);
break;
} }
} }

View File

@ -475,7 +475,7 @@ public class Renderer implements RenderingManager {
return; return;
} }
Set<String> classesRequiringName = findClassesRequiringName(); Map<String, RequiredClassMetadata> classesRequiringName = findRequiredClassMetadata();
int start = writer.getOffset(); int start = writer.getOffset();
try { try {
@ -496,7 +496,7 @@ public class Renderer implements RenderingManager {
} }
private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes, private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes,
Set<String> classesRequiringName) throws IOException { Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
writer.append("$rt_metadata(["); writer.append("$rt_metadata([");
boolean first = true; boolean first = true;
for (PreparedClass cls : classes) { for (PreparedClass cls : classes) {
@ -507,7 +507,8 @@ public class Renderer implements RenderingManager {
debugEmitter.emitClass(cls.getName()); debugEmitter.emitClass(cls.getName());
writer.appendClass(cls.getName()).append(",").ws(); writer.appendClass(cls.getName()).append(",").ws();
if (classesRequiringName.contains(cls.getName())) { RequiredClassMetadata requiredMetadata = metadataRequirements.get(cls.getName());
if (requiredMetadata != null && requiredMetadata.name) {
String className = cls.getName(); String className = cls.getName();
int dotIndex = className.lastIndexOf('.') + 1; int dotIndex = className.lastIndexOf('.') + 1;
String packageName = className.substring(0, dotIndex); String packageName = className.substring(0, dotIndex);
@ -539,6 +540,33 @@ public class Renderer implements RenderingManager {
writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws(); writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws();
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws(); writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
if (requiredMetadata == null || !(requiredMetadata.enclosingClass
|| requiredMetadata.declaringClass || requiredMetadata.simpleName)) {
writer.append("0");
} else {
writer.append('[');
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) {
writer.appendClass(cls.getClassHolder().getDeclaringClassName());
} else {
writer.append('0');
}
writer.append(',');
if (requiredMetadata.simpleName && cls.getClassHolder().getSimpleName() != null) {
writer.append("\"").append(RenderingUtil.escapeString(cls.getClassHolder().getSimpleName()))
.append("\"");
} else {
writer.append('0');
}
writer.append(']');
}
writer.append(",").ws();
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD); MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
if (clinit != null && context.isDynamicInitializer(cls.getName())) { if (clinit != null && context.isDynamicInitializer(cls.getName())) {
writer.appendClassInit(cls.getName()); writer.appendClassInit(cls.getName());
@ -561,13 +589,14 @@ public class Renderer implements RenderingManager {
writer.append("]);").newLine(); writer.append("]);").newLine();
} }
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes, Set<String> classesRequiringName) private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes,
throws IOException { Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
PackageNode root = new PackageNode(null); PackageNode root = new PackageNode(null);
for (PreparedClass classNode : classes) { for (PreparedClass classNode : classes) {
String className = classNode.getName(); String className = classNode.getName();
if (!classesRequiringName.contains(className)) { RequiredClassMetadata requiredMetadata = metadataRequirements.get(className);
if (requiredMetadata == null || !requiredMetadata.name) {
continue; continue;
} }
@ -626,23 +655,50 @@ public class Renderer implements RenderingManager {
} }
} }
private Set<String> findClassesRequiringName() { private Map<String, RequiredClassMetadata> findRequiredClassMetadata() {
Set<String> classesRequiringName = new HashSet<>(); Map<String, RequiredClassMetadata> requirements = new HashMap<>();
MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod( MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getName", String.class)); new MethodReference(Class.class, "getName", String.class));
if (getNameMethod != null) { if (getNameMethod != null) {
addClassesRequiringName(classesRequiringName, getNameMethod.getVariable(0).getClassValueNode().getTypes()); addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes());
} }
MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod( MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod(
new MethodReference(Class.class, "getSimpleName", String.class)); new MethodReference(Class.class, "getSimpleName", String.class));
if (getSimpleNameMethod != null) { if (getSimpleNameMethod != null) {
addClassesRequiringName(classesRequiringName, String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes();
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;
}
} }
return classesRequiringName;
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(Set<String> target, String[] source) { private void addClassesRequiringName(Map<String, RequiredClassMetadata> target, String[] source) {
for (String typeName : source) { for (String typeName : source) {
if (typeName.startsWith("[")) { if (typeName.startsWith("[")) {
if (!typeName.endsWith(";")) { if (!typeName.endsWith(";")) {
@ -654,7 +710,7 @@ public class Renderer implements RenderingManager {
} }
typeName = typeName.substring(index, typeName.length() - 1).replace('/', '.'); typeName = typeName.substring(index, typeName.length() - 1).replace('/', '.');
} }
target.add(typeName); target.computeIfAbsent(typeName, k -> new RequiredClassMetadata()).name = true;
} }
} }
@ -1161,4 +1217,11 @@ public class Renderer implements RenderingManager {
private boolean isVirtual(MethodReference method) { private boolean isVirtual(MethodReference method) {
return context.isVirtual(method); return context.isVirtual(method);
} }
static class RequiredClassMetadata {
boolean name;
boolean simpleName;
boolean declaringClass;
boolean enclosingClass;
}
} }

View File

@ -74,6 +74,8 @@ public class WasmClassGenerator {
DataPrimitives.ADDRESS, /* name */ DataPrimitives.ADDRESS, /* name */
DataPrimitives.ADDRESS, /* item type */ DataPrimitives.ADDRESS, /* item type */
DataPrimitives.ADDRESS, /* array type */ DataPrimitives.ADDRESS, /* array type */
DataPrimitives.ADDRESS, /* declaring class */
DataPrimitives.ADDRESS, /* enclosing class */
DataPrimitives.INT, /* isInstance function */ DataPrimitives.INT, /* isInstance function */
DataPrimitives.INT, /* init function */ DataPrimitives.INT, /* init function */
DataPrimitives.ADDRESS, /* parent */ DataPrimitives.ADDRESS, /* parent */
@ -81,7 +83,9 @@ public class WasmClassGenerator {
DataPrimitives.ADDRESS, /* interfaces */ DataPrimitives.ADDRESS, /* interfaces */
DataPrimitives.ADDRESS, /* enum values */ DataPrimitives.ADDRESS, /* enum values */
DataPrimitives.ADDRESS, /* layout */ DataPrimitives.ADDRESS, /* layout */
DataPrimitives.ADDRESS /* simple name */); DataPrimitives.ADDRESS, /* simple name */
DataPrimitives.ADDRESS, /* simple name cache */
DataPrimitives.ADDRESS /* canonical name cache */);
private IntegerArray staticGcRoots = new IntegerArray(1); private IntegerArray staticGcRoots = new IntegerArray(1);
private int staticGcRootsAddress; private int staticGcRootsAddress;
@ -92,12 +96,14 @@ public class WasmClassGenerator {
private static final int CLASS_NAME = 5; private static final int CLASS_NAME = 5;
private static final int CLASS_ITEM_TYPE = 6; private static final int CLASS_ITEM_TYPE = 6;
private static final int CLASS_ARRAY_TYPE = 7; private static final int CLASS_ARRAY_TYPE = 7;
private static final int CLASS_IS_INSTANCE = 8; private static final int CLASS_DECLARING_CLASS = 8;
private static final int CLASS_INIT = 9; private static final int CLASS_ENCLOSING_CLASS = 9;
private static final int CLASS_PARENT = 10; private static final int CLASS_IS_INSTANCE = 10;
private static final int CLASS_ENUM_VALUES = 13; private static final int CLASS_INIT = 11;
private static final int CLASS_LAYOUT = 14; private static final int CLASS_PARENT = 12;
private static final int CLASS_SIMPLE_NAME = 15; private static final int CLASS_ENUM_VALUES = 15;
private static final int CLASS_LAYOUT = 16;
private static final int CLASS_SIMPLE_NAME = 17;
public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource, public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource,
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter,
@ -275,6 +281,19 @@ public class WasmClassGenerator {
functionTable.add(names.forSupertypeFunction(ValueType.object(name))); functionTable.add(names.forSupertypeFunction(ValueType.object(name)));
header.setAddress(CLASS_PARENT, parentPtr); header.setAddress(CLASS_PARENT, parentPtr);
ClassReader cls = processedClassSource.get(name);
if (cls.getSimpleName() != null) {
header.setAddress(CLASS_SIMPLE_NAME, stringPool.getStringPointer(cls.getSimpleName()));
}
if (cls.getOwnerName() != null && processedClassSource.get(cls.getOwnerName()) != null) {
header.setAddress(CLASS_ENCLOSING_CLASS, getClassPointer(ValueType.object(cls.getOwnerName())));
}
if (cls.getDeclaringClassName() != null && processedClassSource.get(cls.getDeclaringClassName()) != null) {
header.setAddress(CLASS_DECLARING_CLASS, getClassPointer(ValueType.object(cls.getDeclaringClassName())));
}
if (vtable != null) { if (vtable != null) {
fillVirtualTable(vtable, array); fillVirtualTable(vtable, array);
} }
@ -296,10 +315,14 @@ public class WasmClassGenerator {
staticGcRoots.add(binaryData.fieldLayout.get(field.getFieldName())); staticGcRoots.add(binaryData.fieldLayout.get(field.getFieldName()));
} }
ClassReader cls = processedClassSource.get(name); if (cls != null) {
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { if (cls.hasModifier(ElementModifier.ENUM)) {
header.setAddress(CLASS_ENUM_VALUES, generateEnumValues(cls, binaryData)); header.setAddress(CLASS_ENUM_VALUES, generateEnumValues(cls, binaryData));
flags |= RuntimeClass.ENUM; flags |= RuntimeClass.ENUM;
}
if (cls.hasModifier(ElementModifier.SYNTHETIC)) {
flags |= RuntimeClass.SYNTHETIC;
}
} }
if (cls != null && binaryData.start >= 0 if (cls != null && binaryData.start >= 0
@ -311,7 +334,6 @@ public class WasmClassGenerator {
} }
header.setInt(CLASS_FLAGS, flags); header.setInt(CLASS_FLAGS, flags);
header.setAddress(CLASS_SIMPLE_NAME, 0);
return vtable != null ? wrapper : header; return vtable != null ? wrapper : header;
} }

View File

@ -25,12 +25,13 @@ import org.teavm.runtime.RuntimeClass;
public class PlatformClassMetadataIntrinsic implements WasmIntrinsic { public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata"; private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata";
private static final FieldReference ITEM_TYPE_FIELD = new FieldReference( private static final String RUNTIME_CLASS = RuntimeClass.class.getName();
RuntimeClass.class.getName(), "itemType"); private static final FieldReference ITEM_TYPE_FIELD = new FieldReference(RUNTIME_CLASS, "itemType");
private static final FieldReference SUPERCLASS_FIELD = new FieldReference( private static final FieldReference SUPERCLASS_FIELD = new FieldReference(RUNTIME_CLASS, "parent");
RuntimeClass.class.getName(), "parent"); private static final FieldReference NAME_FIELD = new FieldReference(RUNTIME_CLASS, "name");
private static final FieldReference NAME_FIELD = new FieldReference( private static final FieldReference SIMPLE_NAME_FIELD = new FieldReference(RUNTIME_CLASS, "simpleName");
RuntimeClass.class.getName(), "name"); private static final FieldReference ENCLOSING_CLASS_FIELD = new FieldReference(RUNTIME_CLASS, "enclosingClass");
private static final FieldReference DECLARING_CLASS_FIELD = new FieldReference(RUNTIME_CLASS, "declaringClass");
@Override @Override
public boolean isApplicable(MethodReference methodReference) { public boolean isApplicable(MethodReference methodReference) {
@ -41,6 +42,9 @@ public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
case "getArrayItem": case "getArrayItem":
case "getSuperclass": case "getSuperclass":
case "getName": case "getName":
case "getSimpleName":
case "getEnclosingClass":
case "getDeclaringClass":
return true; return true;
} }
return false; return false;
@ -55,6 +59,12 @@ public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
return fieldAccess(manager, invocation, SUPERCLASS_FIELD); return fieldAccess(manager, invocation, SUPERCLASS_FIELD);
case "getName": case "getName":
return fieldAccess(manager, invocation, NAME_FIELD); return fieldAccess(manager, invocation, NAME_FIELD);
case "getSimpleName":
return fieldAccess(manager, invocation, SIMPLE_NAME_FIELD);
case "getEnclosingClass":
return fieldAccess(manager, invocation, ENCLOSING_CLASS_FIELD);
case "getDeclaringClass":
return fieldAccess(manager, invocation, DECLARING_CLASS_FIELD);
default: default:
return new WasmUnreachable(); return new WasmUnreachable();
} }

View File

@ -30,6 +30,8 @@ class CachedClassReader extends CachedElement implements ClassReader {
GenericTypeParameter[] parameters; GenericTypeParameter[] parameters;
GenericValueType.Object genericParent; GenericValueType.Object genericParent;
String owner; String owner;
String declaringClass;
String simpleName;
Set<String> interfaces; Set<String> interfaces;
Set<GenericValueType.Object> genericInterfaces; Set<GenericValueType.Object> genericInterfaces;
Map<MethodDescriptor, CachedMethod> methods; Map<MethodDescriptor, CachedMethod> methods;
@ -84,4 +86,14 @@ class CachedClassReader extends CachedElement implements ClassReader {
public String getOwnerName() { public String getOwnerName() {
return owner; return owner;
} }
@Override
public String getSimpleName() {
return simpleName;
}
@Override
public String getDeclaringClassName() {
return declaringClass;
}
} }

View File

@ -59,6 +59,9 @@ public class ClassIO {
output.writeUnsigned(packModifiers(cls.readModifiers())); output.writeUnsigned(packModifiers(cls.readModifiers()));
output.writeUnsigned(cls.getParent() != null ? symbolTable.lookup(cls.getParent()) + 1 : 0); output.writeUnsigned(cls.getParent() != null ? symbolTable.lookup(cls.getParent()) + 1 : 0);
output.writeUnsigned(cls.getOwnerName() != null ? symbolTable.lookup(cls.getOwnerName()) + 1 : 0); output.writeUnsigned(cls.getOwnerName() != null ? symbolTable.lookup(cls.getOwnerName()) + 1 : 0);
output.writeUnsigned(cls.getDeclaringClassName() != null
? symbolTable.lookup(cls.getDeclaringClassName()) + 1 : 0);
output.writeUnsigned(cls.getSimpleName() != null ? symbolTable.lookup(cls.getSimpleName()) + 1 : 0);
output.writeUnsigned(cls.getInterfaces().size()); output.writeUnsigned(cls.getInterfaces().size());
for (String iface : cls.getInterfaces()) { for (String iface : cls.getInterfaces()) {
output.writeUnsigned(symbolTable.lookup(iface)); output.writeUnsigned(symbolTable.lookup(iface));
@ -84,6 +87,11 @@ public class ClassIO {
cls.parent = parentIndex > 0 ? referenceCache.getCached(symbolTable.at(parentIndex - 1)) : null; cls.parent = parentIndex > 0 ? referenceCache.getCached(symbolTable.at(parentIndex - 1)) : null;
int ownerIndex = input.readUnsigned(); int ownerIndex = input.readUnsigned();
cls.owner = ownerIndex > 0 ? referenceCache.getCached(symbolTable.at(ownerIndex - 1)) : null; cls.owner = ownerIndex > 0 ? referenceCache.getCached(symbolTable.at(ownerIndex - 1)) : null;
int declaringClassIndex = input.readUnsigned();
cls.declaringClass = declaringClassIndex > 0
? referenceCache.getCached(symbolTable.at(declaringClassIndex - 1)) : null;
int simpleNameIndex = input.readUnsigned();
cls.simpleName = simpleNameIndex > 0 ? referenceCache.getCached(symbolTable.at(simpleNameIndex - 1)) : null;
int ifaceCount = input.readUnsigned(); int ifaceCount = input.readUnsigned();
Set<String> interfaces = new LinkedHashSet<>(); Set<String> interfaces = new LinkedHashSet<>();
for (int i = 0; i < ifaceCount; ++i) { for (int i = 0; i < ifaceCount; ++i) {

View File

@ -26,6 +26,8 @@ public class ClassHolder extends ElementHolder implements ClassReader {
private Map<MethodDescriptor, MethodHolder> methods = new LinkedHashMap<>(); private Map<MethodDescriptor, MethodHolder> methods = new LinkedHashMap<>();
private Map<String, FieldHolder> fields = new LinkedHashMap<>(); private Map<String, FieldHolder> fields = new LinkedHashMap<>();
private String ownerName; private String ownerName;
private String simpleName;
private String declaringClassName;
public ClassHolder(String name) { public ClassHolder(String name) {
super(name); super(name);
@ -138,4 +140,22 @@ public class ClassHolder extends ElementHolder implements ClassReader {
public void setOwnerName(String ownerName) { public void setOwnerName(String ownerName) {
this.ownerName = ownerName; this.ownerName = ownerName;
} }
@Override
public String getSimpleName() {
return simpleName;
}
public void setSimpleName(String simpleName) {
this.simpleName = simpleName;
}
@Override
public String getDeclaringClassName() {
return declaringClassName;
}
public void setDeclaringClassName(String declaringClassName) {
this.declaringClassName = declaringClassName;
}
} }

View File

@ -38,4 +38,8 @@ public interface ClassReader extends ElementReader {
Collection<? extends FieldReader> getFields(); Collection<? extends FieldReader> getFields();
String getOwnerName(); String getOwnerName();
String getSimpleName();
String getDeclaringClassName();
} }

View File

@ -49,6 +49,8 @@ public final class ModelUtils {
target.addField(copyField(field)); target.addField(copyField(field));
} }
target.setOwnerName(original.getOwnerName()); target.setOwnerName(original.getOwnerName());
target.setDeclaringClassName(original.getDeclaringClassName());
target.setSimpleName(original.getSimpleName());
copyAnnotations(original.getAnnotations(), target.getAnnotations()); copyAnnotations(original.getAnnotations(), target.getAnnotations());
return target; return target;
} }

View File

@ -89,6 +89,9 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
if (cls.getOwnerName() != null) { if (cls.getOwnerName() != null) {
renamedCls.setOwnerName(classNameMapper.apply(cls.getOwnerName())); renamedCls.setOwnerName(classNameMapper.apply(cls.getOwnerName()));
} }
if (cls.getDeclaringClassName() != null) {
renamedCls.setDeclaringClassName(classNameMapper.apply(cls.getDeclaringClassName()));
}
rename(cls.getAnnotations(), renamedCls.getAnnotations()); rename(cls.getAnnotations(), renamedCls.getAnnotations());
for (String iface : cls.getInterfaces()) { for (String iface : cls.getInterfaces()) {
String mappedIfaceName = classNameMapper.apply(iface); String mappedIfaceName = classNameMapper.apply(iface);

View File

@ -29,6 +29,7 @@ import org.objectweb.asm.commons.JSRInlinerAdapter;
import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import org.teavm.common.Graph; import org.teavm.common.Graph;
import org.teavm.common.GraphUtils; import org.teavm.common.GraphUtils;
@ -302,14 +303,24 @@ public class Parser {
cls.addMethod(method); cls.addMethod(method);
method.updateReference(referenceCache); method.updateReference(referenceCache);
} }
if (node.outerClass != null) { if (node.outerClass != null) {
cls.setOwnerName(node.outerClass.replace('/', '.')); cls.setOwnerName(node.outerClass.replace('/', '.'));
} else { }
int lastIndex = node.name.lastIndexOf('$');
if (lastIndex != -1) { if (node.innerClasses != null && !node.innerClasses.isEmpty()) {
cls.setOwnerName(node.name.substring(0, lastIndex).replace('/', '.')); for (InnerClassNode innerClassNode : node.innerClasses) {
if (node.name.equals(innerClassNode.name)) {
if (innerClassNode.outerName != null) {
cls.setDeclaringClassName(innerClassNode.outerName.replace('/', '.'));
cls.setOwnerName(cls.getDeclaringClassName());
}
cls.setSimpleName(innerClassNode.innerName);
break;
}
} }
} }
parseAnnotations(cls.getAnnotations(), node.visibleAnnotations, node.invisibleAnnotations); parseAnnotations(cls.getAnnotations(), node.visibleAnnotations, node.invisibleAnnotations);
return cls; return cls;
} }

View File

@ -22,6 +22,7 @@ public class RuntimeClass extends RuntimeObject {
public static final int INITIALIZED = 1; public static final int INITIALIZED = 1;
public static final int PRIMITIVE = 2; public static final int PRIMITIVE = 2;
public static final int ENUM = 4; public static final int ENUM = 4;
public static final int SYNTHETIC = 1024;
public static final int PRIMITIVE_SHIFT = 3; public static final int PRIMITIVE_SHIFT = 3;
public static final int PRIMITIVE_MASK = 15; public static final int PRIMITIVE_MASK = 15;
@ -49,6 +50,8 @@ public class RuntimeClass extends RuntimeObject {
public RuntimeObjectPtr name; public RuntimeObjectPtr name;
public RuntimeClass itemType; public RuntimeClass itemType;
public RuntimeClass arrayType; public RuntimeClass arrayType;
public RuntimeClass declaringClass;
public RuntimeClass enclosingClass;
public IsSupertypeFunction isSupertypeOf; public IsSupertypeFunction isSupertypeOf;
public InitFunction init; public InitFunction init;
public RuntimeClass parent; public RuntimeClass parent;
@ -56,7 +59,9 @@ public class RuntimeClass extends RuntimeObject {
public RuntimeClassPointer superinterfaces; public RuntimeClassPointer superinterfaces;
public Address enumValues; public Address enumValues;
public Address layout; public Address layout;
public RuntimeObject simpleName; public RuntimeObjectPtr simpleName;
public RuntimeObject simpleNameCache;
public RuntimeObject canonicalName;
@Unmanaged @Unmanaged
public static int computeCanary(int size, int tag) { public static int computeCanary(int size, int tag) {

View File

@ -18,5 +18,5 @@ package org.teavm.runtime;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
public class RuntimeObjectPtr extends Structure { public class RuntimeObjectPtr extends Structure {
RuntimeObject value; public RuntimeObject value;
} }

View File

@ -30,6 +30,8 @@ typedef struct TeaVM_Class {
TeaVM_Object** name; TeaVM_Object** name;
struct TeaVM_Class* itemType; struct TeaVM_Class* itemType;
struct TeaVM_Class* arrayType; struct TeaVM_Class* arrayType;
struct TeaVM_Class* declaringClass;
struct TeaVM_Class* enclosingClass;
int32_t (*isSupertypeOf)(struct TeaVM_Class*); int32_t (*isSupertypeOf)(struct TeaVM_Class*);
void (*init)(); void (*init)();
struct TeaVM_Class* superclass; struct TeaVM_Class* superclass;
@ -37,7 +39,9 @@ typedef struct TeaVM_Class {
struct TeaVM_Class** superinterfaces; struct TeaVM_Class** superinterfaces;
void* enumValues; void* enumValues;
void* layout; void* layout;
TeaVM_Object* simpleName; TeaVM_Object** simpleName;
TeaVM_Object* simpleNameCache;
TeaVM_Object* canonicalName;
#if TEAVM_HEAP_DUMP #if TEAVM_HEAP_DUMP
TeaVM_FieldDescriptors* fieldDescriptors; TeaVM_FieldDescriptors* fieldDescriptors;
TeaVM_StaticFieldDescriptors* staticFieldDescriptors; TeaVM_StaticFieldDescriptors* staticFieldDescriptors;

View File

@ -108,8 +108,18 @@ function $rt_arraycls(cls) {
if (result === null) { if (result === null) {
var arraycls = {}; var arraycls = {};
var name = "[" + cls.$meta.binaryName; var name = "[" + cls.$meta.binaryName;
arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls(), arraycls.$meta = {
name : name, binaryName : name, enum : false }; item: cls,
supertypes: [$rt_objcls()],
primitive: false,
superclass: $rt_objcls(),
name: name,
binaryName: name,
enum: false,
simpleName: null,
declaringClass: null,
enclosingClass: null
};
arraycls.classObject = null; arraycls.classObject = null;
arraycls.$array = null; arraycls.$array = null;
result = arraycls; result = arraycls;
@ -121,7 +131,7 @@ function $rt_createcls() {
return { return {
$array : null, $array : null,
classObject : null, classObject : null,
$meta : { $meta: {
supertypes : [], supertypes : [],
superclass : null superclass : null
} }
@ -134,6 +144,9 @@ function $rt_createPrimitiveCls(name, binaryName) {
cls.$meta.binaryName = binaryName; cls.$meta.binaryName = binaryName;
cls.$meta.enum = false; cls.$meta.enum = false;
cls.$meta.item = null; cls.$meta.item = null;
cls.$meta.simpleName = null;
cls.$meta.declaringClass = null;
cls.$meta.enclosingClass = null;
return cls; return cls;
} }
var $rt_booleanclsCache = null; var $rt_booleanclsCache = null;
@ -453,6 +466,20 @@ function $rt_metadata(data) {
m.accessLevel = data[i++]; m.accessLevel = data[i++];
var innerClassInfo = data[i++];
if (innerClassInfo === 0) {
m.simpleName = null;
m.declaringClass = null;
m.enclosingClass = null;
} else {
var enclosingClass = innerClassInfo[0];
m.enclosingClass = enclosingClass !== 0 ? enclosingClass : null;
var declaringClass = innerClassInfo[1];
m.declaringClass = declaringClass !== 0 ? declaringClass : null;
var simpleName = innerClassInfo[2];
m.simpleName = simpleName !== 0 ? simpleName : null;
}
var clinit = data[i++]; var clinit = data[i++];
cls.$clinit = clinit !== 0 ? clinit : function() {}; cls.$clinit = clinit !== 0 ? clinit : function() {};

View File

@ -246,6 +246,24 @@ public final class Platform {
return cls.getMetadata().getName(); return cls.getMetadata().getName();
} }
@Unmanaged
@PluggableDependency(PlatformGenerator.class)
public static String getSimpleName(PlatformClass cls) {
return cls.getMetadata().getSimpleName();
}
@Unmanaged
@PluggableDependency(PlatformGenerator.class)
public static PlatformClass getEnclosingClass(PlatformClass cls) {
return cls.getMetadata().getEnclosingClass();
}
@Unmanaged
@PluggableDependency(PlatformGenerator.class)
public static PlatformClass getDeclaringClass(PlatformClass cls) {
return cls.getMetadata().getDeclaringClass();
}
@PlatformMarker(Platforms.LOW_LEVEL) @PlatformMarker(Platforms.LOW_LEVEL)
private static boolean isLowLevel() { private static boolean isLowLevel() {
return false; return false;

View File

@ -46,4 +46,16 @@ public interface PlatformClassMetadata extends JSObject {
@JSProperty @JSProperty
int getAccessLevel(); int getAccessLevel();
@JSProperty
@Unmanaged
String getSimpleName();
@JSProperty
@Unmanaged
PlatformClass getEnclosingClass();
@JSProperty
@Unmanaged
PlatformClass getDeclaringClass();
} }

View File

@ -60,8 +60,13 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
method.getResult().propagate(agent.getType("[Ljava/lang/Enum;")); method.getResult().propagate(agent.getType("[Ljava/lang/Enum;"));
break; break;
case "getName": case "getName":
case "getSimpleName":
method.getResult().propagate(agent.getType("java.lang.String")); method.getResult().propagate(agent.getType("java.lang.String"));
break; break;
case "getEnclosingClass":
case "getDeclaringClass":
method.getResult().propagate(agent.getType("java.lang.Class"));
break;
} }
} }

View File

@ -39,12 +39,7 @@ public class ClassTest {
@Test @Test
public void classSimpleNameEvaluated() { public void classSimpleNameEvaluated() {
assertEquals("Object", Object.class.getSimpleName());
assertEquals("Object[]", Object[].class.getSimpleName());
assertEquals("int", int.class.getSimpleName());
assertEquals("int[]", int[].class.getSimpleName());
assertEquals("InnerClass", InnerClass.class.getSimpleName());
assertEquals("", new Object() { }.getClass().getSimpleName());
} }
@Test @Test
@ -126,8 +121,36 @@ public class ClassTest {
} }
@Test @Test
public void declaringClassFound() { public void classProperties() {
assertEquals(ClassTest.class, new A().getClass().getDeclaringClass()); class B {
}
@SuppressWarnings("Convert2Lambda")
Runnable r = new Runnable() {
@Override
public void run() {
}
};
String testName = "org.teavm.classlib.java.lang.ClassTest";
testClassProperties(getClass(), "ClassTest", testName, null, null);
testClassProperties(new ClassTest[0].getClass(), "ClassTest[]", testName + "[]", null, null);
testClassProperties(int.class, "int", "int", null, null);
testClassProperties(new int[0].getClass(), "int[]", "int[]", null, null);
testClassProperties(new A().getClass(), "A", testName + ".A", ClassTest.class, ClassTest.class);
testClassProperties(new A[0].getClass(), "A[]", testName + ".A[]", null, null);
testClassProperties(new B().getClass(), "B", null, null, ClassTest.class);
testClassProperties(new B[0].getClass(), "B[]", null, null, null);
testClassProperties(r.getClass(), "", null, null, ClassTest.class);
}
private void testClassProperties(Class<?> cls, String expectedSimpleName, String expectedCanonicalName,
Class<?> expectedDeclaringClass, Class<?> expectedEnclosingClass) {
assertEquals(expectedSimpleName, cls.getSimpleName());
assertEquals(expectedCanonicalName, cls.getCanonicalName());
assertEquals(expectedDeclaringClass, cls.getDeclaringClass());
assertEquals(expectedEnclosingClass, cls.getEnclosingClass());
} }
@Test @Test