JS: fixed support for test source roots (KT-6627)

This commit is contained in:
Anton Bannykh
2017-02-01 15:38:51 +03:00
parent 97b6c3013a
commit 289a7a9cc3
25 changed files with 239 additions and 57 deletions
@@ -19,9 +19,7 @@ package org.jetbrains.kotlin.js.config;
import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project;
import com.intellij.util.SmartList;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
@@ -87,7 +85,12 @@ public abstract class JsConfig {
return configuration.get(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN);
}
public abstract boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report);
public static abstract class Reporter {
public void error(@NotNull String message) { /*Do nothing*/ }
public void warning(@NotNull String message) { /*Do nothing*/ }
}
public abstract boolean checkLibFilesAndReportErrors(@NotNull Reporter report);
protected abstract void init(@NotNull List<KtFile> sourceFilesInLibraries, @NotNull List<KotlinJavascriptMetadata> metadata);
@@ -25,7 +25,6 @@ import com.intellij.psi.PsiManager;
import com.intellij.util.PathUtil;
import com.intellij.util.io.URLUtil;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,7 +38,9 @@ import org.jetbrains.kotlin.utils.LibraryUtils;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.utils.LibraryUtils.isOldKotlinJavascriptLibrary;
import static org.jetbrains.kotlin.utils.PathUtil.getKotlinPathsForDistDirectory;
@@ -69,9 +70,9 @@ public class LibrarySourcesConfig extends JsConfig {
final PsiManager psiManager = PsiManager.getInstance(getProject());
Function1<String, Unit> report = new Function1<String, Unit>() {
JsConfig.Reporter report = new JsConfig.Reporter() {
@Override
public Unit invoke(String message) {
public void error(@NotNull String message) {
throw new IllegalStateException(message);
}
};
@@ -98,11 +99,11 @@ public class LibrarySourcesConfig extends JsConfig {
}
@Override
public boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report) {
public boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report) {
return checkLibFilesAndReportErrors(report, null);
}
private boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report, @Nullable Function2<String, VirtualFile, Unit> action) {
private boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report, @Nullable Function2<String, VirtualFile, Unit> action) {
List<String> libraries = getLibraries();
if (libraries.isEmpty()) {
return false;
@@ -111,12 +112,14 @@ public class LibrarySourcesConfig extends JsConfig {
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL);
VirtualFileSystem jarFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL);
Set<String> modules = new HashSet<String>();
for (String path : libraries) {
VirtualFile file;
File filePath = new File(path);
if (!filePath.exists()) {
report.invoke("Path '" + path + "' does not exist");
report.error("Path '" + path + "' does not exist");
return true;
}
@@ -128,7 +131,7 @@ public class LibrarySourcesConfig extends JsConfig {
}
if (file == null) {
report.invoke("File '" + path + "' does not exist or could not be read");
report.error("File '" + path + "' does not exist or could not be read");
return true;
}
@@ -136,21 +139,27 @@ public class LibrarySourcesConfig extends JsConfig {
if (isOldKotlinJavascriptLibrary(filePath)) {
moduleName = LibraryUtils.getKotlinJsModuleName(filePath);
if (!modules.add(moduleName)) {
report.warning("Module \"" + moduleName + "\" is defined in more, than one file");
}
}
else {
List<KotlinJavascriptMetadata> metadataList = KotlinJavascriptMetadataUtils.loadMetadata(filePath);
if (metadataList.isEmpty()) {
report.invoke("'" + path + "' is not a valid Kotlin Javascript library");
report.error("'" + path + "' is not a valid Kotlin Javascript library");
return true;
}
for (KotlinJavascriptMetadata metadata : metadataList) {
if (!metadata.getVersion().isCompatible()) {
report.invoke("File '" + path + "' was compiled with an incompatible version of Kotlin. " +
"The binary version of its metadata is " + metadata.getVersion() +
", expected version is " + JsMetadataVersion.INSTANCE);
report.error("File '" + path + "' was compiled with an incompatible version of Kotlin. " +
"The binary version of its metadata is " + metadata.getVersion() +
", expected version is " + JsMetadataVersion.INSTANCE);
return true;
}
if (!modules.add(metadata.getModuleName())) {
report.warning("Module \"" + metadata.getModuleName() + "\" is defined in more, than one file");
}
}
moduleName = null;
@@ -16,13 +16,13 @@
package org.jetbrains.kotlin.js.inline
import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import com.google.common.collect.HashMultimap
import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig
import org.jetbrains.kotlin.js.inline.util.IdentitySet
import org.jetbrains.kotlin.js.inline.util.isCallInvocation
@@ -51,22 +51,18 @@ private val DEFINE_MODULE_FIND_PATTERN = ".defineModule("
class FunctionReader(private val context: TranslationContext) {
/**
* Maps module name to .js file content, that contains this module definition.
* One file can contain more than one module definition.
* fileContent: .js file content, that contains this module definition.
* One file can contain more than one module definition.
*
* moduleVariable: the variable used to call functions inside module.
* The default variable is _, but it can be renamed by minifier.
*
* kotlinVariable: kotlin object variable.
* The default variable is Kotlin, but it can be renamed by minifier.
*/
private val moduleJsDefinition = hashMapOf<String, String>()
data class ModuleInfo(val fileContent: String, val moduleVariable: String, val kotlinVariable: String)
/**
* Maps module name to variable, that is used to call functions inside module.
* The default variable is _, but it can be renamed by minifier.
*/
private val moduleRootVariable = hashMapOf<String, String>()
/**
* Maps moduleName to kotlin object variable.
* The default variable is Kotlin, but it can be renamed by minifier.
*/
private val moduleKotlinVariable = hashMapOf<String, String>()
private val moduleNameToInfo = HashMultimap.create<String, ModuleInfo>()
init {
val config = context.config as LibrarySourcesConfig
@@ -87,10 +83,7 @@ class FunctionReader(private val context: TranslationContext) {
val moduleName = preciseMatcher.group(3)
val moduleVariable = preciseMatcher.group(4)
val kotlinVariable = preciseMatcher.group(1)
assert(moduleName !in moduleJsDefinition) { "Module \"$moduleName\" is defined in more, than one file" }
moduleJsDefinition[moduleName] = fileContent
moduleRootVariable[moduleName] = moduleVariable
moduleKotlinVariable[moduleName] = kotlinVariable
moduleNameToInfo.put(moduleName, ModuleInfo(fileContent, moduleVariable, kotlinVariable))
}
}
}
@@ -122,7 +115,7 @@ class FunctionReader(private val context: TranslationContext) {
operator fun contains(descriptor: CallableDescriptor): Boolean {
val moduleName = getExternalModuleName(descriptor)
val currentModuleName = context.config.moduleId
return currentModuleName != moduleName && moduleName != null && moduleName in moduleJsDefinition
return currentModuleName != moduleName && moduleName != null && moduleName in moduleNameToInfo.keys()
}
operator fun get(descriptor: CallableDescriptor): JsFunction = functionCache.get(descriptor)
@@ -131,13 +124,17 @@ class FunctionReader(private val context: TranslationContext) {
if (descriptor !in this) return null
val moduleName = getExternalModuleName(descriptor)
val file = moduleJsDefinition[moduleName].sure { "Module $moduleName file have not been read" }
val function = readFunctionFromSource(descriptor, file)
function?.markInlineArguments(descriptor)
return function
for (info in moduleNameToInfo[moduleName]) {
val function = readFunctionFromSource(descriptor, info)
if (function != null) return function
}
return null
}
private fun readFunctionFromSource(descriptor: CallableDescriptor, source: String): JsFunction? {
private fun readFunctionFromSource(descriptor: CallableDescriptor, info: ModuleInfo): JsFunction? {
val source = info.fileContent
val tag = Namer.getFunctionTag(descriptor)
val index = source.indexOf(tag)
if (index < 0) return null
@@ -149,12 +146,12 @@ class FunctionReader(private val context: TranslationContext) {
}
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram()))
val moduleName = getExternalModuleName(descriptor)!!
val moduleReference = context.getModuleExpressionFor(descriptor) ?: getRootPackage()
val replacements = hashMapOf(moduleRootVariable[moduleName]!! to moduleReference,
moduleKotlinVariable[moduleName]!! to Namer.kotlinObject())
val replacements = hashMapOf(info.moduleVariable to moduleReference,
info.kotlinVariable to Namer.kotlinObject())
replaceExternalNames(function, replacements)
function.markInlineArguments(descriptor)
return function
}