Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve access transformers #5

Open
wants to merge 14 commits into
base: FG_2.3
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,22 @@ public void doTask() throws IOException {
AccessTransformationSet transformations = new AccessTransformationSet();
for (File file : getAts()) {
if (file.getName().endsWith(".jar")) {
JarFile jar = new JarFile(file);
ZipEntry entry = jar.getEntry("access_transformations.at");
if (entry != null) {
try (Scanner scanner = new Scanner(jar.getInputStream(entry))) {
while (scanner.hasNextLine()) {
transformations.addMinimumAccessLevel(scanner.nextLine());
try (JarFile jar = new JarFile(file)) {
ZipEntry entry = jar.getEntry("access_transformations.at");

if (entry != null) {
getLogger().info("Found transformer in " + file);

try (Scanner scanner = new Scanner(jar.getInputStream(entry))) {
while (scanner.hasNextLine()) {
transformations.addMinimumAccessLevel(scanner.nextLine());
}
}
}
}
}
} else {
getLogger().info("Found transformer in " + file);

try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
transformations.addMinimumAccessLevel(scanner.nextLine());
Expand Down Expand Up @@ -82,6 +88,9 @@ public void doTask() throws IOException {
out.closeEntry();
}
}

//Make sure there aren't any more transformations that failed to find their classes
transformations.ensureClear();
}

private static byte[] readStream(InputStream inputStream) throws IOException {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/dimdev/accesstransform/AccessLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ public static AccessLevel fromString(String string) {

return new AccessLevel(Visibility.valueOf(string), isFinal);
}

@Override
public String toString() {
return "Access<" + visibility + (isFinal ? '+' : '-') + "F>";
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,70 @@
package org.dimdev.accesstransform;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.dimdev.accesstransform.ElementReference.Kind;

public class AccessTransformationSet {
private final Map<ElementReference, AccessLevel> transformations = new HashMap<>();
private final Set<String> affectedClasses = new HashSet<>();
public static class Transformation {
final Map<ElementReference, AccessLevel> methods = new HashMap<>();
final Map<ElementReference, AccessLevel> fields = new HashMap<>();
private final String name;
AccessLevel access;

public Transformation(String name) {
this.name = name;
}

public boolean wantsAccessChange() {
return access != null;
}

public AccessLevel getAccessChange() {
return access;
}

public boolean wantsMethodChange() {
return !methods.isEmpty();
}

public AccessLevel popMethod(String name, String description) {
return methods.remove(new ElementReference(Kind.METHOD, this.name, name, description));
}

public boolean wantsFieldChange() {
return !fields.isEmpty();
}

public AccessLevel popField(String name, String description) {
return fields.remove(new ElementReference(Kind.FIELD, this.name, name, description));
}

public void ensureClear() {
if (!methods.isEmpty() || !fields.isEmpty()) {
throw new IllegalStateException("Additional transformations to " + name + " missed: methods: " + methods + ", fields: " + fields);
}
}
}
private final Map<String, Transformation> transformations = new HashMap<>();

public void addMimimumAccessLevel(ElementReference elementReference, AccessLevel accessLevel) {
transformations.put(elementReference, AccessLevel.union(transformations.get(elementReference), accessLevel));
affectedClasses.add(elementReference.kind == ElementReference.Kind.CLASS ? elementReference.name : elementReference.owner);
switch (elementReference.kind) {
case CLASS: {
Transformation transform = transformations.computeIfAbsent(elementReference.name, Transformation::new);
transform.access = AccessLevel.union(transform.access, accessLevel);
}
ZombieHDGaming marked this conversation as resolved.
Show resolved Hide resolved

case METHOD: {
Transformation transform = transformations.computeIfAbsent(elementReference.owner, Transformation::new);
transform.methods.put(elementReference, AccessLevel.union(transform.methods.get(elementReference), accessLevel));
}

case FIELD: {
Transformation transform = transformations.computeIfAbsent(elementReference.owner, Transformation::new);
transform.fields.put(elementReference, AccessLevel.union(transform.fields.get(elementReference), accessLevel));
}
}
}

public void addMinimumAccessLevel(String string) {
Expand All @@ -24,15 +77,13 @@ public void addMinimumAccessLevel(String string) {
addMimimumAccessLevel(ElementReference.fromString(elementReference), AccessLevel.fromString(accessLevel));
}

public AccessLevel getMinimumAccessLevel(ElementReference elementReference) {
return transformations.get(elementReference);
}

public boolean isClassAffected(String name) {
return affectedClasses.contains(name);
public Transformation popTransformations(String name) {
return transformations.remove(name);
}

public boolean isEmpty() {
return transformations.size() == 0;
public void ensureClear() {
if (!transformations.isEmpty()) {
throw new IllegalStateException("Additional class transformations missed for " + transformations.keySet());
}
}
}
38 changes: 23 additions & 15 deletions src/main/java/org/dimdev/accesstransform/AccessTransformer.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.dimdev.accesstransform;


import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;

import org.dimdev.accesstransform.AccessTransformationSet.Transformation;

public class AccessTransformer {
private final AccessTransformationSet transformations;

Expand All @@ -16,36 +17,43 @@ public AccessTransformer(AccessTransformationSet transformations) {
}

public byte[] transformClass(String name, byte[] bytes) {
if (bytes == null || !transformations.isClassAffected(name)) {
return bytes;
Transformation transform = transformations.popTransformations(name);
if (bytes == null || transform == null) {
return bytes; //Nothing to do
}

ClassNode clazz = new ClassNode();
ClassReader reader = new ClassReader(bytes);
reader.accept(clazz, 0);

// Transform class access level
clazz.access = getNewAccessLevel(new ElementReference(ElementReference.Kind.CLASS, null, name, null), clazz.access);
if (transform.wantsAccessChange()) {
// Transform class access level
clazz.access = getNewAccessLevel(clazz.access, transform.access);
}

for (FieldNode field : clazz.fields) {
field.access = getNewAccessLevel(new ElementReference(ElementReference.Kind.FIELD, name, field.name, field.desc), field.access);
if (transform.wantsFieldChange()) {
for (FieldNode field : clazz.fields) {
AccessLevel access = transform.popField(field.name, field.desc);
if (access != null) field.access = getNewAccessLevel(field.access, access);
}
}

for (MethodNode method : clazz.methods) {
method.access = getNewAccessLevel(new ElementReference(ElementReference.Kind.METHOD, name, method.name, method.desc), method.access);
if (transform.wantsMethodChange()) {
for (MethodNode method : clazz.methods) {
AccessLevel access = transform.popMethod(method.name, method.desc);
if (access != null) method.access = getNewAccessLevel(method.access, access);
}
}

//Make sure there aren't any other transformations requested for this class
transform.ensureClear();

ClassWriter writer = new ClassWriter(0);
clazz.accept(writer);
return writer.toByteArray();
}

private int getNewAccessLevel(ElementReference elementReference, int access) {
AccessLevel minimumAccessLevel = transformations.getMinimumAccessLevel(elementReference);
if (minimumAccessLevel == null) {
return access;
}

private int getNewAccessLevel(int access, AccessLevel minimumAccessLevel) {
AccessLevel.Visibility visibility;
if ((access & Opcodes.ACC_PUBLIC) != 0) {
visibility = AccessLevel.Visibility.PUBLIC;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,9 @@ public boolean equals(Object obj) {
public int hashCode() {
return Objects.hash(kind, owner, name, desc);
}

@Override
public String toString() {
return "Reference<" + (kind == Kind.CLASS ? name : owner + '#' + name + desc) + '>';
}
}