mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-12-15 02:10:30 +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 {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getReference().getClassName().equals("java.lang.Class")
|
||||
&& method.getReference().getName().equals("getDeclaringClass")) {
|
||||
method.getVariable(0).getClassValueNode().addConsumer(t -> {
|
||||
ClassReader cls = agent.getClassSource().get(t.getName());
|
||||
if (cls != null && cls.getOwnerName() != null) {
|
||||
agent.linkClass(cls.getOwnerName());
|
||||
if (method.getReference().getClassName().equals("java.lang.Class")) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "getEnclosingClass":
|
||||
method.getVariable(0).getClassValueNode().addConsumer(t -> {
|
||||
ClassReader cls = agent.getClassSource().get(t.getName());
|
||||
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));
|
||||
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) {
|
||||
|
@ -53,6 +53,7 @@ import org.teavm.runtime.RuntimeObject;
|
||||
public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||
String name;
|
||||
String simpleName;
|
||||
String canonicalName;
|
||||
private PlatformClass platformClass;
|
||||
private TAnnotation[] annotationsCache;
|
||||
private Map<TClass<?>, TAnnotation> annotationsByType;
|
||||
@ -115,51 +116,110 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||
}
|
||||
|
||||
public String getSimpleName() {
|
||||
String simpleName = getSimpleNameCache();
|
||||
String simpleName = getSimpleNameCache(this);
|
||||
if (simpleName == null) {
|
||||
if (isArray()) {
|
||||
simpleName = getComponentType().getSimpleName() + "[]";
|
||||
setSimpleNameCache(simpleName);
|
||||
return simpleName;
|
||||
}
|
||||
String name = Platform.getName(platformClass);
|
||||
int lastDollar = name.lastIndexOf('$');
|
||||
if (lastDollar != -1) {
|
||||
name = name.substring(lastDollar + 1);
|
||||
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
|
||||
name = "";
|
||||
} else if (getEnclosingClass() != null) {
|
||||
simpleName = Platform.getSimpleName(platformClass);
|
||||
if (simpleName == null) {
|
||||
simpleName = "";
|
||||
}
|
||||
} else {
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
name = name.substring(lastDot + 1);
|
||||
String name = Platform.getName(platformClass);
|
||||
int lastDollar = name.lastIndexOf('$');
|
||||
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(simpleName);
|
||||
setSimpleNameCache(this, simpleName);
|
||||
}
|
||||
return simpleName;
|
||||
}
|
||||
|
||||
@DelegateTo("getSimpleNameCacheLowLevel")
|
||||
private String getSimpleNameCache() {
|
||||
return simpleName;
|
||||
private static String getSimpleNameCache(TClass<?> self) {
|
||||
return self.simpleName;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
@PluggableDependency(ClassDependencyListener.class)
|
||||
private RuntimeObject getSimpleNameCacheLowLevel() {
|
||||
return Address.ofObject(this).<RuntimeClass>toStructure().simpleName;
|
||||
private static RuntimeObject getSimpleNameCacheLowLevel(RuntimeClass self) {
|
||||
return self.simpleNameCache;
|
||||
}
|
||||
|
||||
@DelegateTo("setSimpleNameCacheLowLevel")
|
||||
private void setSimpleNameCache(String value) {
|
||||
simpleName = value;
|
||||
private static void setSimpleNameCache(TClass<?> self, String value) {
|
||||
self.simpleName = value;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private void setSimpleNameCacheLowLevel(RuntimeObject object) {
|
||||
Address.ofObject(this).<RuntimeClass>toStructure().simpleName = object;
|
||||
private static void setSimpleNameCacheLowLevel(RuntimeClass self, RuntimeObject 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() {
|
||||
@ -178,6 +238,15 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||
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)
|
||||
public TClass<?> getComponentType() {
|
||||
return getClass(Platform.getArrayItem(platformClass));
|
||||
@ -597,11 +666,14 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static native PlatformClass getDeclaringClassImpl(PlatformClass cls);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
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",
|
||||
"arrayType", "isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
|
||||
"superinterfaces");
|
||||
"superinterfaces", "simpleNameCache", "declaringClass", "enclosingClass", "canonicalName");
|
||||
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");
|
||||
preserveFieldNames(RuntimeReference.class.getName(), "queue", "object", "next");
|
||||
preserveFieldNames(RuntimeReferenceQueue.class.getName(), "first", "last");
|
||||
|
@ -667,6 +667,9 @@ public class ClassGenerator {
|
||||
String initFunction = "NULL";
|
||||
String superinterfaceCount = "0";
|
||||
String superinterfaces = "NULL";
|
||||
String simpleName = null;
|
||||
String declaringClass = "NULL";
|
||||
String enclosingClass = "NULL";
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
@ -682,8 +685,13 @@ public class ClassGenerator {
|
||||
} else {
|
||||
sizeExpr = "0";
|
||||
}
|
||||
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
|
||||
flags |= RuntimeClass.ENUM;
|
||||
if (cls != null) {
|
||||
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;
|
||||
tag = !context.isIncremental() && ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
|
||||
@ -730,6 +738,22 @@ public class ClassGenerator {
|
||||
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) {
|
||||
includes.includeClass("java.lang.Object");
|
||||
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
|
||||
@ -766,13 +790,20 @@ public class ClassGenerator {
|
||||
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");
|
||||
codeWriter.println(".size = " + sizeExpr + ",");
|
||||
codeWriter.println(".flags = " + flags + ",");
|
||||
codeWriter.println(".tag = " + tag + ",");
|
||||
codeWriter.println(".canary = 0,");
|
||||
codeWriter.println(".name = (TeaVM_Object**) &TEAVM_GET_STRING(" + nameRef + "),");
|
||||
codeWriter.println(".simpleName = NULL,");
|
||||
codeWriter.println(".simpleName = " + simpleName + ",");
|
||||
codeWriter.println(".arrayType = " + arrayTypeExpr + ",");
|
||||
codeWriter.println(".itemType = " + itemTypeExpr + ",");
|
||||
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
|
||||
@ -781,6 +812,8 @@ public class ClassGenerator {
|
||||
codeWriter.println(".superinterfaces = " + superinterfaces + ",");
|
||||
codeWriter.println(".layout = " + layout + ",");
|
||||
codeWriter.println(".enumValues = " + enumConstants + ",");
|
||||
codeWriter.println(".declaringClass = " + declaringClass + ",");
|
||||
codeWriter.println(".enclosingClass = " + enclosingClass + ",");
|
||||
codeWriter.print(".init = " + initFunction);
|
||||
|
||||
if (context.isHeapDump() && type instanceof ValueType.Object) {
|
||||
@ -1161,7 +1194,7 @@ public class ClassGenerator {
|
||||
throw new AssertionError();
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
return nameOfType(((ValueType.Array) type).getItemType()) + "[]";
|
||||
return type.toString().replace('/', '.');
|
||||
} else if (type == ValueType.VOID) {
|
||||
return "void";
|
||||
} else if (type instanceof ValueType.Object) {
|
||||
|
@ -802,7 +802,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||
}
|
||||
|
||||
private boolean isWrappedNativeCall(MethodReader method) {
|
||||
if (!method.hasModifier(ElementModifier.NATIVE)) {
|
||||
if (!method.hasModifier(ElementModifier.NATIVE)
|
||||
|| method.getAnnotations().get(DelegateTo.class.getName()) != null) {
|
||||
return false;
|
||||
}
|
||||
if (method.getAnnotations().get(Variable.class.getName()) != null) {
|
||||
|
@ -28,6 +28,12 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
|
||||
RuntimeClass.class.getName(), "parent");
|
||||
private static final FieldReference NAME_FIELD = new FieldReference(
|
||||
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
|
||||
public boolean canHandle(MethodReference method) {
|
||||
@ -38,6 +44,9 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
|
||||
case "getArrayItem":
|
||||
case "getSuperclass":
|
||||
case "getName":
|
||||
case "getSimpleName":
|
||||
case "getDeclaringClass":
|
||||
case "getEnclosingClass":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -56,6 +65,19 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
|
||||
context.writer().print("*");
|
||||
printFieldAccess(context, invocation, NAME_FIELD);
|
||||
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;
|
||||
}
|
||||
|
||||
Set<String> classesRequiringName = findClassesRequiringName();
|
||||
Map<String, RequiredClassMetadata> classesRequiringName = findRequiredClassMetadata();
|
||||
|
||||
int start = writer.getOffset();
|
||||
try {
|
||||
@ -496,7 +496,7 @@ public class Renderer implements RenderingManager {
|
||||
}
|
||||
|
||||
private void renderClassMetadataPortion(List<PreparedClass> classes, ObjectIntMap<String> packageIndexes,
|
||||
Set<String> classesRequiringName) throws IOException {
|
||||
Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
|
||||
writer.append("$rt_metadata([");
|
||||
boolean first = true;
|
||||
for (PreparedClass cls : classes) {
|
||||
@ -507,7 +507,8 @@ public class Renderer implements RenderingManager {
|
||||
debugEmitter.emitClass(cls.getName());
|
||||
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();
|
||||
int dotIndex = className.lastIndexOf('.') + 1;
|
||||
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(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);
|
||||
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||
writer.appendClassInit(cls.getName());
|
||||
@ -561,13 +589,14 @@ public class Renderer implements RenderingManager {
|
||||
writer.append("]);").newLine();
|
||||
}
|
||||
|
||||
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes, Set<String> classesRequiringName)
|
||||
throws IOException {
|
||||
private ObjectIntMap<String> generatePackageMetadata(List<PreparedClass> classes,
|
||||
Map<String, RequiredClassMetadata> metadataRequirements) throws IOException {
|
||||
PackageNode root = new PackageNode(null);
|
||||
|
||||
for (PreparedClass classNode : classes) {
|
||||
String className = classNode.getName();
|
||||
if (!classesRequiringName.contains(className)) {
|
||||
RequiredClassMetadata requiredMetadata = metadataRequirements.get(className);
|
||||
if (requiredMetadata == null || !requiredMetadata.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -626,23 +655,50 @@ public class Renderer implements RenderingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> findClassesRequiringName() {
|
||||
Set<String> classesRequiringName = new HashSet<>();
|
||||
private Map<String, RequiredClassMetadata> findRequiredClassMetadata() {
|
||||
Map<String, RequiredClassMetadata> requirements = new HashMap<>();
|
||||
|
||||
MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod(
|
||||
new MethodReference(Class.class, "getName", String.class));
|
||||
if (getNameMethod != null) {
|
||||
addClassesRequiringName(classesRequiringName, getNameMethod.getVariable(0).getClassValueNode().getTypes());
|
||||
addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes());
|
||||
}
|
||||
|
||||
MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod(
|
||||
new MethodReference(Class.class, "getSimpleName", String.class));
|
||||
if (getSimpleNameMethod != null) {
|
||||
addClassesRequiringName(classesRequiringName,
|
||||
getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes());
|
||||
String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes();
|
||||
addClassesRequiringName(requirements, classNames);
|
||||
for (String className : classNames) {
|
||||
RequiredClassMetadata requiredMetadata = requirements.computeIfAbsent(className,
|
||||
k -> new RequiredClassMetadata());
|
||||
requiredMetadata.simpleName = true;
|
||||
requiredMetadata.enclosingClass = true;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (typeName.startsWith("[")) {
|
||||
if (!typeName.endsWith(";")) {
|
||||
@ -654,7 +710,7 @@ public class Renderer implements RenderingManager {
|
||||
}
|
||||
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) {
|
||||
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, /* item type */
|
||||
DataPrimitives.ADDRESS, /* array type */
|
||||
DataPrimitives.ADDRESS, /* declaring class */
|
||||
DataPrimitives.ADDRESS, /* enclosing class */
|
||||
DataPrimitives.INT, /* isInstance function */
|
||||
DataPrimitives.INT, /* init function */
|
||||
DataPrimitives.ADDRESS, /* parent */
|
||||
@ -81,7 +83,9 @@ public class WasmClassGenerator {
|
||||
DataPrimitives.ADDRESS, /* interfaces */
|
||||
DataPrimitives.ADDRESS, /* enum values */
|
||||
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 int staticGcRootsAddress;
|
||||
|
||||
@ -92,12 +96,14 @@ public class WasmClassGenerator {
|
||||
private static final int CLASS_NAME = 5;
|
||||
private static final int CLASS_ITEM_TYPE = 6;
|
||||
private static final int CLASS_ARRAY_TYPE = 7;
|
||||
private static final int CLASS_IS_INSTANCE = 8;
|
||||
private static final int CLASS_INIT = 9;
|
||||
private static final int CLASS_PARENT = 10;
|
||||
private static final int CLASS_ENUM_VALUES = 13;
|
||||
private static final int CLASS_LAYOUT = 14;
|
||||
private static final int CLASS_SIMPLE_NAME = 15;
|
||||
private static final int CLASS_DECLARING_CLASS = 8;
|
||||
private static final int CLASS_ENCLOSING_CLASS = 9;
|
||||
private static final int CLASS_IS_INSTANCE = 10;
|
||||
private static final int CLASS_INIT = 11;
|
||||
private static final int CLASS_PARENT = 12;
|
||||
private static final int CLASS_ENUM_VALUES = 15;
|
||||
private static final int CLASS_LAYOUT = 16;
|
||||
private static final int CLASS_SIMPLE_NAME = 17;
|
||||
|
||||
public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource,
|
||||
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter,
|
||||
@ -275,6 +281,19 @@ public class WasmClassGenerator {
|
||||
functionTable.add(names.forSupertypeFunction(ValueType.object(name)));
|
||||
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) {
|
||||
fillVirtualTable(vtable, array);
|
||||
}
|
||||
@ -296,10 +315,14 @@ public class WasmClassGenerator {
|
||||
staticGcRoots.add(binaryData.fieldLayout.get(field.getFieldName()));
|
||||
}
|
||||
|
||||
ClassReader cls = processedClassSource.get(name);
|
||||
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
|
||||
header.setAddress(CLASS_ENUM_VALUES, generateEnumValues(cls, binaryData));
|
||||
flags |= RuntimeClass.ENUM;
|
||||
if (cls != null) {
|
||||
if (cls.hasModifier(ElementModifier.ENUM)) {
|
||||
header.setAddress(CLASS_ENUM_VALUES, generateEnumValues(cls, binaryData));
|
||||
flags |= RuntimeClass.ENUM;
|
||||
}
|
||||
if (cls.hasModifier(ElementModifier.SYNTHETIC)) {
|
||||
flags |= RuntimeClass.SYNTHETIC;
|
||||
}
|
||||
}
|
||||
|
||||
if (cls != null && binaryData.start >= 0
|
||||
@ -311,7 +334,6 @@ public class WasmClassGenerator {
|
||||
}
|
||||
|
||||
header.setInt(CLASS_FLAGS, flags);
|
||||
header.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||
|
||||
return vtable != null ? wrapper : header;
|
||||
}
|
||||
|
@ -25,12 +25,13 @@ import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
|
||||
private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata";
|
||||
private static final FieldReference ITEM_TYPE_FIELD = new FieldReference(
|
||||
RuntimeClass.class.getName(), "itemType");
|
||||
private static final FieldReference SUPERCLASS_FIELD = new FieldReference(
|
||||
RuntimeClass.class.getName(), "parent");
|
||||
private static final FieldReference NAME_FIELD = new FieldReference(
|
||||
RuntimeClass.class.getName(), "name");
|
||||
private static final String RUNTIME_CLASS = RuntimeClass.class.getName();
|
||||
private static final FieldReference ITEM_TYPE_FIELD = new FieldReference(RUNTIME_CLASS, "itemType");
|
||||
private static final FieldReference SUPERCLASS_FIELD = new FieldReference(RUNTIME_CLASS, "parent");
|
||||
private static final FieldReference NAME_FIELD = new FieldReference(RUNTIME_CLASS, "name");
|
||||
private static final FieldReference SIMPLE_NAME_FIELD = new FieldReference(RUNTIME_CLASS, "simpleName");
|
||||
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
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
@ -41,6 +42,9 @@ public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
|
||||
case "getArrayItem":
|
||||
case "getSuperclass":
|
||||
case "getName":
|
||||
case "getSimpleName":
|
||||
case "getEnclosingClass":
|
||||
case "getDeclaringClass":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -55,6 +59,12 @@ public class PlatformClassMetadataIntrinsic implements WasmIntrinsic {
|
||||
return fieldAccess(manager, invocation, SUPERCLASS_FIELD);
|
||||
case "getName":
|
||||
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:
|
||||
return new WasmUnreachable();
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class CachedClassReader extends CachedElement implements ClassReader {
|
||||
GenericTypeParameter[] parameters;
|
||||
GenericValueType.Object genericParent;
|
||||
String owner;
|
||||
String declaringClass;
|
||||
String simpleName;
|
||||
Set<String> interfaces;
|
||||
Set<GenericValueType.Object> genericInterfaces;
|
||||
Map<MethodDescriptor, CachedMethod> methods;
|
||||
@ -84,4 +86,14 @@ class CachedClassReader extends CachedElement implements ClassReader {
|
||||
public String getOwnerName() {
|
||||
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(cls.getParent() != null ? symbolTable.lookup(cls.getParent()) + 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());
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
output.writeUnsigned(symbolTable.lookup(iface));
|
||||
@ -84,6 +87,11 @@ public class ClassIO {
|
||||
cls.parent = parentIndex > 0 ? referenceCache.getCached(symbolTable.at(parentIndex - 1)) : null;
|
||||
int ownerIndex = input.readUnsigned();
|
||||
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();
|
||||
Set<String> interfaces = new LinkedHashSet<>();
|
||||
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<String, FieldHolder> fields = new LinkedHashMap<>();
|
||||
private String ownerName;
|
||||
private String simpleName;
|
||||
private String declaringClassName;
|
||||
|
||||
public ClassHolder(String name) {
|
||||
super(name);
|
||||
@ -138,4 +140,22 @@ public class ClassHolder extends ElementHolder implements ClassReader {
|
||||
public void setOwnerName(String 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();
|
||||
|
||||
String getOwnerName();
|
||||
|
||||
String getSimpleName();
|
||||
|
||||
String getDeclaringClassName();
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ public final class ModelUtils {
|
||||
target.addField(copyField(field));
|
||||
}
|
||||
target.setOwnerName(original.getOwnerName());
|
||||
target.setDeclaringClassName(original.getDeclaringClassName());
|
||||
target.setSimpleName(original.getSimpleName());
|
||||
copyAnnotations(original.getAnnotations(), target.getAnnotations());
|
||||
return target;
|
||||
}
|
||||
|
@ -89,6 +89,9 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
|
||||
if (cls.getOwnerName() != null) {
|
||||
renamedCls.setOwnerName(classNameMapper.apply(cls.getOwnerName()));
|
||||
}
|
||||
if (cls.getDeclaringClassName() != null) {
|
||||
renamedCls.setDeclaringClassName(classNameMapper.apply(cls.getDeclaringClassName()));
|
||||
}
|
||||
rename(cls.getAnnotations(), renamedCls.getAnnotations());
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
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.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InnerClassNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphUtils;
|
||||
@ -302,14 +303,24 @@ public class Parser {
|
||||
cls.addMethod(method);
|
||||
method.updateReference(referenceCache);
|
||||
}
|
||||
|
||||
if (node.outerClass != null) {
|
||||
cls.setOwnerName(node.outerClass.replace('/', '.'));
|
||||
} else {
|
||||
int lastIndex = node.name.lastIndexOf('$');
|
||||
if (lastIndex != -1) {
|
||||
cls.setOwnerName(node.name.substring(0, lastIndex).replace('/', '.'));
|
||||
}
|
||||
|
||||
if (node.innerClasses != null && !node.innerClasses.isEmpty()) {
|
||||
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);
|
||||
return cls;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ public class RuntimeClass extends RuntimeObject {
|
||||
public static final int INITIALIZED = 1;
|
||||
public static final int PRIMITIVE = 2;
|
||||
public static final int ENUM = 4;
|
||||
public static final int SYNTHETIC = 1024;
|
||||
|
||||
public static final int PRIMITIVE_SHIFT = 3;
|
||||
public static final int PRIMITIVE_MASK = 15;
|
||||
@ -49,6 +50,8 @@ public class RuntimeClass extends RuntimeObject {
|
||||
public RuntimeObjectPtr name;
|
||||
public RuntimeClass itemType;
|
||||
public RuntimeClass arrayType;
|
||||
public RuntimeClass declaringClass;
|
||||
public RuntimeClass enclosingClass;
|
||||
public IsSupertypeFunction isSupertypeOf;
|
||||
public InitFunction init;
|
||||
public RuntimeClass parent;
|
||||
@ -56,7 +59,9 @@ public class RuntimeClass extends RuntimeObject {
|
||||
public RuntimeClassPointer superinterfaces;
|
||||
public Address enumValues;
|
||||
public Address layout;
|
||||
public RuntimeObject simpleName;
|
||||
public RuntimeObjectPtr simpleName;
|
||||
public RuntimeObject simpleNameCache;
|
||||
public RuntimeObject canonicalName;
|
||||
|
||||
@Unmanaged
|
||||
public static int computeCanary(int size, int tag) {
|
||||
|
@ -18,5 +18,5 @@ package org.teavm.runtime;
|
||||
import org.teavm.interop.Structure;
|
||||
|
||||
public class RuntimeObjectPtr extends Structure {
|
||||
RuntimeObject value;
|
||||
public RuntimeObject value;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ typedef struct TeaVM_Class {
|
||||
TeaVM_Object** name;
|
||||
struct TeaVM_Class* itemType;
|
||||
struct TeaVM_Class* arrayType;
|
||||
struct TeaVM_Class* declaringClass;
|
||||
struct TeaVM_Class* enclosingClass;
|
||||
int32_t (*isSupertypeOf)(struct TeaVM_Class*);
|
||||
void (*init)();
|
||||
struct TeaVM_Class* superclass;
|
||||
@ -37,7 +39,9 @@ typedef struct TeaVM_Class {
|
||||
struct TeaVM_Class** superinterfaces;
|
||||
void* enumValues;
|
||||
void* layout;
|
||||
TeaVM_Object* simpleName;
|
||||
TeaVM_Object** simpleName;
|
||||
TeaVM_Object* simpleNameCache;
|
||||
TeaVM_Object* canonicalName;
|
||||
#if TEAVM_HEAP_DUMP
|
||||
TeaVM_FieldDescriptors* fieldDescriptors;
|
||||
TeaVM_StaticFieldDescriptors* staticFieldDescriptors;
|
||||
|
@ -108,8 +108,18 @@ function $rt_arraycls(cls) {
|
||||
if (result === null) {
|
||||
var arraycls = {};
|
||||
var name = "[" + cls.$meta.binaryName;
|
||||
arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls(),
|
||||
name : name, binaryName : name, enum : false };
|
||||
arraycls.$meta = {
|
||||
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.$array = null;
|
||||
result = arraycls;
|
||||
@ -121,7 +131,7 @@ function $rt_createcls() {
|
||||
return {
|
||||
$array : null,
|
||||
classObject : null,
|
||||
$meta : {
|
||||
$meta: {
|
||||
supertypes : [],
|
||||
superclass : null
|
||||
}
|
||||
@ -134,6 +144,9 @@ function $rt_createPrimitiveCls(name, binaryName) {
|
||||
cls.$meta.binaryName = binaryName;
|
||||
cls.$meta.enum = false;
|
||||
cls.$meta.item = null;
|
||||
cls.$meta.simpleName = null;
|
||||
cls.$meta.declaringClass = null;
|
||||
cls.$meta.enclosingClass = null;
|
||||
return cls;
|
||||
}
|
||||
var $rt_booleanclsCache = null;
|
||||
@ -453,6 +466,20 @@ function $rt_metadata(data) {
|
||||
|
||||
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++];
|
||||
cls.$clinit = clinit !== 0 ? clinit : function() {};
|
||||
|
||||
|
@ -246,6 +246,24 @@ public final class Platform {
|
||||
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)
|
||||
private static boolean isLowLevel() {
|
||||
return false;
|
||||
|
@ -46,4 +46,16 @@ public interface PlatformClassMetadata extends JSObject {
|
||||
|
||||
@JSProperty
|
||||
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;"));
|
||||
break;
|
||||
case "getName":
|
||||
case "getSimpleName":
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
break;
|
||||
case "getEnclosingClass":
|
||||
case "getDeclaringClass":
|
||||
method.getResult().propagate(agent.getType("java.lang.Class"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,12 +39,7 @@ public class ClassTest {
|
||||
|
||||
@Test
|
||||
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
|
||||
@ -126,8 +121,36 @@ public class ClassTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void declaringClassFound() {
|
||||
assertEquals(ClassTest.class, new A().getClass().getDeclaringClass());
|
||||
public void classProperties() {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user