diff options
Diffstat (limited to 'src/forgeInject')
7 files changed, 541 insertions, 0 deletions
diff --git a/src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java b/src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java new file mode 100644 index 00000000..8fbac050 --- /dev/null +++ b/src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java @@ -0,0 +1,34 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package mcp; + +/** + * A dummy class, required for some Forge classes to load. + * + * @deprecated Don't use this in your mods. JetBrains annotations are there for you. + */ +@Deprecated +public @interface MethodsReturnNonnullByDefault { +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/Pair.java b/src/forgeInject/java/net/fabricmc/loom/inject/Pair.java new file mode 100644 index 00000000..0206cb5d --- /dev/null +++ b/src/forgeInject/java/net/fabricmc/loom/inject/Pair.java @@ -0,0 +1,52 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.inject; + +import java.util.Map; + +final class Pair<A, B> implements Map.Entry<A, B> { + private final A first; + private final B second; + + Pair(A first, B second) { + this.first = first; + this.second = second; + } + + @Override + public A getKey() { + return first; + } + + @Override + public B getValue() { + return second; + } + + @Override + public B setValue(B value) { + throw new UnsupportedOperationException("Pairs are immutable!"); + } +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java b/src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java new file mode 100644 index 00000000..2b62dc53 --- /dev/null +++ b/src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java @@ -0,0 +1,127 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.inject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +import cpw.mods.modlauncher.api.INameMappingService; + +import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyTree; + +public class YarnNamingService implements INameMappingService { + private static final String PATH_TO_MAPPINGS = "fabric.yarnWithSrg.path"; + private TinyTree mappings = null; + + @Override + public String mappingName() { + return "srgtoyarn"; + } + + @Override + public String mappingVersion() { + return "1"; + } + + @Override + public Map.Entry<String, String> understanding() { + return new Pair<>("srg", "mcp"); + } + + @Override + public BiFunction<Domain, String, String> namingFunction() { + return this::remap; + } + + private TinyTree getMappings() { + if (mappings != null) { + return mappings; + } + + String pathStr = System.getProperty(PATH_TO_MAPPINGS); + if (pathStr == null) throw new RuntimeException("Missing system property '" + PATH_TO_MAPPINGS + "'!"); + Path path = Paths.get(pathStr); + + try (BufferedReader reader = Files.newBufferedReader(path)) { + mappings = TinyMappingFactory.loadWithDetection(reader); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + return mappings; + } + + private String remap(Domain domain, String name) { + TinyTree mappings = getMappings(); + + switch (domain) { + case CLASS: + boolean dot = name.contains("."); + return find(mappings.getClasses(), def -> maybeReplace(dot, def.getName("srg"), '/', '.').equals(name)) + .map(def -> maybeReplace(dot, def.getName("named"), '/', '.')) + .orElse(name); + case METHOD: + return mappings.getClasses().stream() + .flatMap(def -> def.getMethods().stream()) + .filter(def -> def.getName("srg").equals(name)) + .findAny() + .map(def -> def.getName("named")) + .orElse(name); + case FIELD: + return mappings.getClasses().stream() + .flatMap(def -> def.getFields().stream()) + .filter(def -> def.getName("srg").equals(name)) + .findAny() + .map(def -> def.getName("named")) + .orElse(name); + default: + return name; + } + } + + // From CollectionUtil + private static <E> Optional<E> find(Iterable<? extends E> collection, Predicate<? super E> filter) { + for (E e : collection) { + if (filter.test(e)) { + return Optional.of(e); + } + } + + return Optional.empty(); + } + + private static String maybeReplace(boolean run, String s, char from, char to) { + return run ? s.replace(from, to) : s; + } +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java b/src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java new file mode 100644 index 00000000..1fe961c6 --- /dev/null +++ b/src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java @@ -0,0 +1,93 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.inject.mixin; + +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.IncompatibleEnvironmentException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.MixinEnvironment; + +import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyTree; + +public class ForgeLoomMixinRemapperInjectorService implements ITransformationService { + private static final Logger LOGGER = LogManager.getLogger("ForgeLoomRemapperInjector"); + + @Override + public String name() { + return "ForgeLoomMixinRemapperInjector"; + } + + @Override + public void initialize(IEnvironment environment) { + } + + @Override + public void beginScanning(IEnvironment environment) { + LOGGER.debug("We will be injecting our remapper."); + + try { + MixinEnvironment.getDefaultEnvironment().getRemappers().add(new MixinIntermediaryDevRemapper(Objects.requireNonNull(resolveMappings()), "intermediary", "named")); + LOGGER.debug("We have successfully injected our remapper."); + } catch (Exception e) { + LOGGER.debug("We have failed to inject our remapper.", e); + } + } + + @Override + public void onLoad(IEnvironment env, Set<String> otherServices) throws IncompatibleEnvironmentException { + } + + @Override + public List<ITransformer> transformers() { + return Collections.emptyList(); + } + + private static TinyTree resolveMappings() { + try { + String srgNamedProperty = System.getProperty("mixin.forgeloom.inject.mappings.srg-named"); + Path path = Paths.get(srgNamedProperty); + + try (BufferedReader reader = Files.newBufferedReader(path)) { + return TinyMappingFactory.loadWithDetection(reader); + } + } catch (Throwable throwable) { + throwable.printStackTrace(); + return null; + } + } +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java b/src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java new file mode 100644 index 00000000..ef5724dc --- /dev/null +++ b/src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java @@ -0,0 +1,233 @@ +/* + * Copyright 2016 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.loom.inject.mixin; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import org.spongepowered.asm.mixin.transformer.ClassInfo; + +import net.fabricmc.mapping.tree.ClassDef; +import net.fabricmc.mapping.tree.Descriptored; +import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.mapping.util.MixinRemapper; + +public class MixinIntermediaryDevRemapper extends MixinRemapper { + private static final String ambiguousName = "<ambiguous>"; // dummy value for ambiguous mappings - needs querying with additional owner and/or desc info + + private final Set<String> allPossibleClassNames = new HashSet<>(); + private final Map<String, String> nameFieldLookup = new HashMap<>(); + private final Map<String, String> nameMethodLookup = new HashMap<>(); + private final Map<String, String> nameDescFieldLookup = new HashMap<>(); + private final Map<String, String> nameDescMethodLookup = new HashMap<>(); + + public MixinIntermediaryDevRemapper(TinyTree mappings, String from, String to) { + super(mappings, from, to); + + for (ClassDef classDef : mappings.getClasses()) { + allPossibleClassNames.add(classDef.getName(from)); + allPossibleClassNames.add(classDef.getName(to)); + + putMemberInLookup(from, to, classDef.getFields(), nameFieldLookup, nameDescFieldLookup); + putMemberInLookup(from, to, classDef.getMethods(), nameMethodLookup, nameDescMethodLookup); + } + } + + private <T extends Descriptored> void putMemberInLookup(String from, String to, Collection<T> descriptored, Map<String, String> nameMap, Map<String, String> nameDescMap) { + for (T field : descriptored) { + String nameFrom = field.getName(from); + String descFrom = field.getDescriptor(from); + String nameTo = field.getName(to); + + String prev = nameMap.putIfAbsent(nameFrom, nameTo); + + if (prev != null && prev != ambiguousName && !prev.equals(nameTo)) { + nameDescMap.put(nameFrom, ambiguousName); + } + + String key = getNameDescKey(nameFrom, descFrom); + prev = nameDescMap.putIfAbsent(key, nameTo); + + if (prev != null && prev != ambiguousName && !prev.equals(nameTo)) { + nameDescMap.put(key, ambiguousName); + } + } + } + + private void throwAmbiguousLookup(String type, String name, String desc) { + throw new RuntimeException("Ambiguous Mixin: " + type + " lookup " + name + " " + desc + " is not unique"); + } + + private String mapMethodNameInner(String owner, String name, String desc) { + String result = super.mapMethodName(owner, name, desc); + + if (result.equals(name)) { + String otherClass = unmap(owner); + return super.mapMethodName(otherClass, name, unmapDesc(desc)); + } else { + return result; + } + } + + private String mapFieldNameInner(String owner, String name, String desc) { + String result = super.mapFieldName(owner, name, desc); + + if (result.equals(name)) { + String otherClass = unmap(owner); + return super.mapFieldName(otherClass, name, unmapDesc(desc)); + } else { + return result; + } + } + + @Override + public String mapMethodName(String owner, String name, String desc) { + // handle unambiguous values early + if (owner == null || allPossibleClassNames.contains(owner)) { + String newName; + + if (desc == null) { + newName = nameMethodLookup.get(name); + } else { + newName = nameDescMethodLookup.get(getNameDescKey(name, desc)); + } + + if (newName != null) { + if (newName == ambiguousName) { + if (owner == null) { + throwAmbiguousLookup("method", name, desc); + } + } else { + return newName; + } + } else if (owner == null) { + return name; + } else { + // FIXME: this kind of namespace mixing shouldn't happen.. + // TODO: this should not repeat more than once + String unmapOwner = unmap(owner); + String unmapDesc = unmapDesc(desc); + + if (!unmapOwner.equals(owner) || !unmapDesc.equals(desc)) { + return mapMethodName(unmapOwner, name, unmapDesc); + } else { + // take advantage of the fact allPossibleClassNames + // and nameDescLookup cover all sets; if none are present, + // we don't have a mapping for it. + return name; + } + } + } + + Queue<ClassInfo> classInfos = new ArrayDeque<>(); + classInfos.add(ClassInfo.forName(owner)); + + while (!classInfos.isEmpty()) { + ClassInfo c = classInfos.remove(); + String ownerO = unmap(c.getName()); + String s; + + if (!(s = mapMethodNameInner(ownerO, name, desc)).equals(name)) { + return s; + } + + if (!c.getSuperName().startsWith("java/")) { + ClassInfo cSuper = c.getSuperClass(); + + if (cSuper != null) { + classInfos.add(cSuper); + } + } + + for (String itf : c.getInterfaces()) { + if (itf.startsWith("java/")) { + continue; + } + + ClassInfo cItf = ClassInfo.forName(itf); + + if (cItf != null) { + classInfos.add(cItf); + } + } + } + + return name; + } + + @Override + public String mapFieldName(String owner, String name, String desc) { + // handle unambiguous values early + if (owner == null || allPossibleClassNames.contains(owner)) { + String newName = nameDescFieldLookup.get(getNameDescKey(name, desc)); + + if (newName != null) { + if (newName == ambiguousName) { + if (owner == null) { + throwAmbiguousLookup("field", name, desc); + } + } else { + return newName; + } + } else if (owner == null) { + return name; + } else { + // FIXME: this kind of namespace mixing shouldn't happen.. + // TODO: this should not repeat more than once + String unmapOwner = unmap(owner); + String unmapDesc = unmapDesc(desc); + + if (!unmapOwner.equals(owner) || !unmapDesc.equals(desc)) { + return mapFieldName(unmapOwner, name, unmapDesc); + } else { + // take advantage of the fact allPossibleClassNames + // and nameDescLookup cover all sets; if none are present, + // we don't have a mapping for it. + return name; + } + } + } + + ClassInfo c = ClassInfo.forName(map(owner)); + + while (c != null) { + String nextOwner = unmap(c.getName()); + String s; + + if (!(s = mapFieldNameInner(nextOwner, name, desc)).equals(name)) { + return s; + } + + if (c.getSuperName().startsWith("java/")) { + break; + } + + c = c.getSuperClass(); + } + + return name; + } + + private static String getNameDescKey(String name, String descriptor) { + return name + ";;" + descriptor; + } +} diff --git a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService new file mode 100644 index 00000000..45290566 --- /dev/null +++ b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService @@ -0,0 +1 @@ +net.fabricmc.loom.inject.YarnNamingService diff --git a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService new file mode 100644 index 00000000..0fb04144 --- /dev/null +++ b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -0,0 +1 @@ +net.fabricmc.loom.inject.mixin.ForgeLoomMixinRemapperInjectorService
\ No newline at end of file |
