Skip to content

add getId method to EnigmaService #164

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

Merged
merged 32 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f32f05f
rework interface
ix0rai Nov 6, 2023
552b761
work
ix0rai Nov 8, 2023
a04fa74
work
ix0rai Nov 9, 2023
27ea6eb
somewhat functional
ix0rai Nov 9, 2023
45f75fd
fix and improve server stuff
ix0rai Nov 9, 2023
3106481
check the style
ix0rai Nov 9, 2023
2429f71
fix rare npe due to mishandling of flatlaf
ix0rai Nov 10, 2023
72a12de
misc
ix0rai Nov 11, 2023
4a6715c
restore old save/load behaviour
ix0rai Nov 11, 2023
b0d4014
fully working I think :D
ix0rai Nov 12, 2023
d172324
fix checkstyle
ix0rai Nov 12, 2023
3ff4cbd
port enigma-cli
ix0rai Nov 12, 2023
1985d89
delete recaf (it broke and I'm lazy)
ix0rai Nov 12, 2023
fa197fe
fix
ix0rai Nov 12, 2023
0d18c05
error handling for invalid token types
ix0rai Nov 12, 2023
37ab82f
fix navigator issues
ix0rai Nov 12, 2023
e6c712b
clarify javadoc
ix0rai Nov 12, 2023
74425be
priority
ix0rai Nov 13, 2023
671de4b
fix npe
ix0rai Nov 13, 2023
1a6bde0
store proposed mappings separately
ix0rai Nov 13, 2023
9b46ba9
store proposed mappings separately
ix0rai Nov 14, 2023
f014b81
add a test for service ordering
ix0rai Nov 17, 2023
d675df0
malformed json test
ix0rai Nov 19, 2023
0ed0ec0
start moving plugin ID into plugin code
ix0rai Nov 19, 2023
3d67ea7
Merge branch 'develop/2.0' into 2.0/service-ids
ix0rai Nov 20, 2023
0d48318
fix error
ix0rai Nov 20, 2023
6af226c
finish
ix0rai Dec 18, 2023
42df8d8
fix
ix0rai Dec 18, 2023
e9b486d
lol
ix0rai Dec 18, 2023
c262f33
resolve comments
ix0rai Dec 18, 2023
316fa44
address review from iota
ix0rai Dec 19, 2023
8c1a31d
check the style
ix0rai Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog

index.indexJar(scope, classProvider, progress);

var indexers = this.services.getWithIds(JarIndexerService.TYPE);
var indexers = this.services.get(JarIndexerService.TYPE);
progress.init(indexers.size(), I18n.translate("progress.jar.custom_indexing"));

int i = 1;
for (var service : indexers) {
progress.step(i++, I18n.translateFormatted("progress.jar.custom_indexing.indexer", service.id()));
service.service().acceptJar(scope, classProvider, index);
progress.step(i++, I18n.translateFormatted("progress.jar.custom_indexing.indexer", service.getId()));
service.acceptJar(scope, classProvider, index);
}

progress.step(i, I18n.translate("progress.jar.custom_indexing.finished"));
Expand All @@ -87,7 +87,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog

int j = 1;
for (var service : nameProposalServices) {
progress.step(j++, I18n.translateFormatted("progress.jar.name_proposal"));
progress.step(j++, I18n.translateFormatted("progress.jar.name_proposal.proposer", service.getId()));
Map<Entry<?>, EntryMapping> proposed = service.getProposedNames(index);

if (proposed != null) {
Expand Down Expand Up @@ -123,7 +123,7 @@ public EnigmaServices getServices() {
* @return the ordered list of services
*/
public List<NameProposalService> getNameProposalServices() {
var proposalServices = new ArrayList<>(this.services.getWithIds(NameProposalService.TYPE).stream().map(EnigmaServices.RegisteredService::service).toList());
var proposalServices = new ArrayList<>(this.services.get(NameProposalService.TYPE));
Collections.reverse(proposalServices);
return proposalServices;
}
Expand Down Expand Up @@ -161,20 +161,20 @@ public Enigma build() {
private static class PluginContext implements EnigmaPluginContext {
private final EnigmaProfile profile;

private final ImmutableListMultimap.Builder<EnigmaServiceType<?>, EnigmaServices.RegisteredService<?>> services = ImmutableListMultimap.builder();
private final ImmutableListMultimap.Builder<EnigmaServiceType<?>, EnigmaService> services = ImmutableListMultimap.builder();

PluginContext(EnigmaProfile profile) {
this.profile = profile;
}

@Override
public <T extends EnigmaService> void registerService(String id, EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory) {
public <T extends EnigmaService> void registerService(EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory) {
List<EnigmaProfile.Service> serviceProfiles = this.profile.getServiceProfiles(serviceType);

for (EnigmaProfile.Service serviceProfile : serviceProfiles) {
if (serviceProfile.matches(id)) {
T service = factory.create(this.getServiceContext(serviceProfile));
this.services.put(serviceType, new EnigmaServices.RegisteredService<>(id, service));
T service = factory.create(this.getServiceContext(serviceProfile));
if (serviceProfile.matches(service.getId())) {
this.services.put(serviceType, service);
break;
}
}
Expand All @@ -200,13 +200,13 @@ public Path getPath(String path) {
*/
EnigmaServices buildServices() {
var builtServices = this.services.build();
ImmutableListMultimap.Builder<EnigmaServiceType<?>, EnigmaServices.RegisteredService<?>> orderedServices = ImmutableListMultimap.builder();
ImmutableListMultimap.Builder<EnigmaServiceType<?>, EnigmaService> orderedServices = ImmutableListMultimap.builder();
for (EnigmaServiceType<?> type : builtServices.keySet()) {
List<EnigmaProfile.Service> serviceProfiles = this.profile.getServiceProfiles(type);

for (EnigmaProfile.Service service : serviceProfiles) {
for (EnigmaServices.RegisteredService<?> registeredService : builtServices.get(type)) {
if (service.matches(registeredService.id())) {
for (EnigmaService registeredService : builtServices.get(type)) {
if (service.matches(registeredService.getId())) {
orderedServices.put(type, registeredService);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import org.quiltmc.enigma.api.service.EnigmaServiceType;

public interface EnigmaPluginContext {
<T extends EnigmaService> void registerService(String id, EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory);
<T extends EnigmaService> void registerService(EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory);
}
16 changes: 4 additions & 12 deletions enigma/src/main/java/org/quiltmc/enigma/api/EnigmaServices.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,14 @@
import java.util.List;

public final class EnigmaServices {
private final ImmutableListMultimap<EnigmaServiceType<?>, RegisteredService<? extends EnigmaService>> services;
private final ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services;

EnigmaServices(ImmutableListMultimap<EnigmaServiceType<?>, RegisteredService<? extends EnigmaService>> services) {
EnigmaServices(ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services) {
this.services = services;
}

public <T extends EnigmaService> List<T> get(EnigmaServiceType<T> type) {
List<RegisteredService<T>> withIds = this.getWithIds(type);
return withIds.stream().map(RegisteredService::service).toList();
}

@SuppressWarnings("unchecked")
public <T extends EnigmaService> List<RegisteredService<T>> getWithIds(EnigmaServiceType<T> type) {
return (List<RegisteredService<T>>) (Object) this.services.get(type);
}

public record RegisteredService<T extends EnigmaService>(String id, T service) {
public <T extends EnigmaService> List<T> get(EnigmaServiceType<T> type) {
return (List<T>) this.services.get(type);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
package org.quiltmc.enigma.api.service;

public interface EnigmaService {
/**
* The ID of this service. This should satisfy a few criteria:
* <ul>
* <li>Be namespaced, with the plugin name and service name separated by a colon. The {@code enigma} namespace is reserved for builtin plugins.</li>
* <li>Be all lowercase, with words separated by underscores. Slashes are allowed only after the namespace.</li>
* <li>Be constant and unique: it should never change.</li>
* </ul>
* <p>Examples: {@code enigma:cfr}, {@code enigma:enum_name_proposer}, {@code your_plugin:custom_indexer}</p>
*
* @return the constant ID
*/
String getId();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.quiltmc.enigma.api.service;

import org.objectweb.asm.tree.ClassNode;
import org.quiltmc.enigma.api.analysis.index.jar.JarIndex;
import org.quiltmc.enigma.api.class_provider.ClassProvider;
import org.objectweb.asm.ClassVisitor;
Expand All @@ -11,10 +12,21 @@ public interface JarIndexerService extends EnigmaService {

void acceptJar(Set<String> scope, ClassProvider classProvider, JarIndex jarIndex);

static JarIndexerService fromVisitor(ClassVisitor visitor) {
return (scope, classProvider, jarIndex) -> {
for (String className : scope) {
classProvider.get(className).accept(visitor);
static JarIndexerService fromVisitor(ClassVisitor visitor, String id) {
return new JarIndexerService() {
@Override
public void acceptJar(Set<String> scope, ClassProvider classProvider, JarIndex jarIndex) {
for (String className : scope) {
ClassNode node = classProvider.get(className);
if (node != null) {
node.accept(visitor);
}
}
}

@Override
public String getId() {
return id;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,18 @@ public interface NameProposalService extends EnigmaService {
*/
@Nullable
Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remapper, @Nullable Entry<?> obfEntry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping);

/**
* Creates a proposed mapping, with no javadoc and using {@link #getId()} as the source plugin ID.
* @param name the name
* @param tokenType the token type - must be either {@link TokenType#JAR_PROPOSED} or {@link TokenType#DYNAMIC_PROPOSED}
* @return the newly created mapping
*/
default EntryMapping createMapping(String name, TokenType tokenType) {
if (!tokenType.isProposed()) {
throw new IllegalArgumentException("token type must be proposed!");
}

return new EntryMapping(name, null, tokenType, this.getId());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
package org.quiltmc.enigma.api.source;

import org.quiltmc.enigma.api.class_provider.ClassProvider;
import org.quiltmc.enigma.impl.source.bytecode.BytecodeDecompiler;
import org.quiltmc.enigma.impl.source.cfr.CfrDecompiler;
import org.quiltmc.enigma.impl.source.procyon.ProcyonDecompiler;
import org.quiltmc.enigma.impl.source.vineflower.VineflowerDecompiler;

import java.util.function.BiFunction;

public class Decompilers {
public static final DecompilerService VINEFLOWER = VineflowerDecompiler::new;
public static final DecompilerService PROCYON = ProcyonDecompiler::new;
public static final DecompilerService CFR = CfrDecompiler::new;
public static final DecompilerService BYTECODE = BytecodeDecompiler::new;
public static final DecompilerService VINEFLOWER = create("enigma:vineflower", VineflowerDecompiler::new);
public static final DecompilerService PROCYON = create("enigma:procyon", ProcyonDecompiler::new);
public static final DecompilerService CFR = create("enigma:cfr", CfrDecompiler::new);
public static final DecompilerService BYTECODE = create("enigma:bytecode", BytecodeDecompiler::new);

private static DecompilerService create(String id, BiFunction<ClassProvider, SourceSettings, Decompiler> factory) {
return new DecompilerService() {
@Override
public Decompiler create(ClassProvider classProvider, SourceSettings settings) {
return factory.apply(classProvider, settings);
}

@Override
public String getId() {
return id;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public EntryMapping(@Nullable String targetName, @Nullable String javadoc) {
}

public EntryMapping {
validateSourcePluginId(sourcePluginId);

if (tokenType == TokenType.OBFUSCATED && targetName != null) {
throw new RuntimeException("cannot create a named mapping with an obfuscated token type!");
} else if (targetName == null && tokenType != TokenType.OBFUSCATED) {
Expand All @@ -34,6 +36,12 @@ public EntryMapping(@Nullable String targetName, @Nullable String javadoc) {
}
}

private static void validateSourcePluginId(String id) {
if (id != null && !id.matches("([a-z0-9_]+:[a-z0-9_/]+)")) {
throw new IllegalArgumentException("invalid plugin ID: '" + id + "! plugin ID should be all lowercase, only contain letters, numbers, underscores and slashes, and be namespaced separated by a colon.");
}
}

public EntryMapping withName(@Nullable String newName) {
return new EntryMapping(newName, this.javadoc, this.tokenType, this.sourcePluginId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

public final class BuiltinPlugin implements EnigmaPlugin {
@Override
Expand All @@ -54,64 +53,69 @@ private void registerEnumNamingService(EnigmaPluginContext ctx) {
final Map<Entry<?>, String> names = new HashMap<>();
final EnumFieldNameFindingVisitor visitor = new EnumFieldNameFindingVisitor(names);

ctx.registerService("enigma:enum_initializer_indexer", JarIndexerService.TYPE, ctx1 -> JarIndexerService.fromVisitor(visitor));
ctx.registerService(JarIndexerService.TYPE, ctx1 -> JarIndexerService.fromVisitor(visitor, "enigma:enum_initializer_indexer"));

String id = "enigma:enum_name_proposer";
ctx.registerService(id, NameProposalService.TYPE, ctx1 -> new NameProposalFunction() {
ctx.registerService(NameProposalService.TYPE, ctx1 -> new NameProposalService() {
@Override
public Map<Entry<?>, EntryMapping> apply(JarIndex jarIndex) {
public Map<Entry<?>, EntryMapping> getProposedNames(JarIndex index) {
Map<Entry<?>, EntryMapping> mappings = new HashMap<>();

jarIndex.getIndex(EntryIndex.class).getFields().forEach(field -> {
index.getIndex(EntryIndex.class).getFields().forEach(field -> {
if (names.containsKey(field)) {
mappings.put(field, new EntryMapping(names.get(field), null, TokenType.JAR_PROPOSED, id));
mappings.put(field, this.createMapping(names.get(field), TokenType.JAR_PROPOSED));
}
});

return mappings;
}

@Override
public Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remapper, @Nullable Entry<?> obfEntry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping) {
return null;
}

@Override
public String getId() {
return "enigma:enum_name_proposer";
}
});
}

private void registerSpecializedMethodNamingService(EnigmaPluginContext ctx) {
String id = "enigma:specialized_method_name_proposer";
ctx.registerService(NameProposalService.TYPE, ctx1 -> new NameProposalService() {
@Override
public Map<Entry<?>, EntryMapping> getProposedNames(JarIndex index) {
BridgeMethodIndex bridgeMethodIndex = index.getIndex(BridgeMethodIndex.class);
Map<Entry<?>, EntryMapping> mappings = new HashMap<>();

ctx.registerService(id, NameProposalService.TYPE, ctx1 -> new NameProposalFunction() {
@Override
public Map<Entry<?>, EntryMapping> apply(JarIndex jarIndex) {
BridgeMethodIndex bridgeMethodIndex = jarIndex.getIndex(BridgeMethodIndex.class);
Map<Entry<?>, EntryMapping> mappings = new HashMap<>();
bridgeMethodIndex.getSpecializedToBridge().forEach((specialized, bridge) -> {
EntryMapping mapping = this.createMapping(bridge.getName(), TokenType.JAR_PROPOSED);

bridgeMethodIndex.getSpecializedToBridge().forEach((specialized, bridge) -> {
EntryMapping mapping = new EntryMapping(bridge.getName(), null, TokenType.JAR_PROPOSED, id);
mappings.put(specialized, mapping);
// IndexEntryResolver#resolveEntry can return the bridge method, so we can just use the name
mappings.put(bridge, mapping);
});

mappings.put(specialized, mapping);
// IndexEntryResolver#resolveEntry can return the bridge method, so we can just use the name
mappings.put(bridge, mapping);
});
return mappings;
}

return mappings;
}
@Override
public Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remapper, @Nullable Entry<?> obfEntry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping) {
return null;
}

@Override
public String getId() {
return "enigma:specialized_method_name_proposer";
}
});
}

private void registerDecompilerServices(EnigmaPluginContext ctx) {
ctx.registerService("enigma:vineflower", DecompilerService.TYPE, ctx1 -> Decompilers.VINEFLOWER);
ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON);
ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR);
ctx.registerService("enigma:bytecode", DecompilerService.TYPE, ctx1 -> Decompilers.BYTECODE);
}

private abstract static class NameProposalFunction implements NameProposalService, Function<JarIndex, Map<Entry<?>, EntryMapping>> {
@Override
public Map<Entry<?>, EntryMapping> getProposedNames(JarIndex index) {
return this.apply(index);
}

@Override
public Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remapper, @Nullable Entry<?> obfEntry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping) {
return null;
}
ctx.registerService(DecompilerService.TYPE, ctx1 -> Decompilers.VINEFLOWER);
ctx.registerService(DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON);
ctx.registerService(DecompilerService.TYPE, ctx1 -> Decompilers.CFR);
ctx.registerService(DecompilerService.TYPE, ctx1 -> Decompilers.BYTECODE);
}

private static final class EnumFieldNameFindingVisitor extends ClassVisitor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,12 @@ public void init(EnigmaPluginContext ctx) {
nameAllFields(ctx, "c");
nameAllFields(ctx, "b");

String id = "test:q_to_w";
ctx.registerService(id, NameProposalService.TYPE, ctx1 -> new TestDynamicNameProposer(id));
ctx.registerService(NameProposalService.TYPE, ctx1 -> new TestDynamicNameProposer("test:q_to_w"));
}

private static void nameAllFields(EnigmaPluginContext ctx, String prefix) {
String id = "test:name_all_fields_" + prefix;
ctx.registerService(id, NameProposalService.TYPE, ctx1 -> new TestJarNameProposer(prefix, id));
ctx.registerService(NameProposalService.TYPE, ctx1 -> new TestJarNameProposer(prefix, id));
}

private record TestJarNameProposer(String prefix, String id) implements NameProposalService {
Expand All @@ -135,6 +134,11 @@ public Map<Entry<?>, EntryMapping> getProposedNames(JarIndex index) {
public Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remapper, @Nullable Entry<?> obfEntry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping) {
return null;
}

@Override
public String getId() {
return this.id;
}
}

private record TestDynamicNameProposer(String id) implements NameProposalService {
Expand All @@ -155,6 +159,11 @@ public Map<Entry<?>, EntryMapping> getDynamicProposedNames(EntryRemapper remappe

return null;
}

@Override
public String getId() {
return this.id;
}
}
}
}
Loading