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

com.jetbrains.pluginverifier.jdk.JdkJImageResolver.kt Maven / Gradle / Ivy

Go to download

JetBrains Plugin Verifier Classes for IntelliJ Platform integration with API usage detection and reporting.

The newest version!
/*
 * Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 */

package com.jetbrains.pluginverifier.jdk

import com.jetbrains.plugin.structure.base.utils.rethrowIfInterrupted
import com.jetbrains.plugin.structure.classes.resolvers.*
import com.jetbrains.plugin.structure.classes.utils.AsmUtil
import org.objectweb.asm.tree.ClassNode
import java.net.URI
import java.nio.file.*
import java.util.*
import java.util.stream.Collectors

/**
 * [Resolver] that reads class files from JImage corresponding to `/lib/modules` file of JDK 9 and later.
 */
class JdkJImageResolver(jdkPath: Path, override val readMode: ReadMode) : Resolver() {
  private companion object {

    val JRT_SCHEME_URI: URI = URI.create("jrt:/")
  }

  private val fileOrigin: FileOrigin = JdkFileOrigin(jdkPath)

  private val classNameToModuleName: Map

  private val packageSet = PackageSet()

  private val nameSeparator: String

  private val modulesPath: Path

  private val fileSystem: FileSystem

  init {
    fileSystem = try {
      getJrtFileSystem(jdkPath)
    } catch (e: Exception) {
      throw RuntimeException("Unable to read content from jrt:/ file system.", e)
    }

    nameSeparator = fileSystem.separator
    modulesPath = fileSystem.getPath("/modules")

    classNameToModuleName = Files.walk(modulesPath).use { stream ->
      stream
        .filter { p -> p.fileName.toString().endsWith(".class") }
        .collect(
          Collectors.toMap(
            { p -> getClassName(p) },
            { p -> getModuleName(p) },
            { one, _ -> one }
          )
        )
    }

    for (className in classNameToModuleName.keys) {
      packageSet.addPackagesOfClass(className)
    }
  }

  private fun getJrtFileSystem(javaHome: Path): FileSystem {
    return FileSystems.newFileSystem(JRT_SCHEME_URI, mapOf("java.home" to javaHome.toString()))
  }

  private fun getModuleName(classPath: Path): String =
    modulesPath.relativize(classPath).first().toString()

  private fun getClassName(classPath: Path): String {
    val relative = modulesPath.relativize(classPath)
    return relative
      .subpath(1, relative.nameCount).toString()
      .substringBeforeLast(".class").replace(nameSeparator, "/")
  }

  override val allClasses
    get() = classNameToModuleName.keys

  override val allPackages
    get() = packageSet.getAllPackages()

  override val allBundleNameSet
    get() = ResourceBundleNameSet(emptyMap())

  /**
   * Resolve class with specified binary name.
   *
   * The class name must be slash separated, e.g. `java/lang/String`.
   */
  override fun resolveClass(className: String): ResolutionResult {
    val moduleName = classNameToModuleName[className]
    if (moduleName != null) {
      val classPath = modulesPath.resolve(moduleName).resolve(className.replace("/", nameSeparator) + ".class")
      return readClass(className, classPath)
    }
    return ResolutionResult.NotFound
  }

  private fun readClass(className: String, classPath: Path): ResolutionResult =
    try {
      val classNode = readClassNode(className, classPath)
      ResolutionResult.Found(classNode, fileOrigin)
    } catch (e: InvalidClassFileException) {
      ResolutionResult.Invalid(e.message)
    } catch (e: Exception) {
      e.rethrowIfInterrupted()
      ResolutionResult.FailedToRead(e.localizedMessage ?: e.javaClass.name)
    }

  override fun resolveExactPropertyResourceBundle(baseName: String, locale: Locale) = ResolutionResult.NotFound

  private fun readClassNode(className: String, classFilePath: Path): ClassNode =
    Files.newInputStream(classFilePath, StandardOpenOption.READ).use { inputStream ->
      AsmUtil.readClassNode(className, inputStream, readMode == ReadMode.FULL)
    }

  override fun containsClass(className: String) = className in classNameToModuleName

  override fun containsPackage(packageName: String) = packageSet.containsPackage(packageName)

  override fun processAllClasses(processor: (ResolutionResult) -> Boolean): Boolean {
    Files.walk(modulesPath).use { stream ->
      for (classPath in stream.filter { it.fileName.toString().endsWith(".class") }) {
        val className = getClassName(classPath)
        val result = readClass(className, classPath)
        if (!processor(result)) {
          return false
        }
      }
    }
    return true
  }

  override fun close() {
    fileSystem.close()
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy