Skip to content

Commit

Permalink
Add docs, fixed resolution for default methods, basic example
Browse files Browse the repository at this point in the history
  • Loading branch information
xxDark committed Oct 20, 2022
1 parent 86cd1e9 commit 1531613
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 4 deletions.
144 changes: 143 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,144 @@
# jlinker
Java resolution library for methods and fields.
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<String, ClassNode> 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<ClassNode, MethodNode, FieldNode> resolver = LinkResolver.jvm();
// Create class info for java/util/AbstractList
ClassInfo<ClassNode> classInfo = classInfo(fn.apply("java/util/AbstractList"), fn);
// Resolve virtual "containsAll" method
Resolution<ClassNode, MethodNode> 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<MethodNode> methodInfo(MethodNode node) {
return new MemberInfo<MethodNode>() {
@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<FieldNode> fieldInfo(FieldNode node) {
return new MemberInfo<FieldNode>() {
@Override
public FieldNode innerValue() {
return node;
}

@Override
public int accessFlags() {
return node.access;
}

@Override
public boolean isPolymorphic() {
return false;
}
};
}

// Class information
private static ClassInfo<ClassNode> classInfo(ClassNode node, Function<String, ClassNode> fn) {
return new ClassInfo<ClassNode>() {
@Override
public ClassNode innerValue() {
return node;
}

@Override
public int accessFlags() {
return node.access;
}

@Override
public ClassInfo<ClassNode> superClass() {
String superName = node.superName;
return superName == null ? null : classInfo(fn.apply(superName), fn);
}

@Override
public List<ClassInfo<ClassNode>> 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;
}
};
}
}
```
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group 'dev.xdark'
version '1.0'
version '1.0.1'

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/dev.xdark.jlinker/Error.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public Error(ResolutionError error) {

@Override
public V value() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException(error.name());
}

@Override
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/dev.xdark.jlinker/JVMLinkResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,20 @@ Resolution<C, M> uncachedInterfaceMethod(ClassInfo<C> owner, String name, String
ClassInfo<C> info = owner;
try (Arena<ClassInfo<C>> arena = classArenaAllocator.push()) {
arena.push(owner);
Resolution<C, M> candidate = null;
while ((owner = arena.poll()) != null) {
MemberInfo<M> member = (MemberInfo<M>) 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
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/dev.xdark.jlinker/LinkResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,24 @@ public interface LinkResolver<C, M, F> {
*/
Result<Resolution<C, M>> resolveInterfaceMethod(ClassInfo<C> owner, String name, String descriptor);

/**
* Resolves static field.
*
* @param owner Field owner.
* @param name Field name.
* @param descriptor Field descriptor.
* @return Resolution result.
*/
Result<Resolution<C, F>> resolveStaticField(ClassInfo<C> owner, String name, String descriptor);

/**
* Resolves virtual field.
*
* @param owner Field owner.
* @param name Field name.
* @param descriptor Field descriptor.
* @return Resolution result.
*/
Result<Resolution<C, F>> resolveVirtualField(ClassInfo<C> owner, String name, String descriptor);

/**
Expand Down

0 comments on commit 1531613

Please sign in to comment.