Skip to content

Commit

Permalink
initial work on supporting better versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
Machine-Maker committed Sep 26, 2024
1 parent eb639db commit 4dd1120
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package io.papermc.asm.rules.method.params;

import io.papermc.asm.versioned.ApiVersion;
import io.papermc.asm.versioned.VersionedRuleFactory;
import io.papermc.asm.rules.RewriteRule;
import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import io.papermc.asm.rules.method.generated.TargetedTypeGeneratedStaticRewrite;
import java.lang.constant.ClassDesc;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Rewrites matching bytecode to a generated method which will invoke the static handler on all parameters that need to be converted. That
Expand All @@ -16,4 +24,21 @@
* @param staticHandler the method which will be used to convert the legacy type to the new type
*/
public record DirectParameterRewrite(Set<ClassDesc> owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter {

public record Versioned(Set<ClassDesc> owners, ClassDesc existingType, NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> versions) implements VersionedRuleFactory {

public Versioned {
versions = Collections.unmodifiableNavigableMap(versions);
}

@Override
public @Nullable RewriteRule createRule(final ApiVersion apiVersion) {
final Map.@Nullable Entry<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> apiVersionEntryEntry = this.versions.ceilingEntry(apiVersion);
if (apiVersionEntryEntry == null) {
return null;
}
final Map.Entry<TargetedMethodMatcher, Method> entry = apiVersionEntryEntry.getValue();
return new DirectParameterRewrite(this.owners, this.existingType, entry.getKey(), entry.getValue());
}
}
}
23 changes: 23 additions & 0 deletions src/main/java/io/papermc/asm/versioned/ApiVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.papermc.asm.versioned;

import org.jetbrains.annotations.ApiStatus;

@ApiStatus.OverrideOnly
public interface ApiVersion extends Comparable<ApiVersion> {

default boolean isNewerThan(final ApiVersion apiVersion) {
return this.compareTo(apiVersion) > 0;
}

default boolean isOlderThan(final ApiVersion apiVersion) {
return this.compareTo(apiVersion) < 0;
}

default boolean isNewerThanOrSameAs(final ApiVersion apiVersion) {
return this.compareTo(apiVersion) >= 0;
}

default boolean isOlderThanOrSameAs(final ApiVersion apiVersion) {
return this.compareTo(apiVersion) <= 0;
}
}
43 changes: 43 additions & 0 deletions src/main/java/io/papermc/asm/versioned/VersionedRuleFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.papermc.asm.versioned;

import io.papermc.asm.rules.RewriteRule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;

@DefaultQualifier(NonNull.class)
public interface VersionedRuleFactory {

static VersionedRuleFactory chain(final VersionedRuleFactory... factories) {
return chain(Arrays.asList(factories));
}

static VersionedRuleFactory chain(final Collection<? extends VersionedRuleFactory> factories) {
return new Chain(List.copyOf(factories));
}

@Nullable RewriteRule createRule(ApiVersion apiVersion);

record Chain(List<VersionedRuleFactory> factories) implements VersionedRuleFactory{

public Chain {
factories = List.copyOf(factories);
}

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
final List<RewriteRule> rules = new ArrayList<>();
for (final VersionedRuleFactory factory : this.factories) {
final @Nullable RewriteRule rule = factory.createRule(apiVersion);
if (rule != null) {
rules.add(rule);
}
}
return RewriteRule.chain(rules);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.papermc.asm.versioned;

import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import java.lang.constant.ClassDesc;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;

import static io.papermc.asm.util.DescriptorUtils.desc;

public interface VersionedRuleFactoryBuilder {

static VersionedRuleFactoryBuilder create(final Set<ClassDesc> owners) {
return new VersionedRuleFactoryBuilderImpl(owners);
}

default void changeParamDirect(final Class<?> newParamType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher, final Method staticHandler) {
this.changeParamDirect(desc(newParamType), apiVersion, methodMatcher, staticHandler);
}

default void changeParamDirect(final ClassDesc newParamType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher, final Method staticHandler) {
this.changeParamDirect(newParamType, new TreeMap<>(Map.of(apiVersion, Map.entry(methodMatcher, staticHandler))));
}

default void changeParamDirect(final Class<?> newParamType, final NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> versions) {
this.changeParamDirect(desc(newParamType), versions);
}

void changeParamDirect(ClassDesc newParamType, NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> versions);

VersionedRuleFactory build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.papermc.asm.versioned;

import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import io.papermc.asm.rules.method.params.DirectParameterRewrite;
import java.lang.constant.ClassDesc;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;

public class VersionedRuleFactoryBuilderImpl implements VersionedRuleFactoryBuilder {

final Set<ClassDesc> owners;
private final List<VersionedRuleFactory> factories = new ArrayList<>();

public VersionedRuleFactoryBuilderImpl(final Set<ClassDesc> owners) {
this.owners = Set.copyOf(owners);
}

@Override
public void changeParamDirect(final ClassDesc newParamType, final NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> versions) {
this.factories.add(new DirectParameterRewrite.Versioned(this.owners, newParamType, versions));
}

@Override
public VersionedRuleFactory build() {
if (this.factories.size() == 1) {
return this.factories.get(0);
}
return VersionedRuleFactory.chain(this.factories);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.papermc.asm.versioned.builder;

public interface VersionedMethodMatcherBuilder {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.papermc.asm.versioned.builder;

import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import io.papermc.asm.util.Builder;
import io.papermc.asm.versioned.ApiVersion;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.NavigableMap;
import org.jetbrains.annotations.Contract;

public interface VersionedTargetedMethodMatcherBuilder extends Builder<NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>>> {

static VersionedTargetedMethodMatcherBuilder builder() {
return new VersionedTargetedMethodMatcherBuilderImpl();
}

@Contract(value = "_, _, _ -> this", mutates = "this")
VersionedTargetedMethodMatcherBuilder with(ApiVersion apiVersion, TargetedMethodMatcher targetedMethodMatcher, Method method);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.papermc.asm.versioned.builder;

import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import io.papermc.asm.versioned.ApiVersion;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;

public class VersionedTargetedMethodMatcherBuilderImpl implements VersionedTargetedMethodMatcherBuilder {

private final NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> versions = new TreeMap<>();

@Override
public VersionedTargetedMethodMatcherBuilder with(final ApiVersion apiVersion, final TargetedMethodMatcher targetedMethodMatcher, final Method method) {
if (this.versions.containsKey(apiVersion)) {
throw new IllegalArgumentException("Duplicate version: " + apiVersion);
}
this.versions.put(apiVersion, Map.entry(targetedMethodMatcher, method));
return this;
}

@Override
public NavigableMap<ApiVersion, Map.Entry<TargetedMethodMatcher, Method>> build() {
return Collections.unmodifiableNavigableMap(this.versions);
}
}

0 comments on commit 4dd1120

Please sign in to comment.