mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add support for Class.getCanonicalName/getEnclosingClass
This commit is contained in:
parent
28212f70ff
commit
8a91605c56
|
@ -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;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user