Extract JS-related LibraryUtils utilities to JsLibraryUtils

This commit is contained in:
Alexander Udalov
2015-12-25 22:11:55 +03:00
parent f9085327e7
commit d1f2255d38
5 changed files with 155 additions and 144 deletions
@@ -0,0 +1,139 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.utils
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.Processor
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
import java.io.File
import java.io.IOException
import java.util.zip.ZipFile
object JsLibraryUtils {
private val LOG = Logger.getInstance(LibraryUtils::class.java)
private val META_INF_RESOURCES = "${LibraryUtils.META_INF}resources/"
@JvmStatic fun copyJsFilesFromLibraries(libraries: List<String>, outputLibraryJsPath: String) {
for (library in libraries) {
val file = File(library)
assert(file.exists()) { "Library $library not found" }
if (file.isDirectory) {
copyJsFilesFromDirectory(file, outputLibraryJsPath)
}
else {
copyJsFilesFromZip(file, outputLibraryJsPath)
}
}
}
@JvmStatic fun traverseJsLibraries(libs: List<File>, action: (content: String, path: String) -> Unit) {
libs.forEach { traverseJsLibrary(it, action) }
}
@JvmStatic fun traverseJsLibrary(lib: File, action: (content: String, path: String) -> Unit) {
when {
lib.isDirectory -> traverseDirectory(lib, action)
FileUtil.isJarOrZip(lib) -> traverseArchive(lib, action)
lib.name.endsWith(KotlinJavascriptMetadataUtils.JS_EXT) -> {
lib.runIfFileExists(action)
val jsFile = lib.withReplacedExtensionOrNull(
KotlinJavascriptMetadataUtils.META_JS_SUFFIX, KotlinJavascriptMetadataUtils.JS_EXT
)
jsFile?.runIfFileExists(action)
}
else -> throw IllegalArgumentException("Unknown library format (directory, zip or js file expected): $lib")
}
}
private fun File.runIfFileExists(action: (content: String, path: String) -> Unit) {
if (isFile) {
action(FileUtil.loadFile(this), "")
}
}
private fun copyJsFilesFromDirectory(dir: File, outputLibraryJsPath: String) {
traverseDirectory(dir) { content, relativePath ->
FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
}
}
private fun processDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
FileUtil.processFilesRecursively(dir, Processor<File> { file ->
val relativePath = FileUtil.getRelativePath(dir, file)
?: throw IllegalArgumentException("relativePath should not be null $dir $file")
if (file.isFile && relativePath.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val suggestedRelativePath = getSuggestedPath(relativePath) ?: return@Processor true
action(FileUtil.loadFile(file), suggestedRelativePath)
}
true
})
}
private fun traverseDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
try {
processDirectory(dir, action)
}
catch (ex: IOException) {
LOG.error("Could not read files from directory ${dir.name}: ${ex.message}")
}
}
private fun copyJsFilesFromZip(file: File, outputLibraryJsPath: String) {
traverseArchive(file) { content, relativePath ->
FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
}
}
private fun traverseArchive(file: File, action: (content: String, relativePath: String) -> Unit) {
val zipFile = ZipFile(file.path)
try {
val zipEntries = zipFile.entries()
while (zipEntries.hasMoreElements()) {
val entry = zipEntries.nextElement()
val entryName = entry.name
if (!entry.isDirectory && entryName.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val relativePath = getSuggestedPath(entryName) ?: continue
val stream = zipFile.getInputStream(entry)
val content = FileUtil.loadTextAndClose(stream)
action(content, relativePath)
}
}
}
catch (ex: IOException) {
LOG.error("Could not extract files from archive ${file.name}: ${ex.message}")
}
finally {
zipFile.close()
}
}
private fun getSuggestedPath(path: String): String? {
val systemIndependentPath = FileUtil.toSystemIndependentName(path)
if (systemIndependentPath.startsWith(LibraryUtils.META_INF)) {
if (systemIndependentPath.startsWith(META_INF_RESOURCES)) {
return path.substring(META_INF_RESOURCES.length)
}
return null
}
return path
}
}
@@ -30,6 +30,7 @@ object KotlinJavascriptMetadataUtils {
const val VFS_PROTOCOL: String = "kotlin-js-meta"
private val KOTLIN_JAVASCRIPT_METHOD_NAME = "kotlin_module_metadata"
private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toPattern()
/**
* Matches string like <name>.kotlin_module_metadata(<abi version>, <module name>, <base64 data>)
*/
@@ -44,28 +45,13 @@ object KotlinJavascriptMetadataUtils {
@JvmStatic fun hasMetadata(text: String): Boolean =
KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find() && METADATA_PATTERN.matcher(text).find()
fun hasMetadataWithIncompatibleAbiVersion(text: String): Boolean {
val matcher = METADATA_PATTERN.matcher(text)
while (matcher.find()) {
var abiVersion = matcher.group(1).toInt()
if (abiVersion != ABI_VERSION) return true
}
return false
}
fun formatMetadataAsString(moduleName: String, content: ByteArray): String =
"// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME($ABI_VERSION, \"$moduleName\", \"${printBase64Binary(content)}\");\n"
@JvmStatic fun loadMetadata(file: File): List<KotlinJavascriptMetadata> {
assert(file.exists()) { "Library " + file + " not found" }
assert(file.exists()) { "Library $file not found" }
val metadataList = arrayListOf<KotlinJavascriptMetadata>()
LibraryUtils.traverseJsLibrary(file) { content, relativePath ->
var path = file.path
if (relativePath.isNotBlank()) {
path += "/$relativePath"
}
JsLibraryUtils.traverseJsLibrary(file) { content, relativePath ->
parseMetadata(content, metadataList)
}
@@ -17,16 +17,12 @@
package org.jetbrains.kotlin.utils
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.Processor
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
import java.io.*
import java.util.Properties
import java.util.*
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.zip.ZipFile
object LibraryUtils {
private val LOG = Logger.getInstance(LibraryUtils::class.java)
@@ -34,9 +30,9 @@ object LibraryUtils {
val KOTLIN_JS_MODULE_NAME: String = "Kotlin-JS-Module-Name"
private var TITLE_KOTLIN_JAVASCRIPT_STDLIB: String
private var TITLE_KOTLIN_JAVASCRIPT_LIB: String
private val METAINF = "META-INF/"
private val MANIFEST_PATH = "${METAINF}MANIFEST.MF"
private val METAINF_RESOURCES = "${METAINF}resources/"
val META_INF = "META-INF/"
private val MANIFEST_PATH = "${META_INF}MANIFEST.MF"
private val KOTLIN_JS_MODULE_ATTRIBUTE_NAME = Attributes.Name(KOTLIN_JS_MODULE_NAME)
init {
@@ -73,10 +69,13 @@ object LibraryUtils {
}
@JvmStatic fun isOldKotlinJavascriptLibrary(library: File): Boolean =
checkAttributeValue(library, TITLE_KOTLIN_JAVASCRIPT_LIB, Attributes.Name.SPECIFICATION_TITLE) && getKotlinJsModuleName(library) != null
checkAttributeValue(library, TITLE_KOTLIN_JAVASCRIPT_LIB, Attributes.Name.SPECIFICATION_TITLE) &&
getKotlinJsModuleName(library) != null
@JvmStatic fun isKotlinJavascriptLibraryWithMetadata(library: File): Boolean = KotlinJavascriptMetadataUtils.loadMetadata(library).isNotEmpty()
@JvmStatic fun isKotlinJavascriptLibraryWithMetadata(library: File): Boolean =
KotlinJavascriptMetadataUtils.loadMetadata(library).isNotEmpty()
@Suppress("unused") // used in K2JSCompilerMojo
@JvmStatic fun isKotlinJavascriptLibrary(library: File): Boolean =
isOldKotlinJavascriptLibrary(library) || isKotlinJavascriptLibraryWithMetadata(library)
@@ -84,117 +83,6 @@ object LibraryUtils {
return checkAttributeValue(library, TITLE_KOTLIN_JAVASCRIPT_STDLIB, Attributes.Name.IMPLEMENTATION_TITLE)
}
@JvmStatic fun copyJsFilesFromLibraries(libraries: List<String>, outputLibraryJsPath: String) {
for (library in libraries) {
val file = File(library)
assert(file.exists()) { "Library " + library + " not found" }
if (file.isDirectory) {
copyJsFilesFromDirectory(file, outputLibraryJsPath)
}
else {
copyJsFilesFromZip(file, outputLibraryJsPath)
}
}
}
@JvmStatic fun traverseJsLibraries(libs: List<File>, action: (content: String, path: String)->Unit) {
libs.forEach { traverseJsLibrary(it, action) }
}
@JvmStatic fun traverseJsLibrary(lib: File, action: (content: String, path: String)->Unit) {
when {
lib.isDirectory -> traverseDirectory(lib, action)
FileUtil.isJarOrZip(lib) -> traverseArchive(lib, action)
lib.name.endsWith(KotlinJavascriptMetadataUtils.JS_EXT) -> {
lib.runIfFileExists(action)
val jsFile = lib.withReplacedExtensionOrNull(KotlinJavascriptMetadataUtils.META_JS_SUFFIX, KotlinJavascriptMetadataUtils.JS_EXT)
jsFile?.runIfFileExists(action)
}
else ->
throw IllegalArgumentException("Unknown library format (directory, zip or js file expected): $lib")
}
}
private fun File.runIfFileExists(action: (content: String, path: String)->Unit) {
if (isFile) {
action(FileUtil.loadFile(this), "")
}
}
private fun copyJsFilesFromDirectory(dir: File, outputLibraryJsPath: String) {
traverseDirectory(dir) {
content, relativePath -> FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
}
}
private fun processDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
FileUtil.processFilesRecursively(dir, object : Processor<File> {
override fun process(file: File): Boolean {
val relativePath = FileUtil.getRelativePath(dir, file) ?: throw IllegalArgumentException("relativePath should not be null " + dir + " " + file)
if (file.isFile && relativePath.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val suggestedRelativePath = getSuggestedPath(relativePath)
if (suggestedRelativePath == null) return true
action(FileUtil.loadFile(file), suggestedRelativePath)
}
return true
}
})
}
fun traverseDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
try {
processDirectory(dir, action)
}
catch (ex: IOException) {
LOG.error("Could not read files from directory ${dir.name}: ${ex.message}")
}
}
private fun copyJsFilesFromZip(file: File, outputLibraryJsPath: String) {
traverseArchive(file) { content, relativePath ->
FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
}
}
fun traverseArchive(file: File, action: (content: String, relativePath: String) -> Unit) {
val zipFile = ZipFile(file.path)
try {
val zipEntries = zipFile.entries()
while (zipEntries.hasMoreElements()) {
val entry = zipEntries.nextElement()
val entryName = entry.name
if (!entry.isDirectory && entryName.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val relativePath = getSuggestedPath(entryName)
if (relativePath == null) continue
val stream = zipFile.getInputStream(entry)
val content = FileUtil.loadTextAndClose(stream)
action(content, relativePath)
}
}
}
catch (ex: IOException) {
LOG.error("Could not extract files from archive ${file.name}: ${ex.message}")
}
finally {
zipFile.close()
}
}
private fun getSuggestedPath(path: String): String? {
val systemIndependentPath = FileUtil.toSystemIndependentName(path)
if (systemIndependentPath.startsWith(METAINF)) {
if (systemIndependentPath.startsWith(METAINF_RESOURCES)) {
return path.substring(METAINF_RESOURCES.length)
}
return null
}
return path
}
private fun getManifestFromJar(library: File): Manifest? {
if (!library.canRead()) return null
@@ -64,7 +64,7 @@ import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.CompilationCanceledException
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.LibraryUtils
import org.jetbrains.kotlin.utils.JsLibraryUtils
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.sure
@@ -592,7 +592,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val outputLibraryRuntimeDirectory = File(outputDir, compilerSettings.outputDirectoryForJsLibraryFiles).absolutePath
val libraryFilesToCopy = arrayListOf<String>()
JpsJsModuleUtils.getLibraryFiles(representativeTarget, libraryFilesToCopy)
LibraryUtils.copyJsFilesFromLibraries(libraryFilesToCopy, outputLibraryRuntimeDirectory)
JsLibraryUtils.copyJsFilesFromLibraries(libraryFilesToCopy, outputLibraryRuntimeDirectory)
}
}
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.js.inline
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.inlineStrategy
import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
@@ -33,8 +32,7 @@ import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getExternalModuleName
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import org.jetbrains.kotlin.utils.LibraryUtils
import org.jetbrains.kotlin.utils.JsLibraryUtils
import org.jetbrains.kotlin.utils.sure
import java.io.File
@@ -68,7 +66,7 @@ class FunctionReader(private val context: TranslationContext) {
val config = context.config as LibrarySourcesConfig
val libs = config.libraries.map { File(it) }
LibraryUtils.traverseJsLibraries(libs) { fileContent, path ->
JsLibraryUtils.traverseJsLibraries(libs) { fileContent, path ->
val matcher = DEFINE_MODULE_PATTERN.toPattern().matcher(fileContent)
while (matcher.find()) {