All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sootup.java.bytecode.frontend.AsmUtil Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package sootup.java.bytecode.frontend;
/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2018-2023 Andreas Dann, Markus Schmidt, Jan Martin Persch and others
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import sootup.core.frontend.ResolveException;
import sootup.core.jimple.common.constant.ClassConstant;
import sootup.core.model.Modifier;
import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.types.VoidType;
import sootup.java.core.AnnotationUsage;
import sootup.java.core.ConstantUtil;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.ModuleModifier;
import sootup.java.core.language.JavaJimple;
import sootup.java.core.types.AnnotationType;
import sootup.java.core.types.JavaClassType;

public final class AsmUtil {

  private AsmUtil() {}

  public static final int SUPPORTED_ASM_OPCODE = Opcodes.ASM9;

  /**
   * Initializes a class node.
   *
   * @param classSource The source.
   * @param classNode The node to initialize
   */
  protected static void initAsmClassSource(
      @Nonnull Path classSource, @Nonnull ClassVisitor classNode) throws IOException {
    try (InputStream sourceFileInputStream = Files.newInputStream(classSource)) {
      ClassReader clsr = new ClassReader(sourceFileInputStream);

      clsr.accept(classNode, ClassReader.SKIP_FRAMES);
    }
  }

  /**
   * Determines if a type is a dword type.
   *
   * @param type the type to check.
   * @return {@code true} if its a dword type.
   */
  public static boolean isDWord(@Nonnull Type type) {
    return type == PrimitiveType.getLong() || type == PrimitiveType.getDouble();
  }

  /**
   * Converts an str class name to a fully qualified name.
   *
   * @param str str name.
   * @return fully qualified name.
   */
  public static String toQualifiedName(@Nonnull String str) {
    final int endpos = str.length() - 1;
    if (endpos > 2 && str.charAt(endpos) == ';' && str.charAt(0) == 'L') {
      str = str.substring(1, endpos);
    }
    return str.replace('/', '.');
  }

  public static EnumSet getModifiers(int access) {
    EnumSet modifierEnumSet = EnumSet.noneOf(Modifier.class);

    // add all modifiers for which (access & ABSTRACT) =! 0
    for (Modifier modifier : Modifier.values()) {
      if ((access & modifier.getBytecode()) != 0) {
        modifierEnumSet.add(modifier);
      }
    }
    return modifierEnumSet;
  }

  public static EnumSet getModuleModifiers(int access) {
    EnumSet modifierEnumSet = EnumSet.noneOf(ModuleModifier.class);

    // add all modifiers for which (access & ABSTRACT) =! 0
    for (ModuleModifier modifier : ModuleModifier.values()) {
      if ((access & modifier.getBytecode()) != 0) {
        modifierEnumSet.add(modifier);
      }
    }
    return modifierEnumSet;
  }

  @Nonnull
  public static Collection asmIdToSignature(
      @Nullable Iterable asmClassNames) {
    if (asmClassNames == null) {
      return Collections.emptyList();
    }

    return StreamSupport.stream(asmClassNames.spliterator(), false)
        .map(AsmUtil::toJimpleClassType)
        .collect(Collectors.toList());
  }

  @Nonnull
  public static JavaClassType toJimpleClassType(@Nonnull String asmClassName) {
    return JavaIdentifierFactory.getInstance().getClassType(toQualifiedName(asmClassName));
  }

  /**
   * Converts a type descriptor to a Jimple reference type.
   *
   * @param desc the descriptor.
   * @return the reference type.
   */
  public static Type toJimpleSignature(@Nonnull String desc) {
    return desc.charAt(0) == '['
        ? toJimpleType(desc)
        : JavaIdentifierFactory.getInstance().getClassType(toQualifiedName(desc));
  }

