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

sootup.java.bytecode.inputlocation.MultiReleaseJarAnalysisInputLocation Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package sootup.java.bytecode.inputlocation;

/*-
 * #%L
 * Soot
 * %%
 * Copyright (C) 2018-2020 Manuel Benz, Christian Brüggemann, Markus Schmidt 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.FileInputStream;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import sootup.core.Language;
import sootup.core.frontend.SootClassSource;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.model.SourceType;
import sootup.core.transform.BodyInterceptor;
import sootup.core.types.ClassType;
import sootup.core.views.View;
import sootup.java.core.JavaSootClassSource;

/**
 * If the user wants to analyze a Multi-Release Jar, they have to specify the language level to
 * analyze explicitly. if there is no match for the given language level, the default location
 * inside the jar will be used.
 *
 * 

see JEP 238 */ public class MultiReleaseJarAnalysisInputLocation extends ArchiveBasedAnalysisInputLocation { // ModularInputLocations exist since Java 9 -> in previous language levels its structured like a // "usual" Jar protected static final Integer DEFAULT_VERSION = 0; @Nonnull protected final Language language; @Nonnull private final List bodyInterceptors; @Nonnull protected final Map inputLocations = new LinkedHashMap<>(); public static AnalysisInputLocation create( @Nonnull Path path, @Nonnull SourceType srcType, @Nonnull Language language, List bodyInterceptors) { if (isMultiReleaseJar(path)) { return new MultiReleaseJarAnalysisInputLocation( path, srcType, language, bodyInterceptors, true); } return PathBasedAnalysisInputLocation.create( path, srcType, bodyInterceptors, Collections.singletonList(Paths.get("/META-INF"))); } public MultiReleaseJarAnalysisInputLocation(@Nonnull Path path, @Nonnull Language language) { this(path, SourceType.Application, language); } public MultiReleaseJarAnalysisInputLocation( @Nonnull Path path, @Nonnull SourceType srcType, @Nonnull Language language) { this(path, srcType, language, Collections.emptyList()); } public MultiReleaseJarAnalysisInputLocation( @Nonnull Path path, @Nonnull SourceType srcType, @Nonnull Language language, @Nonnull List bodyInterceptors) { this(path, srcType, language, bodyInterceptors, isMultiReleaseJar(path)); } protected MultiReleaseJarAnalysisInputLocation( @Nonnull Path path, @Nonnull SourceType srcType, @Nonnull Language language, @Nonnull List bodyInterceptors, boolean isMultiRelease) { super(path, srcType); this.language = language; this.bodyInterceptors = bodyInterceptors; if (!isMultiRelease) { throw new IllegalArgumentException("The given path does not point to a multi release jar."); } FileSystem fs; try { fs = fileSystemCache.get(path); } catch (ExecutionException e) { throw new IllegalArgumentException("Could not open filesystemcache.", e); } final Path archiveRoot = fs.getPath("/"); Path versionedRoot = archiveRoot.getFileSystem().getPath("/META-INF/versions/"); try (Stream list = Files.list(versionedRoot)) { list.map( dir -> { String versionDirName = dir.getFileName().toString(); return versionDirName.substring(0, versionDirName.length() - 1); }) .map(Integer::new) .filter(version -> version <= language.getVersion()) .sorted(Comparator.reverseOrder()) .forEach( version -> { final Path versionRoot = archiveRoot .getFileSystem() .getPath("/META-INF", "versions", version.toString()); inputLocations.put( version, PathBasedAnalysisInputLocation.create( versionRoot, sourceType, bodyInterceptors, Collections.emptyList())); }); inputLocations.put( DEFAULT_VERSION, createAnalysisInputLocation(archiveRoot, srcType, getBodyInterceptors())); } catch (IOException e) { throw new IllegalStateException("Can not index the given file.", e); } } protected AnalysisInputLocation createAnalysisInputLocation( Path archiveRoot, SourceType sourceType, List bodyInterceptors) { return PathBasedAnalysisInputLocation.create( archiveRoot, sourceType, bodyInterceptors, Collections.singletonList(Paths.get("/META-INF"))); } @Override @Nonnull public Optional getClassSource(@Nonnull ClassType type, @Nonnull View view) { for (AnalysisInputLocation analysisInputLocation : inputLocations.values()) { Optional classSource = analysisInputLocation.getClassSource(type, view); if (classSource.isPresent()) { SootClassSource src = classSource.get(); JavaSootClassSource javaSootClassSource = (JavaSootClassSource) src; return Optional.of(javaSootClassSource); } } return Optional.empty(); } @Override @Nonnull public Collection getClassSources(@Nonnull View view) { Collection classSources = new ArrayList<>(); inputLocations.values().stream() .flatMap(location -> location.getClassSources(view).stream()) .map(src -> (JavaSootClassSource) src) .forEach( cs -> { // do not add duplicate class sources if (classSources.stream() .noneMatch( bestMatchCS -> bestMatchCS .getClassType() .getFullyQualifiedName() .equals(cs.getClassType().getFullyQualifiedName()))) { classSources.add(cs); } }); return classSources; } @Nonnull public Language getLanguage() { return language; } public static boolean isMultiReleaseJar(Path path) { try { FileInputStream inputStream = new FileInputStream(path.toFile()); JarInputStream jarStream = new JarInputStream(inputStream); Manifest mf = jarStream.getManifest(); if (mf == null) { return false; } Attributes attributes = mf.getMainAttributes(); String value = attributes.getValue("Multi-Release"); if (value == null) { return false; } return Boolean.parseBoolean(value); } catch (IOException e) { throw new IllegalArgumentException("Manifest file not found.", e); } } @Override @Nonnull public List getBodyInterceptors() { return bodyInterceptors; } /** * lists all versions from the version directories inside the META-INF/ directory - excluding the * default implemention version */ protected static List getLanguageVersions(@Nonnull Path path) { FileSystem fs; try { fs = fileSystemCache.get(path); } catch (ExecutionException e) { throw new IllegalArgumentException("Could not open filesystemcache.", e); } final Path archiveRoot = fs.getPath("/"); Path versionedRoot = archiveRoot.getFileSystem().getPath("/META-INF/versions/"); try (Stream list = Files.list(versionedRoot)) { return list.map( dir -> { String versionDirName = dir.getFileName().toString(); return versionDirName.substring(0, versionDirName.length() - 1); }) .map(Integer::new) .sorted() .collect(Collectors.toCollection(ArrayList::new)); } catch (IOException e) { throw new IllegalStateException("Can not index the given file.", e); } } @Override public boolean equals(Object o) { if (!(o instanceof MultiReleaseJarAnalysisInputLocation)) { return false; } return path.equals(((MultiReleaseJarAnalysisInputLocation) o).path); } @Override public int hashCode() { return path.hashCode(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy