From 1531613d20d74a6b9604bcf0a1b5bfec6e00d4a3 Mon Sep 17 00:00:00 2001 From: xDark <19853368+xxDark@users.noreply.github.com> Date: Thu, 20 Oct 2022 22:14:49 +0300 Subject: [PATCH] Add docs, fixed resolution for default methods, basic example --- README.md | 144 +++++++++++++++++- build.gradle | 2 +- src/main/java/dev.xdark.jlinker/Error.java | 2 +- .../dev.xdark.jlinker/JVMLinkResolver.java | 9 +- .../java/dev.xdark.jlinker/LinkResolver.java | 16 ++ 5 files changed, 169 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03ae672..88494dd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,144 @@ # jlinker -Java resolution library for methods and fields. \ No newline at end of file +Java resolution library for methods and fields. + +## example + +```Java +package dev.xdark.jlinker; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Example { + public static void main(String[] args) { + Function fn = s -> { + // Ideally, the resolver should do that conversion, + // but it doesn't. Yet. + if (s.charAt(0) == '[') { + s = "java/lang/Object"; + } + ClassReader reader; + try (InputStream in = ClassLoader.getSystemResourceAsStream(s + ".class")) { + if (in == null) { + throw new IllegalStateException(s); + } + reader = new ClassReader(in); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + ClassNode node = new ClassNode(); + // We don't care about code or debug information + reader.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG); + return node; + }; + // Create link resolver + LinkResolver resolver = LinkResolver.jvm(); + // Create class info for java/util/AbstractList + ClassInfo classInfo = classInfo(fn.apply("java/util/AbstractList"), fn); + // Resolve virtual "containsAll" method + Resolution resolution = resolver.resolveVirtualMethod(classInfo, "containsAll", "(Ljava/util/Collection;)Z").value(); + ClassNode owner = resolution.owner().innerValue(); + MethodNode m = resolution.member().innerValue(); + // Should be java/util/AbstractCollection.containsAll(Ljava/util/Collection;)Z + System.out.println(owner.name + '.' + m.name + m.desc); + } + + // Method information + private static MemberInfo methodInfo(MethodNode node) { + return new MemberInfo() { + @Override + public MethodNode innerValue() { + return node; + } + + @Override + public int accessFlags() { + return node.access; + } + + @Override + public boolean isPolymorphic() { + // To implement that, one should check for PolymorphicSignature annotation, + // doesn't matter in this example. Linker does not support polymorphic methods. + // Yet. + return false; + } + }; + } + + // Field information + private static MemberInfo fieldInfo(FieldNode node) { + return new MemberInfo() { + @Override + public FieldNode innerValue() { + return node; + } + + @Override + public int accessFlags() { + return node.access; + } + + @Override + public boolean isPolymorphic() { + return false; + } + }; + } + + // Class information + private static ClassInfo classInfo(ClassNode node, Function fn) { + return new ClassInfo() { + @Override + public ClassNode innerValue() { + return node; + } + + @Override + public int accessFlags() { + return node.access; + } + + @Override + public ClassInfo superClass() { + String superName = node.superName; + return superName == null ? null : classInfo(fn.apply(superName), fn); + } + + @Override + public List> interfaces() { + return node.interfaces.stream().map(x -> classInfo(fn.apply(x), fn)).collect(Collectors.toList()); + } + + @Override + public MemberInfo getMethod(String name, String descriptor) { + for (MethodNode method : node.methods) { + if (name.equals(method.name) && descriptor.equals(method.desc)) { + return methodInfo(method); + } + } + return null; + } + + @Override + public MemberInfo getField(String name, String descriptor) { + for (FieldNode field : node.fields) { + if (name.equals(field.name) && descriptor.equals(field.desc)) { + return fieldInfo(field); + } + } + return null; + } + }; + } +} +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4ab1dd0..ebab677 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'dev.xdark' -version '1.0' +version '1.0.1' repositories { mavenCentral() diff --git a/src/main/java/dev.xdark.jlinker/Error.java b/src/main/java/dev.xdark.jlinker/Error.java index 6b805fc..b7f3047 100644 --- a/src/main/java/dev.xdark.jlinker/Error.java +++ b/src/main/java/dev.xdark.jlinker/Error.java @@ -18,7 +18,7 @@ public Error(ResolutionError error) { @Override public V value() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(error.name()); } @Override diff --git a/src/main/java/dev.xdark.jlinker/JVMLinkResolver.java b/src/main/java/dev.xdark.jlinker/JVMLinkResolver.java index 297eb54..61cbfe9 100644 --- a/src/main/java/dev.xdark.jlinker/JVMLinkResolver.java +++ b/src/main/java/dev.xdark.jlinker/JVMLinkResolver.java @@ -124,13 +124,20 @@ Resolution uncachedInterfaceMethod(ClassInfo owner, String name, String ClassInfo info = owner; try (Arena> arena = classArenaAllocator.push()) { arena.push(owner); + Resolution candidate = null; while ((owner = arena.poll()) != null) { MemberInfo member = (MemberInfo) owner.getMethod(name, descriptor); if (member != null) { - return new Resolution<>(owner, member, false); + candidate = new Resolution<>(owner, member, false); + if (!Modifier.isAbstract(member.accessFlags())) { + return candidate; + } } arena.push(owner.interfaces()); } + if (candidate != null) { + return candidate; + } } // We have corner case when a compiler can generate interface call // to java/lang/Object. This cannot happen with javac, but the spec diff --git a/src/main/java/dev.xdark.jlinker/LinkResolver.java b/src/main/java/dev.xdark.jlinker/LinkResolver.java index 250f690..afe6385 100644 --- a/src/main/java/dev.xdark.jlinker/LinkResolver.java +++ b/src/main/java/dev.xdark.jlinker/LinkResolver.java @@ -38,8 +38,24 @@ public interface LinkResolver { */ Result> resolveInterfaceMethod(ClassInfo owner, String name, String descriptor); + /** + * Resolves static field. + * + * @param owner Field owner. + * @param name Field name. + * @param descriptor Field descriptor. + * @return Resolution result. + */ Result> resolveStaticField(ClassInfo owner, String name, String descriptor); + /** + * Resolves virtual field. + * + * @param owner Field owner. + * @param name Field name. + * @param descriptor Field descriptor. + * @return Resolution result. + */ Result> resolveVirtualField(ClassInfo owner, String name, String descriptor); /**