  @Nonnull
  public static Type toJimpleType(@Nonnull String desc) {
    int nrDims = countArrayDim(desc);
    if (nrDims > 0) {
      desc = desc.substring(nrDims);
    }

    Type baseType = toPrimitiveOrVoidType(desc).orElse(null);
    if (baseType == null) {
      if (desc.charAt(0) != 'L') {
        throw new AssertionError("Unknown descriptor: " + desc);
      }
      if (desc.charAt(desc.length() - 1) != ';') {
        throw new AssertionError("Invalid reference descriptor: " + desc);
      }
      String name = desc.substring(1, desc.length() - 1);
      baseType = JavaIdentifierFactory.getInstance().getType(toQualifiedName(name));
    }
    if ((baseType instanceof PrimitiveType || baseType instanceof VoidType) && desc.length() > 1) {
      throw new AssertionError("Invalid primitive type descriptor: " + desc);
    }
    return nrDims > 0
        ? JavaIdentifierFactory.getInstance().getArrayType(baseType, nrDims)
        : baseType;
  }

  @Nonnull
  public static Type arrayTypetoJimpleType(@Nonnull String desc) {
    if (desc.startsWith("[")) {
      return toJimpleType(desc);
    }
    return toJimpleClassType(desc);
  }

  /** returns the amount of dimensions of a description. */
  private static int countArrayDim(@Nonnull String desc) {
    int nrDims = desc.lastIndexOf('[') + 1;
    for (int index = 0; index < nrDims; index++) {
      if (desc.charAt(index) != '[') {
        throw new AssertionError("Invalid array descriptor: " + desc);
      }
    }
    return nrDims;
  }

  /**
   * Converts a description to a primitive type or the void type. If the description does not match
   * it will return an empty Optional
   */
  private static Optional toPrimitiveOrVoidType(@Nonnull String desc) {
    if (desc.length() > 1) {
      return Optional.empty();
    }
    switch (desc.charAt(0)) {
      case 'Z':
        return Optional.of(PrimitiveType.getBoolean());
      case 'B':
        return Optional.of(PrimitiveType.getByte());
      case 'C':
        return Optional.of(PrimitiveType.getChar());
      case 'S':
        return Optional.of(PrimitiveType.getShort());
      case 'I':
        return Optional.of(PrimitiveType.getInt());
      case 'F':
        return Optional.of(PrimitiveType.getFloat());
      case 'J':
        return Optional.of(PrimitiveType.getLong());
      case 'D':
        return Optional.of(PrimitiveType.getDouble());
      case 'V':
        return Optional.of(VoidType.getInstance());
      default:
    }
    return Optional.empty();
  }

  /** Converts n types contained in desc to a list of Jimple Types */
  @Nonnull
  public static List toJimpleSignatureDesc(@Nonnull String desc) {
    // [ms] more types are possibly needed for method type which is ( arg-type* ) ret-type
    List types = new ArrayList<>(1);
    int len = desc.length();
    int idx = 0;
    all:
    while (idx != len) {
      int nrDims = 0;
      Type baseType = null;
      this_type:
      while (idx != len) {
        char c = desc.charAt(idx++);
        switch (c) {
          case '(':
          case ')':
            continue all;
          case '[':
            ++nrDims;
            continue this_type;
          case 'Z':
            baseType = PrimitiveType.getBoolean();
            break this_type;
          case 'B':
            baseType = PrimitiveType.getByte();
            break this_type;
          case 'C':
            baseType = PrimitiveType.getChar();
            break this_type;
          case 'S':
            baseType = PrimitiveType.getShort();
            break this_type;
          case 'I':
            baseType = PrimitiveType.getInt();
            break this_type;
          case 'F':
            baseType = PrimitiveType.getFloat();
            break this_type;
          case 'J':
            baseType = PrimitiveType.getLong();
            break this_type;
          case 'D':
            baseType = PrimitiveType.getDouble();
            break this_type;
          case 'V':
            baseType = VoidType.getInstance();
            break this_type;
          case 'L':
            int begin = idx;
            idx = desc.indexOf(';', begin);
            String cls = desc.substring(begin, idx++);
            baseType = JavaIdentifierFactory.getInstance().getType(toQualifiedName(cls));
            break this_type;
          default:
            throw new AssertionError("Unknown type: '" + c + "' in '" + desc + "'.");
        }
      }

      if (baseType != null && nrDims > 0) {
        types.add(JavaIdentifierFactory.getInstance().getArrayType(baseType, nrDims));
      } else {
        types.add(baseType);
      }
    }
    return types;
  }

  public static String toString(AbstractInsnNode insn) {
    Printer printer = new Textifier();
    TraceMethodVisitor mp = new TraceMethodVisitor(printer);

    insn.accept(mp);
    StringWriter sw = new StringWriter();
    printer.print(new PrintWriter(sw));
    return Arrays.stream(sw.toString().split("\n"))
        .filter(line -> !line.trim().isEmpty())
        .reduce("", String::concat);
  }

  public static Iterable createAnnotationUsage(
      List invisibleParameterAnnotation) {
    if (invisibleParameterAnnotation == null) {
      return Collections.emptyList();
    }

    List annotationUsages = new ArrayList<>();
    for (AnnotationNode e : invisibleParameterAnnotation) {

      Map paramMap = new HashMap<>();

      if (e.values != null) {
        for (int j = 0; j < e.values.size(); j++) {
          final String annotationName = (String) e.values.get(j);
          final Object annotationValue = e.values.get(++j);

          // repeatable annotations will have annotations (as a ArrayList) as value!
          if (annotationValue instanceof ArrayList
              && !((ArrayList) annotationValue).isEmpty()
              && ((ArrayList) annotationValue).get(0) instanceof AnnotationNode) {
            final ArrayList annotationValueList =
                (ArrayList) annotationValue;

            paramMap.put(annotationName, createAnnotationUsage(annotationValueList));
          } else {
            if (annotationValue instanceof ArrayList) {
              paramMap.put(
                  annotationName,
                  ((ArrayList) annotationValue)
                      .stream().map(AsmUtil::convertAnnotationValue).collect(Collectors.toList()));
            } else {
              paramMap.put(annotationName, convertAnnotationValue(annotationValue));
            }
          }
        }
      }

      AnnotationType at =
          JavaIdentifierFactory.getInstance().getAnnotationType(AsmUtil.toQualifiedName(e.desc));
      annotationUsages.add(new AnnotationUsage(at, paramMap));
    }

    return annotationUsages;
  }

  public static Object convertAnnotationValue(Object annotationValue) {
    if (annotationValue instanceof String[]) {
      // is an enum
      // [0] is the type of the enum
      // [1] is the value of the enum
      // transform the enum type to a fully qualified name
      String[] enumData = (String[]) annotationValue;
      enumData[0] = AsmUtil.toQualifiedName(enumData[0]);
      return ConstantUtil.fromObject(enumData);
    } else {
      if (annotationValue instanceof org.objectweb.asm.Type) {
        // is a class constant
        // transform asm Type to ClassConstant
        ClassConstant classConstant =
            JavaJimple.getInstance()
                .newClassConstant(((org.objectweb.asm.Type) annotationValue).toString());
        return ConstantUtil.fromObject(classConstant);
      }
    }
    return ConstantUtil.fromObject(annotationValue);
  }

  @Nonnull
  public static ClassNode getModuleDescriptor(Path moduleInfoFile) {
    ClassNode moduleDescriptor;
    try (InputStream sourceFileInputStream = Files.newInputStream(moduleInfoFile)) {
      ClassReader clsr = new ClassReader(sourceFileInputStream);
      moduleDescriptor = new ClassNode(AsmUtil.SUPPORTED_ASM_OPCODE);
      clsr.accept(moduleDescriptor, ClassReader.SKIP_FRAMES);
    } catch (IOException e) {
      throw new ResolveException("Error loading the module-descriptor", moduleInfoFile, e);
    }
    return moduleDescriptor;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy