org.spongepowered.asm.util.LanguageFeatures Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sponge-mixin Show documentation
Show all versions of sponge-mixin Show documentation
Fabric Mixin is a trait/mixin and bytecode weaving framework for Java using ASM.
The newest version!
/*
* This file is part of Mixin, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered
* Copyright (c) contributors
*
* 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 org.spongepowered.asm.util;
import java.lang.reflect.Field;
import java.util.List;
import java.util.ListIterator;
import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.spongepowered.asm.util.Bytecode.Visibility;
import org.spongepowered.asm.util.asm.ASM;
import org.spongepowered.asm.util.asm.ClassNodeAdapter;
/**
* Bitmask values for language features supported. Contains utility methods for
* detecting language features in use in supplied class nodes.
*/
public final class LanguageFeatures {
/**
* Language version supports methods in interfaces
*/
public static final int METHODS_IN_INTERFACES = 1;
/**
* Language version supports synthetic private methods in interfaces
*/
public static final int PRIVATE_SYNTHETIC_METHODS_IN_INTERFACES = 2;
/**
* Language version supports user-defined private methods in interfaces
*/
public static final int PRIVATE_METHODS_IN_INTERFACES = 4;
/**
* Native nesting
*/
public static final int NESTING = 8;
/**
* Dynamic constants
*/
public static final int DYNAMIC_CONSTANTS = 16;
/**
* Record types
*/
public static final int RECORDS = 32;
/**
* Sealed classes (permitted subclasses)
*/
public static final int SEALED_CLASSES = 64;
/**
* Utility class
*/
private LanguageFeatures() {
}
/**
* Scan the supplied class node to determine required language features via
* heuristic.
*
* @param classNode ClassNode to scan (must include method bodies for
* reliable detection)
* @return detected language features
*/
public static int scan(ClassNode classNode) {
int features = LanguageFeatures.scanClassFeatures(classNode);
boolean isInterface = Bytecode.hasFlag(classNode, Opcodes.ACC_INTERFACE);
for (MethodNode methodNode : classNode.methods) {
if (isInterface) {
features |= LanguageFeatures.scanInterfaceFeatures(methodNode);
} else {
features |= LanguageFeatures.scanMethodFeatures(methodNode);
}
}
return features;
}
/**
* Sacn for features at the class level
*/
private static int scanClassFeatures(ClassNode classNode) {
int features = 0;
String nestHostClass = ClassNodeAdapter.getNestHostClass(classNode);
List nestMembers = ClassNodeAdapter.getNestMembers(classNode);
if (nestHostClass != null || (nestMembers != null && nestMembers.size() > 0)) {
features |= LanguageFeatures.NESTING;
}
return features;
}
/**
* Scan the method for interface-specific features
*/
private static int scanInterfaceFeatures(MethodNode methodNode) {
int features = 0;
if (!Bytecode.hasFlag(methodNode, Opcodes.ACC_ABSTRACT)) {
features |= LanguageFeatures.METHODS_IN_INTERFACES;
}
if (Bytecode.getVisibility(methodNode).isLessThan(Visibility.PUBLIC)) {
features |= Bytecode.hasFlag(methodNode, Opcodes.ACC_SYNTHETIC)
? LanguageFeatures.PRIVATE_SYNTHETIC_METHODS_IN_INTERFACES
: LanguageFeatures.PRIVATE_METHODS_IN_INTERFACES;
}
return features;
}
/**
* Scan the method code for feature requirements
*/
private static int scanMethodFeatures(MethodNode methodNode) {
// ConstantDynamic only exists in ASM 6 and later
if (ASM.isAtLeastVersion(6)) {
for (ListIterator iter = methodNode.instructions.iterator(); iter.hasNext();) {
AbstractInsnNode insn = iter.next();
if (insn instanceof LdcInsnNode && ((LdcInsnNode)insn).cst instanceof ConstantDynamic) {
return LanguageFeatures.DYNAMIC_CONSTANTS;
}
}
}
return 0;
}
/**
* Format the supplied feature mask as a plain-text list for use in error
* messages etc.
*
* @param features Language features to format
* @return Formatted list of features
*/
public static final String format(int features) {
StringBuilder sb = new StringBuilder("[");
try {
int count = 0;
for (Field field : LanguageFeatures.class.getDeclaredFields()) {
if ((features & field.getInt(null)) != 0) {
if (count++ > 0) {
sb.append(',');
}
sb.append(field.getName());
}
}
} catch (ReflectiveOperationException ex) {
sb.append("ERROR");
}
return sb.append(']').toString();
}
}