diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index a3d16b629..801291e4e 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -20,8 +20,10 @@ import com.carrotsearch.hppc.ObjectIntMap; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; @@ -487,22 +489,17 @@ public class Renderer implements RenderingManager { } private void renderClassMetadata(List classes) { - Set classesRequiringName = new HashSet<>(); - MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod( - new MethodReference(Class.class, "getName", String.class)); - if (getNameMethod != null) { - classesRequiringName.addAll(Arrays.asList(getNameMethod.getVariable(0).getClassValueNode().getTypes())); - } - MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod( - new MethodReference(Class.class, "getSimpleName", String.class)); - if (getSimpleNameMethod != null) { - classesRequiringName.addAll(Arrays.asList( - getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes())); + if (classes.isEmpty()) { + return; } + Set classesRequiringName = findClassesRequiringName(); + int start = writer.getOffset(); try { writer.append("$rt_metadata(["); + ObjectIntMap packageIndexes = generatePackageMetadata(classes, classesRequiringName); + boolean first = true; for (ClassNode cls : classes) { if (!first) { @@ -512,7 +509,12 @@ public class Renderer implements RenderingManager { writer.appendClass(cls.getName()).append(",").ws(); if (classesRequiringName.contains(cls.getName())) { - writer.append("\"").append(RenderingUtil.escapeString(cls.getName())).append("\""); + String className = cls.getName(); + int dotIndex = className.lastIndexOf('.') + 1; + String packageName = className.substring(0, dotIndex); + className = className.substring(dotIndex); + writer.append("\"").append(RenderingUtil.escapeString(className)).append("\"").append(",").ws(); + writer.append(String.valueOf(packageIndexes.getOrDefault(packageName, -1))); } else { writer.append("0"); } @@ -566,6 +568,85 @@ public class Renderer implements RenderingManager { metadataSize = writer.getOffset() - start; } + private ObjectIntMap generatePackageMetadata(List classes, Set classesRequiringName) + throws IOException { + PackageNode root = new PackageNode(null); + + for (ClassNode classNode : classes) { + String className = classNode.getName(); + if (!classesRequiringName.contains(className)) { + continue; + } + + int dotIndex = className.lastIndexOf('.'); + if (dotIndex < 0) { + continue; + } + + addPackageName(root, className.substring(0, dotIndex)); + } + + ObjectIntMap indexes = new ObjectIntHashMap<>(); + writer.append(String.valueOf(root.count())).append(",").ws(); + writePackageStructure(root, -1, "", indexes); + writer.softNewLine(); + return indexes; + } + + private int writePackageStructure(PackageNode node, int startIndex, String prefix, ObjectIntMap indexes) + throws IOException { + int index = startIndex; + for (PackageNode child : node.children.values()) { + writer.append(String.valueOf(startIndex)).append(",").ws() + .append("\"").append(RenderingUtil.escapeString(child.name)).append("\",").ws(); + String fullName = prefix + child.name + "."; + ++index; + indexes.put(fullName, index); + index = writePackageStructure(child, index, fullName, indexes); + } + return index; + } + + static class PackageNode { + String name; + Map children = new HashMap<>(); + + PackageNode(String name) { + this.name = name; + } + + int count() { + int result = 0; + for (PackageNode child : children.values()) { + result += 1 + child.count(); + } + return result; + } + } + + private void addPackageName(PackageNode node, String name) { + String[] parts = name.split("\\."); + for (String part : parts) { + node = node.children.computeIfAbsent(part, PackageNode::new); + } + } + + private Set findClassesRequiringName() { + Set classesRequiringName = new HashSet<>(); + MethodDependencyInfo getNameMethod = context.getDependencyInfo().getMethod( + new MethodReference(Class.class, "getName", String.class)); + if (getNameMethod != null) { + classesRequiringName.addAll(Arrays.asList(getNameMethod.getVariable(0).getClassValueNode().getTypes())); + } + MethodDependencyInfo getSimpleNameMethod = context.getDependencyInfo().getMethod( + new MethodReference(Class.class, "getSimpleName", String.class)); + if (getSimpleNameMethod != null) { + classesRequiringName.addAll(Arrays.asList( + getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes())); + } + return classesRequiringName; + } + private void collectMethodsToCopyFromInterfaces(ClassReader cls, List targetList) { Set implementedMethods = new HashSet<>(); implementedMethods.addAll(targetList.stream().map(method -> method.getDescriptor()) diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index cb27b8cd5..28ec2be2a 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -429,23 +429,40 @@ function $rt_putStderr(ch) { } } function $rt_metadata(data) { - for (var i = 0; i < data.length; i += 8) { - var cls = data[i]; + var i = 0; + var packageCount = data[i++]; + var packages = new Array(packageCount); + for (var j = 0; j < packageCount; ++j) { + var prefixIndex = data[i++]; + var prefix = prefixIndex >= 0 ? packages[prefixIndex] : ""; + packages[j] = prefix + data[i++] + "."; + } + + while (i < data.length) { + var cls = data[i++]; cls.$meta = {}; var m = cls.$meta; - var className = data[i + 1]; + var className = data[i++]; + m.name = className !== 0 ? className : null; + if (m.name !== null) { + var packageIndex = data[i++]; + if (packageIndex >= 0) { + m.name = packages[packageIndex] + m.name; + } + } + m.binaryName = "L" + m.name + ";"; - var superclass = data[i + 2]; + var superclass = data[i++]; m.superclass = superclass !== 0 ? superclass : null; - m.supertypes = data[i + 3]; + m.supertypes = data[i++]; if (m.superclass) { m.supertypes.push(m.superclass); cls.prototype = Object.create(m.superclass.prototype); } else { cls.prototype = {}; } - var flags = data[i + 4]; + var flags = data[i++]; m.enum = (flags & 16) !== 0; m.flags = flags; m.primitive = false; @@ -453,13 +470,13 @@ function $rt_metadata(data) { cls.prototype.constructor = cls; cls.classObject = null; - m.accessLevel = data[i + 5]; + m.accessLevel = data[i++]; - var clinit = data[i + 6]; + var clinit = data[i++]; cls.$clinit = clinit !== 0 ? clinit : function() {}; - var virtualMethods = data[i + 7]; - for (var j = 0; j < virtualMethods.length; j += 2) { + var virtualMethods = data[i++]; + for (j = 0; j < virtualMethods.length; j += 2) { var name = virtualMethods[j]; var func = virtualMethods[j + 1]; if (typeof name === 'string') {