JS: support internal visibility from friend modules

Friend modules should be provided using the -Xfriend-modules flag
in the same format as -libraries. No manual configuration required for
JPS, Gradle and Maven plugins.

Friend modules could be switched off using the -Xfriend-modules-disabled
flag. Doing that will
  * prevent internal declarations from being exported,
  * values provided by -Xfriend-modules ignored,
  * raise a compilation error on attemps to use internal declarations from other modules

Fixes #KT-15135 and #KT-16568.
This commit is contained in:
Anton Bannykh
2017-03-30 15:03:38 +03:00
parent 7bbf9861d0
commit 2e9a59819a
48 changed files with 765 additions and 133 deletions
@@ -79,9 +79,20 @@ public class K2JSCompilerArguments extends CommonCompilerArguments {
// Advanced options
@GradleOption(DefaultValues.BooleanFalseDefault.class)
@Argument(value = "-Xtypedarrays", description = "Translate primitive arrays to JS typed arrays")
@Argument(value = "-Xtyped-arrays", description = "Translate primitive arrays to JS typed arrays")
public boolean typedArrays;
@GradleOption(DefaultValues.BooleanFalseDefault.class)
@Argument(value = "-Xfriend-modules-disabled", description = "Disable internal declaration export")
public boolean friendModulesDisabled;
@Argument(
value = "-Xfriend-modules",
valueDescription = "<path>",
description = "Paths to friend modules"
)
public String friendModules;
@NotNull
public static K2JSCompilerArguments createDefaultInstance() {
K2JSCompilerArguments arguments = new K2JSCompilerArguments();
@@ -274,11 +274,19 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
ContainerUtil.addAll(libraries, ArraysKt.filterNot(arguments.libraries.split(File.pathSeparator), String::isEmpty));
}
configuration.put(JSConfigurationKeys.LIBRARIES, libraries);
if (arguments.typedArrays) {
configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true);
}
configuration.put(JSConfigurationKeys.LIBRARIES, libraries);
configuration.put(JSConfigurationKeys.FRIEND_PATHS_DISABLED, arguments.friendModulesDisabled);
if (!arguments.friendModulesDisabled && arguments.friendModules != null) {
List<String> friendPaths = ArraysKt.filterNot(arguments.friendModules.split(File.pathSeparator), String::isEmpty);
configuration.put(JSConfigurationKeys.FRIEND_PATHS, friendPaths);
}
String moduleKindName = arguments.moduleKind;
ModuleKind moduleKind = moduleKindName != null ? moduleKindMap.get(moduleKindName) : ModuleKind.PLAIN;
+4 -2
View File
@@ -1,6 +1,8 @@
Usage: kotlinc-js <options> <source files>
where advanced options include:
-Xtypedarrays Translate primitive arrays to JS typed arrays
-Xtyped-arrays Translate primitive arrays to JS typed arrays
-Xfriend-modules-disabled Disable internal declaration export
-Xfriend-modules=<path> Paths to friend modules
-Xno-inline Disable method inlining
-Xrepeat=<count> Repeat compilation (for performance analysis)
-Xskip-metadata-version-check Load classes with bad metadata version anyway (incl. pre-release classes)
@@ -14,4 +16,4 @@ where advanced options include:
Enable coroutines or report warnings or errors on declarations and use sites of 'suspend' modifier
Advanced options are non-standard and may be changed or removed without any notice.
OK
OK
+37
View File
@@ -0,0 +1,37 @@
// MODULE: lib
// FILE: lib.kt
package lib
internal fun foo() = 1
internal val bar = 2
internal class A {
internal fun baz(a: Int): Int {
return a * 10
}
internal val foo = 3
internal inner class B {
internal fun foo() = 4
}
}
// MODULE: main(lib)(lib)
// FILE: main.kt
package main
import lib.*
fun box(): String {
if (foo() != 1) return "fail 1: ${foo()}"
if (bar != 2) return "fail 2: ${bar}"
val a = A()
if (a.baz(10) != 100) return "fail 3: ${a.baz(10)}"
if (a.foo != 3) return "fail 4: ${a.foo}"
if (a.B().foo() != 4) return "fail 5: ${a.B().foo()}"
return "OK"
}
@@ -46,10 +46,12 @@ public abstract class KotlinMultiFileTestWithJava<M, F> extends KotlinTestWithEn
public class ModuleAndDependencies {
final M module;
final List<String> dependencies;
final List<String> friends;
ModuleAndDependencies(M module, List<String> dependencies) {
ModuleAndDependencies(M module, List<String> dependencies, List<String> friends) {
this.module = module;
this.dependencies = dependencies;
this.friends = friends;
}
}
@@ -129,9 +131,9 @@ public abstract class KotlinMultiFileTestWithJava<M, F> extends KotlinTestWithEn
}
@Override
public M createModule(@NotNull String name, @NotNull List<String> dependencies) {
public M createModule(@NotNull String name, @NotNull List<String> dependencies, @NotNull List<String> friends) {
M module = createTestModule(name);
ModuleAndDependencies oldValue = modules.put(name, new ModuleAndDependencies(module, dependencies));
ModuleAndDependencies oldValue = modules.put(name, new ModuleAndDependencies(module, dependencies, friends));
assert oldValue == null : "Module " + name + " declared more than once";
return module;
@@ -118,7 +118,7 @@ public class KotlinTestUtils {
private static final String MODULE_DELIMITER = ",\\s*";
private static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile(
"(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*)?" +
"(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*)?" +
"//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE);
private static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!([\\w_]+)(:\\s*(.*)$)?", Pattern.MULTILINE);
@@ -610,7 +610,7 @@ public class KotlinTestUtils {
public interface TestFileFactory<M, F> {
F createFile(@Nullable M module, @NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
M createModule(@NotNull String name, @NotNull List<String> dependencies);
M createModule(@NotNull String name, @NotNull List<String> dependencies, @NotNull List<String> friends);
}
public static abstract class TestFileFactoryNoModules<F> implements TestFileFactory<Void, F> {
@@ -628,7 +628,7 @@ public class KotlinTestUtils {
public abstract F create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
@Override
public Void createModule(@NotNull String name, @NotNull List<String> dependencies) {
public Void createModule(@NotNull String name, @NotNull List<String> dependencies, @NotNull List<String> friends) {
return null;
}
}
@@ -651,12 +651,13 @@ public class KotlinTestUtils {
while (true) {
String moduleName = matcher.group(1);
String moduleDependencies = matcher.group(2);
String moduleFriends = matcher.group(3);
if (moduleName != null) {
hasModules = true;
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
module = factory.createModule(moduleName, parseModuleList(moduleDependencies), parseModuleList(moduleFriends));
}
String fileName = matcher.group(3);
String fileName = matcher.group(4);
int start = processedChars;
boolean nextFileExists = matcher.find();
@@ -681,7 +682,7 @@ public class KotlinTestUtils {
}
if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) {
M supportModule = hasModules ? factory.createModule("support", Collections.emptyList()) : null;
M supportModule = hasModules ? factory.createModule("support", Collections.emptyList(), Collections.emptyList()) : null;
testFiles.add(factory.createFile(supportModule,
"CoroutineUtil.kt",
"import kotlin.coroutines.experimental.*\n" +
@@ -715,7 +716,7 @@ public class KotlinTestUtils {
return testFiles;
}
private static List<String> parseDependencies(@Nullable String dependencies) {
private static List<String> parseModuleList(@Nullable String dependencies) {
if (dependencies == null) return Collections.emptyList();
return StringsKt.split(dependencies, Pattern.compile(MODULE_DELIMITER), 0);
}
@@ -10535,6 +10535,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("internal.kt")
public void testInternal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt");
doTest(fileName);
}
@TestMetadata("internalOverride.kt")
public void testInternalOverride() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt");
@@ -10535,6 +10535,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("internal.kt")
public void testInternal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt");
doTest(fileName);
}
@TestMetadata("internalOverride.kt")
public void testInternalOverride() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt");
@@ -10535,6 +10535,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("internal.kt")
public void testInternal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt");
doTest(fileName);
}
@TestMetadata("internalOverride.kt")
public void testInternalOverride() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt");
@@ -97,6 +97,10 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
setDependencies(ModuleDependenciesImpl(descriptors, emptySet()))
}
fun setDependencies(descriptors: List<ModuleDescriptorImpl>, friends: Set<ModuleDescriptorImpl>) {
setDependencies(ModuleDependenciesImpl(descriptors, friends))
}
override fun shouldSeeInternalsOf(targetModule: ModuleDescriptor): Boolean {
return this == targetModule || targetModule in dependencies!!.modulesWhoseInternalsAreVisible
}
@@ -373,6 +373,11 @@ class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
makeAll().assertSuccessful()
}
fun testKotlinJavaScriptInternalFromSpecialRelatedModule() {
initProject(JS_STDLIB)
makeAll().assertSuccessful()
}
fun testKotlinJavaScriptProjectWithTests() {
initProject(JS_STDLIB)
makeAll().assertSuccessful()
@@ -82,6 +82,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
environment: JpsCompilerEnvironment,
sourceFiles: Collection<File>,
libraries: List<String>,
friendModules: List<String>,
outputFile: File
) {
log.debug("K2JS: common arguments: " + ArgumentUtils.convertArgumentsToStringList(commonArguments))
@@ -90,7 +91,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
val arguments = mergeBeans(commonArguments, XmlSerializerUtil.createCopy(k2jsArguments))
log.debug("K2JS: merged arguments: " + ArgumentUtils.convertArgumentsToStringList(arguments))
setupK2JsArguments(outputFile, sourceFiles, libraries, arguments)
setupK2JsArguments(outputFile, sourceFiles, libraries, friendModules, arguments)
log.debug("K2JS: arguments after setup" + ArgumentUtils.convertArgumentsToStringList(arguments))
withCompilerSettings(compilerSettings) {
@@ -203,13 +204,14 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
}
}
private fun setupK2JsArguments(_outputFile: File, sourceFiles: Collection<File>, _libraries: List<String>, settings: K2JSCompilerArguments) {
private fun setupK2JsArguments(_outputFile: File, sourceFiles: Collection<File>, _libraries: List<String>, _friendModules: List<String>, settings: K2JSCompilerArguments) {
with(settings) {
noStdlib = true
freeArgs = sourceFiles.map { it.path }
outputFile = _outputFile.path
metaInfo = true
libraries = _libraries.joinToString(File.pathSeparator)
friendModules = _friendModules.joinToString(File.pathSeparator)
}
}
@@ -51,18 +51,16 @@ object JpsJsModuleUtils {
if (module.moduleType != JpsJavaModuleType.INSTANCE) return
if ((module != target.module || target.isTests) && module.sourceRoots.any { it.rootType == JavaSourceRootType.SOURCE}) {
addTarget(module, JavaModuleBuildTargetType.PRODUCTION)
addTarget(module, isTests = false)
}
if (module != target.module && target.isTests && module.sourceRoots.any { it.rootType == JavaSourceRootType.TEST_SOURCE}) {
addTarget(module, JavaModuleBuildTargetType.TEST)
addTarget(module, isTests = true)
}
}
fun addTarget(module: JpsModule, targetType: JavaModuleBuildTargetType) {
val moduleBuildTarget = ModuleBuildTarget(module, targetType)
val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(moduleBuildTarget)
val metaInfoFile = getOutputMetaFile(outputDir, module.name, targetType.isTests)
fun addTarget(module: JpsModule, isTests: Boolean) {
val metaInfoFile = getOutputMetaFile(module, isTests)
if (metaInfoFile.exists()) {
result.add(metaInfoFile.absolutePath)
}
@@ -70,6 +68,13 @@ object JpsJsModuleUtils {
})
}
@JvmStatic
fun getOutputMetaFile(module: JpsModule, isTests: Boolean): File {
val moduleBuildTarget = ModuleBuildTarget(module, if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION)
val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(moduleBuildTarget)
return getOutputMetaFile(outputDir, module.name, isTests)
}
@JvmStatic
fun getOutputFile(outputDir: File, moduleName: String, isTests: Boolean)
= File(outputDir, moduleName + suffix(isTests) + KotlinJavascriptMetadataUtils.JS_EXT)
@@ -57,6 +57,7 @@ import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.build.JpsJsModuleUtils.getOutputMetaFile
import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
@@ -661,8 +662,13 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeModule)
val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeModule)
val friendPaths = KotlinBuilderModuleScriptGenerator.getProductionModulesWhichInternalsAreVisible(representativeTarget).mapNotNull {
val file = getOutputMetaFile(it, false)
if (file.exists()) file.absolutePath.toString() else null
}
val compilerRunner = JpsKotlinCompilerRunner()
compilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, environment, sourceFiles, libraries, outputFile)
compilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, environment, sourceFiles, libraries, friendPaths, outputFile)
return environment.outputItemsCollector
}
@@ -132,20 +132,21 @@ object KotlinBuilderModuleScriptGenerator {
fun getOutputDirSafe(target: ModuleBuildTarget): File =
target.outputDir ?: throw ProjectBuildException("No output directory found for " + target)
private fun getAdditionalOutputDirsWhereInternalsAreVisible(target: ModuleBuildTarget): List<File> {
if (!target.isTests) return emptyList()
fun getProductionModulesWhichInternalsAreVisible(from: ModuleBuildTarget): List<JpsModule> {
if (!from.isTests) return emptyList()
val result = SmartList<File>()
result.addIfNotNull(JpsJavaExtensionService.getInstance().getOutputDirectory(target.module, false))
getRelatedProductionModule(target.module)?.let {
result.addIfNotNull(JpsJavaExtensionService.getInstance().getOutputDirectory(it, false))
}
val result = SmartList<JpsModule>(from.module)
result.addIfNotNull(getRelatedProductionModule(from.module))
return result
}
fun getAdditionalOutputDirsWhereInternalsAreVisible(target: ModuleBuildTarget): List<File> {
return getProductionModulesWhichInternalsAreVisible(target).mapNotNullTo(SmartList<File>()) {
JpsJavaExtensionService.getInstance().getOutputDirectory(it, false)
}
}
private fun findClassPathRoots(target: ModuleBuildTarget): Collection<File> {
return getAllDependencies(target).classes().roots.filter { file ->
if (!file.exists()) {
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
<component name="CopyrightManager" default="">
<module2copyright />
</component>
<component name="DependencyValidationManager">
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</component>
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/module1/module1.iml" filepath="$PROJECT_DIR$/module1/module1.iml" />
<module fileurl="file://$PROJECT_DIR$/module2/module2.iml" filepath="$PROJECT_DIR$/module2/module2.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA_JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="IDEA_JDK" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinRuntime" level="project" />
</component>
</module>
@@ -0,0 +1,27 @@
package test1
@Target(AnnotationTarget.FILE)
@Retention(AnnotationRetention.SOURCE)
internal annotation class InternalFileAnnotation1()
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
internal annotation class InternalClassAnnotation1()
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
internal annotation class InternalFunctionAnnotation1()
internal open class InternalClass1
abstract class ClassA1(internal val member: Int)
abstract class ClassB1 {
internal abstract val member: Int
internal fun func() = 1
}
internal val internalProp = 1
internal fun internalFun() {}
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="jdk" jdkName="IDEA_JDK" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="module1" />
<orderEntry type="library" name="KotlinRuntime" level="project" />
</component>
<component name="TestModuleProperties" production-module="module1" />
</module>
@@ -0,0 +1,33 @@
@file:InternalFileAnnotation1
package test2
import test1.*
internal class FromInternalClass1: InternalClass1()
@InternalClassAnnotation1
class FromClassA1 : ClassA1(10) {
@InternalClassAnnotation1
class Nested {
@InternalFunctionAnnotation1
fun foo() {}
}
}
class FromClassB1 : ClassB1() {
internal override val member = 10
}
@InternalFunctionAnnotation1
fun foo() {}
fun box() {
internalProp
internalFun()
InternalClass1()
FromClassA1().member
FromClassB1().member
FromClassB1().func()
}
@@ -1,2 +1,6 @@
fun main() {
}
}
internal fun internalFun() {}
internal val internalVal = 10
@@ -1,3 +1,5 @@
fun testMain() {
main()
}
internalFun()
var a = internalVal
}
@@ -39,10 +39,11 @@ object TopDownAnalyzerFacadeForJS {
val context = ContextForNewModule(
ProjectContext(config.project), Name.special("<${config.moduleId}>"), JsPlatform.builtIns, null
)
context.setDependencies(
context.module.setDependencies(
listOf(context.module) +
config.moduleDescriptors.map { it.data } +
listOf(JsPlatform.builtIns.builtInsModule)
listOf(JsPlatform.builtIns.builtInsModule),
config.friendModuleDescriptors.map { it.data }.toSet()
)
val trace = BindingTraceContext()
trace.record(MODULE_KIND, context.module, config.moduleKind)
@@ -45,4 +45,10 @@ public class JSConfigurationKeys {
CompilerConfigurationKey.create("fallback metadata");
public static final CompilerConfigurationKey<Boolean> SERIALIZE_FRAGMENTS = CompilerConfigurationKey.create("serialize fragments");
public static final CompilerConfigurationKey<Boolean> FRIEND_PATHS_DISABLED =
CompilerConfigurationKey.create("disable support for friend paths");
public static final CompilerConfigurationKey<List<String>> FRIEND_PATHS =
CompilerConfigurationKey.create("friend module paths");
}
@@ -26,7 +26,7 @@ import com.intellij.util.SmartList;
import com.intellij.util.io.URLUtil;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.config.*;
@@ -63,10 +63,14 @@ public class JsConfig {
private final LockBasedStorageManager storageManager = new LockBasedStorageManager();
private final List<KotlinJavascriptMetadata> metadata = new SmartList<>();
private final List<KotlinJavascriptMetadata> friends = new SmartList<>();
@Nullable
private List<JsModuleDescriptor<ModuleDescriptorImpl>> moduleDescriptors = null;
@Nullable
private List<JsModuleDescriptor<ModuleDescriptorImpl>> friendModuleDescriptors = null;
private boolean initialized = false;
public JsConfig(@NotNull Project project, @NotNull CompilerConfiguration configuration) {
@@ -99,6 +103,13 @@ public class JsConfig {
return getConfiguration().getList(JSConfigurationKeys.LIBRARIES);
}
@NotNull
public List<String> getFriends() {
if (getConfiguration().getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return Collections.emptyList();
return getConfiguration().getList(JSConfigurationKeys.FRIEND_PATHS);
}
public static abstract class Reporter {
public void error(@NotNull String message) { /*Do nothing*/ }
@@ -109,8 +120,15 @@ public class JsConfig {
return checkLibFilesAndReportErrors(report, null);
}
private boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report, @Nullable Function1<VirtualFile, Unit> action) {
List<String> libraries = getLibraries();
private boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report, @Nullable Function2<VirtualFile, String, Unit> action) {
return checkLibFilesAndReportErrors(getLibraries(), report, action);
}
private boolean checkLibFilesAndReportErrors(
@NotNull Collection<String> libraries,
@NotNull JsConfig.Reporter report,
@Nullable Function2<VirtualFile, String, Unit> action
) {
if (libraries.isEmpty()) {
return false;
}
@@ -163,7 +181,7 @@ public class JsConfig {
}
if (action != null) {
action.invoke(file);
action.invoke(file, path);
}
}
@@ -193,49 +211,85 @@ public class JsConfig {
return moduleDescriptors;
}
@NotNull
public List<JsModuleDescriptor<ModuleDescriptorImpl>> getFriendModuleDescriptors() {
init();
if (friendModuleDescriptors != null) return friendModuleDescriptors;
friendModuleDescriptors = new SmartList<>();
for (KotlinJavascriptMetadata metadataEntry : friends) {
JsModuleDescriptor<ModuleDescriptorImpl> descriptor = createModuleDescriptor(metadataEntry);
friendModuleDescriptors.add(descriptor);
}
friendModuleDescriptors = Collections.unmodifiableList(friendModuleDescriptors);
return friendModuleDescriptors;
}
private void init() {
if (initialized) return;
if (!getLibraries().isEmpty()) {
Function1<VirtualFile, Unit> action = file -> {
String libraryPath = PathUtil.getLocalPath(file);
assert libraryPath != null : "libraryPath for " + file + " should not be null";
metadata.addAll(KotlinJavascriptMetadataUtils.loadMetadata(libraryPath));
return Unit.INSTANCE;
};
boolean hasErrors = checkLibFilesAndReportErrors(new Reporter() {
JsConfig.Reporter reporter = new Reporter() {
@Override
public void error(@NotNull String message) {
throw new IllegalStateException(message);
}
}, action);
};
boolean hasErrors = checkLibFilesAndReportErrors(getFriends(), reporter, (file, path) -> {
List<KotlinJavascriptMetadata> metaList = loadMetadata(file, "friendPath");
metadata.addAll(metaList);
friends.addAll(metaList);
return Unit.INSTANCE;
});
hasErrors |= checkLibFilesAndReportErrors(CollectionsKt.subtract(getLibraries(), getFriends()), reporter, (file, path) -> {
metadata.addAll(loadMetadata(file, "libraryPath"));
return Unit.INSTANCE;
});
assert !hasErrors : "hasErrors should be false";
}
initialized = true;
}
@NotNull
private static List<KotlinJavascriptMetadata> loadMetadata(@NotNull VirtualFile file, @NotNull String name) {
String libraryPath = PathUtil.getLocalPath(file);
assert libraryPath != null : name + " for " + file + " should not be null";
return KotlinJavascriptMetadataUtils.loadMetadata(libraryPath);
}
private final IdentityHashMap<KotlinJavascriptMetadata, JsModuleDescriptor<ModuleDescriptorImpl>> factoryMap = new IdentityHashMap<>();
private JsModuleDescriptor<ModuleDescriptorImpl> createModuleDescriptor(KotlinJavascriptMetadata metadata) {
LanguageVersionSettings languageVersionSettings = CommonConfigurationKeysKt.getLanguageVersionSettings(configuration);
assert metadata.getVersion().isCompatible() ||
languageVersionSettings.isFlagEnabled(AnalysisFlags.getSkipMetadataVersionCheck()) :
"Expected JS metadata version " + JsMetadataVersion.INSTANCE + ", but actual metadata version is " + metadata.getVersion();
return factoryMap.computeIfAbsent(metadata, m -> {
LanguageVersionSettings languageVersionSettings = CommonConfigurationKeysKt.getLanguageVersionSettings(configuration);
assert m.getVersion().isCompatible() ||
languageVersionSettings.isFlagEnabled(AnalysisFlags.getSkipMetadataVersionCheck()) :
"Expected JS metadata version " + JsMetadataVersion.INSTANCE + ", but actual metadata version is " + m.getVersion();
ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl(
Name.special("<" + metadata.getModuleName() + ">"), storageManager, JsPlatform.INSTANCE.getBuiltIns()
);
ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl(
Name.special("<" + m.getModuleName() + ">"), storageManager, JsPlatform.INSTANCE.getBuiltIns()
);
JsModuleDescriptor<PackageFragmentProvider> rawDescriptor = KotlinJavascriptSerializationUtil.readModule(
metadata.getBody(), storageManager, moduleDescriptor,
new CompilerDeserializationConfiguration(languageVersionSettings)
);
JsModuleDescriptor<PackageFragmentProvider> rawDescriptor = KotlinJavascriptSerializationUtil.readModule(
m.getBody(), storageManager, moduleDescriptor,
new CompilerDeserializationConfiguration(languageVersionSettings)
);
PackageFragmentProvider provider = rawDescriptor.getData();
moduleDescriptor.initialize(provider != null ? provider : PackageFragmentProvider.Empty.INSTANCE);
PackageFragmentProvider provider = rawDescriptor.getData();
moduleDescriptor.initialize(provider != null ? provider : PackageFragmentProvider.Empty.INSTANCE);
return rawDescriptor.copy(moduleDescriptor);
return rawDescriptor.copy(moduleDescriptor);
});
}
private static void setDependencies(ModuleDescriptorImpl module, List<ModuleDescriptorImpl> modules) {
@@ -273,38 +273,40 @@ class NameSuggestion {
}
fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, encodeSignature(descriptor)), true)
fun mangledInternal() = NameAndStability(getInternalMangledName(baseName, encodeSignature(descriptor)), true)
fun mangledPrivate() = NameAndStability(getPrivateMangledName(baseName, descriptor), false)
val effectiveVisibility = descriptor.ownEffectiveVisibility
val containingDeclaration = descriptor.containingDeclaration
return when (containingDeclaration) {
is PackageFragmentDescriptor -> if (effectiveVisibility.isPublicAPI) mangledAndStable() else regularAndUnstable()
is ClassDescriptor -> {
is PackageFragmentDescriptor -> when {
effectiveVisibility.isPublicAPI -> mangledAndStable()
effectiveVisibility == Visibilities.INTERNAL -> mangledInternal()
else -> regularAndUnstable()
}
is ClassDescriptor -> when {
// valueOf() is created in the library with a mangled name for every enum class
if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return mangledAndStable()
descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod() -> mangledAndStable()
// Make all public declarations stable
if (effectiveVisibility == Visibilities.PUBLIC) {
return mangledAndStable()
}
effectiveVisibility == Visibilities.PUBLIC -> mangledAndStable()
if (descriptor is CallableMemberDescriptor && descriptor.isOverridableOrOverrides) return mangledAndStable()
descriptor is CallableMemberDescriptor && descriptor.isOverridableOrOverrides -> mangledAndStable()
// Make all protected declarations of non-final public classes stable
if (effectiveVisibility == Visibilities.PROTECTED &&
effectiveVisibility == Visibilities.PROTECTED &&
!containingDeclaration.isFinalClass &&
containingDeclaration.visibility.isPublicAPI
) {
return mangledAndStable()
}
containingDeclaration.visibility.isPublicAPI -> mangledAndStable()
effectiveVisibility == Visibilities.INTERNAL -> mangledInternal()
// Mangle (but make unstable) all non-public API of public classes
if (containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass) {
return mangledPrivate()
}
containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass -> mangledPrivate()
regularAndUnstable()
else -> regularAndUnstable()
}
else -> {
assert(containingDeclaration is CallableMemberDescriptor) {
@@ -323,6 +325,11 @@ class NameSuggestion {
return getStableMangledName(baseName, ownerName + ":" + encodeSignature(descriptor))
}
fun getInternalMangledName(suggestedName: String, forCalculateId: String): String {
val suffix = "_${mangledId("internal:" + forCalculateId)}\$"
return suggestedName + suffix
}
@JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String {
val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$"
return suggestedName + suffix
@@ -96,9 +96,10 @@ abstract class BasicBoxTest(
val generatedJsFiles = orderedModules.asReversed().mapNotNull { module ->
val dependencies = module.dependencies.mapNotNull { modules[it]?.outputFileName(outputDir) + ".meta.js" }
val friends = module.friends.mapNotNull { modules[it]?.outputFileName(outputDir) + ".meta.js" }
val outputFileName = module.outputFileName(outputDir) + ".js"
generateJavaScriptFile(file.parent, module, outputFileName, dependencies, modules.size > 1,
generateJavaScriptFile(file.parent, module, outputFileName, dependencies, friends, modules.size > 1,
outputPrefixFile, outputPostfixFile, mainCallParameters)
if (!module.name.endsWith(OLD_MODULE_SUFFIX)) outputFileName else null
@@ -222,6 +223,7 @@ abstract class BasicBoxTest(
module: TestModule,
outputFileName: String,
dependencies: List<String>,
friends: List<String>,
multiModule: Boolean,
outputPrefixFile: File?,
outputPostfixFile: File?,
@@ -239,7 +241,7 @@ abstract class BasicBoxTest(
val additionalFiles = globalCommonFiles + localCommonFiles + additionalCommonFiles
val psiFiles = createPsiFiles(testFiles + additionalFiles)
val config = createConfig(module, dependencies, multiModule, additionalMetadata = null)
val config = createConfig(module, dependencies, friends, multiModule, additionalMetadata = null)
val outputFile = File(outputFileName)
translateFiles(psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters)
@@ -263,7 +265,7 @@ abstract class BasicBoxTest(
}
val headerFile = File(incrementalDir, HEADER_FILE)
val recompiledConfig = createConfig(module, dependencies, multiModule, Pair(headerFile,serializedMetadata))
val recompiledConfig = createConfig(module, dependencies, friends, multiModule, Pair(headerFile,serializedMetadata))
val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
translateFiles(allTranslationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
@@ -357,7 +359,7 @@ abstract class BasicBoxTest(
private fun createPsiFiles(fileNames: List<String>): List<KtFile> = fileNames.map(this::createPsiFile)
private fun createConfig(
module: TestModule, dependencies: List<String>, multiModule: Boolean, additionalMetadata: Pair<File, List<File>>?
module: TestModule, dependencies: List<String>, friends: List<String>, multiModule: Boolean, additionalMetadata: Pair<File, List<File>>?
): JsConfig {
val configuration = environment.configuration.copy()
@@ -368,6 +370,7 @@ abstract class BasicBoxTest(
}
configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST + dependencies)
configuration.put(JSConfigurationKeys.FRIEND_PATHS, friends)
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name.removeSuffix(OLD_MODULE_SUFFIX))
configuration.put(JSConfigurationKeys.MODULE_KIND, module.moduleKind)
@@ -397,7 +400,7 @@ abstract class BasicBoxTest(
private inner class TestFileFactoryImpl : TestFileFactory<TestModule, TestFile>, Closeable {
var testPackage: String? = null
val tmpDir = KotlinTestUtils.tmpDir("js-tests")
val defaultModule = TestModule(TEST_MODULE, emptyList())
val defaultModule = TestModule(TEST_MODULE, emptyList(), emptyList())
override fun createFile(module: TestModule?, fileName: String, text: String, directives: Map<String, String>): TestFile? {
val currentModule = module ?: defaultModule
@@ -433,8 +436,8 @@ abstract class BasicBoxTest(
return TestFile(temporaryFile.absolutePath, currentModule, recompile = RECOMPILE_PATTERN.matcher(text).find())
}
override fun createModule(name: String, dependencies: List<String>): TestModule? {
return TestModule(name, dependencies)
override fun createModule(name: String, dependencies: List<String>, friends: List<String>): TestModule? {
return TestModule(name, dependencies, friends)
}
override fun close() {
@@ -450,9 +453,11 @@ abstract class BasicBoxTest(
private class TestModule(
val name: String,
dependencies: List<String>
dependencies: List<String>,
friends: List<String>
) {
val dependencies = dependencies.toMutableList()
val friends = friends.toMutableList()
var moduleKind = ModuleKind.PLAIN
var inliningDisabled = false
val files = mutableListOf<TestFile>()
@@ -12072,6 +12072,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("internal.kt")
public void testInternal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt");
doTest(fileName);
}
@TestMetadata("internalOverride.kt")
public void testInternalOverride() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt");
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage
import org.jetbrains.kotlin.js.backend.ast.metadata.exportedTag
import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject
@@ -151,7 +152,14 @@ internal class DeclarationExporter(val context: StaticContext) {
private fun JsExpression.exportStatement(declaration: DeclarationDescriptor) = JsExpressionStatement(this).also {
it.exportedTag = context.getTag(declaration)
}
private fun EffectiveVisibility.publicOrInternal(): Boolean {
if (publicApi) return true
if (context.config.configuration.getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return false
return toVisibility() == Visibilities.INTERNAL
}
private fun MemberDescriptor.shouldBeExported(force: Boolean) =
force || effectiveVisibility(checkPublishedApi = true).publicOrInternal() || AnnotationsUtils.getJsNameAnnotation(this) != null
}
private fun MemberDescriptor.shouldBeExported(force: Boolean) =
force || effectiveVisibility(checkPublishedApi = true).publicApi || AnnotationsUtils.getJsNameAnnotation(this) != null
@@ -214,6 +214,7 @@ val SIMPLE = "baz"
val SIMPLE0 = "${SIMPLE}_0"
val NATIVE = SIMPLE
val STABLE = "baz_za3lpa$"
val INTERNAL = "baz_kcn2v3$"
fun box(): String {
testGroup = "Top Level"
@@ -225,7 +226,7 @@ fun box(): String {
testGroup = "Public Class"
test(STABLE) { PublicClass().public_baz(0) }
test(NATIVE) { PublicClass().public_baz("native") }
test(SIMPLE0) { PublicClass().internal_baz(0) }
test(INTERNAL) { PublicClass().internal_baz(0) }
test(NATIVE) { PublicClass().internal_baz("native") }
test(SIMPLE0, PublicClass().call_private_baz)
test(NATIVE, PublicClass().call_private_native_baz)
@@ -233,7 +234,7 @@ fun box(): String {
testGroup = "Internal Class"
test(STABLE) { InternalClass().public_baz(0) }
test(NATIVE) { InternalClass().public_baz("native") }
test(SIMPLE0) { InternalClass().internal_baz(0) }
test(INTERNAL) { InternalClass().internal_baz(0) }
test(NATIVE) { InternalClass().internal_baz("native") }
test(SIMPLE0, InternalClass().call_private_baz)
test(NATIVE, InternalClass().call_private_native_baz)
@@ -241,7 +242,7 @@ fun box(): String {
testGroup = "Private Class"
test(STABLE) { PrivateClass().public_baz(0) }
test(NATIVE) { PrivateClass().public_baz("native") }
test(SIMPLE0) { PrivateClass().internal_baz(0) }
test(INTERNAL) { PrivateClass().internal_baz(0) }
test(NATIVE) { PrivateClass().internal_baz("native") }
test(SIMPLE0, PrivateClass().call_private_baz)
test(NATIVE, PrivateClass().call_private_native_baz)
@@ -249,7 +250,7 @@ fun box(): String {
testGroup = "Open Public Class"
test(STABLE) { OpenPublicClass().public_baz(0) }
test(NATIVE) { OpenPublicClass().public_baz("native") }
testMangledPrivate { OpenPublicClass().internal_baz(0) }
test(INTERNAL) { OpenPublicClass().internal_baz(0) }
test(NATIVE) { OpenPublicClass().internal_baz("native") }
testMangledPrivate(OpenPublicClass().call_private_baz)
test(NATIVE, OpenPublicClass().call_private_native_baz)
@@ -257,7 +258,7 @@ fun box(): String {
testGroup = "Open Internal Class"
test(STABLE) { OpenInternalClass().public_baz(0) }
test(NATIVE) { OpenInternalClass().public_baz("native") }
test(SIMPLE0) { OpenInternalClass().internal_baz(0) }
test(INTERNAL) { OpenInternalClass().internal_baz(0) }
test(NATIVE) { OpenInternalClass().internal_baz("native") }
test(SIMPLE0, OpenInternalClass().call_private_baz)
test(NATIVE, OpenInternalClass().call_private_native_baz)
@@ -265,7 +266,7 @@ fun box(): String {
testGroup = "Open Private Class"
test(STABLE) { OpenPrivateClass().public_baz(0) }
test(NATIVE) { OpenPrivateClass().public_baz("native") }
test(SIMPLE0) { OpenPrivateClass().internal_baz(0) }
test(INTERNAL) { OpenPrivateClass().internal_baz(0) }
test(NATIVE) { OpenPrivateClass().internal_baz("native") }
test(SIMPLE0, OpenPrivateClass().call_private_baz)
test(NATIVE, OpenPrivateClass().call_private_native_baz)
@@ -14,9 +14,11 @@ fun test2(`p 1`: Int, `.p 2`: Int) = `+`(`p 1`, `.p 2`)
fun test3(): String {
val `#` = "K"
class ` `(private val `::`: String) {
internal fun `@`() = `::` + `#`
private fun `@`() = `::` + `#`
operator fun invoke() = `@`()
}
return ` `("O").`@`()
return ` `("O")()
}
fun test4(): String {
@@ -124,6 +124,15 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
}
}
@Test
fun testCompilerTestAccessInternalProduction() {
val project = Project("kotlin2JsInternalTest", "2.10")
project.build("runRhino") {
assertSuccessful()
}
}
@Test
fun testJsCustomSourceSet() {
val project = Project("kotlin2JsProjectWithCustomSourceset", "2.10")
@@ -0,0 +1,50 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin2js'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
compile "org.mozilla:rhino:1.7.7.1"
}
task runRhino(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
workingDir = "${buildDir}/classes/"
main = 'org.mozilla.javascript.tools.shell.Main'
args = ["-opt", "-1", "-f", "kotlin.js", "-f", "main/kotlin2JsInternalTest_main.js", "-f", "test/kotlin2JsInternalTest_test.js", "-f", "check.js"]
}
build.doLast {
configurations.compile.each { File file ->
copy {
includeEmptyDirs = false
from zipTree(file.absolutePath)
into "${buildDir}/classes/"
include { fileTreeElement ->
def path = fileTreeElement.path
path.endsWith(".js") && (path.startsWith("META-INF/resources/") || !path.startsWith("META-INF/"))
}
}
}
copy {
from "."
include "check.js"
into "${buildDir}/classes/"
}
}
runRhino.dependsOn build
@@ -0,0 +1,33 @@
/*
* 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.
*/
internal val CONST = "CONST"
open class PublicClass {
internal fun foo(): String = "foo"
internal val bar: String = "bar"
open internal fun baz(): String = "PublicClass.baz()"
}
internal data class InternalDataClass(val x: Int, val y: Int)
internal fun box(): String {
return "OK"
}
@@ -0,0 +1,41 @@
/*
* 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.
*/
class PublicClassHeir : PublicClass() {
override internal fun baz(): String = "PublicClassHeir.baz()"
}
fun <T> assertEquals(e: T, a: T) {
if (e != a) throw Exception("Expected: $e, actual: $a")
}
fun test() {
assertEquals("CONST", CONST)
assertEquals("foo", PublicClass().foo())
assertEquals("bar", PublicClass().bar)
assertEquals("PublicClass.baz()", PublicClass().baz())
assertEquals("foo", PublicClassHeir().foo())
assertEquals("bar", PublicClassHeir().bar)
assertEquals("PublicClassHeir.baz()", PublicClassHeir().baz())
val data = InternalDataClass(10, 20)
assertEquals(10, data.x)
assertEquals(20, data.y)
assertEquals("OK", box())
}
@@ -4,6 +4,12 @@ package org.jetbrains.kotlin.gradle.dsl
interface KotlinJsOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions {
/**
* Disable internal declaration export
* Default value: false
*/
var friendModulesDisabled: kotlin.Boolean
/**
* Whether a main function should be called
* Possible values: "call", "noCall"
@@ -24,6 +24,11 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
get() = verboseField ?: false
set(value) { verboseField = value }
private var friendModulesDisabledField: kotlin.Boolean? = null
override var friendModulesDisabled: kotlin.Boolean
get() = friendModulesDisabledField ?: false
set(value) { friendModulesDisabledField = value }
private var mainField: kotlin.String? = null
override var main: kotlin.String
get() = mainField ?: "call"
@@ -69,6 +74,7 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
languageVersionField?.let { args.languageVersion = it }
suppressWarningsField?.let { args.suppressWarnings = it }
verboseField?.let { args.verbose = it }
friendModulesDisabledField?.let { args.friendModulesDisabled = it }
mainField?.let { args.main = it }
metaInfoField?.let { args.metaInfo = it }
moduleKindField?.let { args.moduleKind = it }
@@ -85,6 +91,7 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments.fil
languageVersion = "1.1"
suppressWarnings = false
verbose = false
friendModulesDisabled = false
main = "call"
metaInfo = true
moduleKind = "plain"
@@ -372,6 +372,8 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
null
}
args.friendModules = friendDependency
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
val messageCollector = GradleMessageCollector(logger)
@@ -106,13 +106,6 @@
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>1.9</version>
<dependencies>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
<version>1.7.7.1</version>
</dependency>
</dependencies>
<configuration>
<projectsDirectory>src/it</projectsDirectory>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
@@ -0,0 +1 @@
this['test-js-accessToInternal-tests'].org.jetbrains.test();
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>test-js-accessToInternal</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-js</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>js</goal>
</goals>
<configuration>
<output>${project.basedir}/customOutput/</output>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-js</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-js</artifactId>
<version>${kotlin.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/js/</outputDirectory>
<includes>**/*.js</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2017 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
internal val CONST = "CONST"
open class PublicClass {
internal fun foo(): String = "foo"
internal val bar: String = "bar"
open internal fun baz(): String = "PublicClass.baz()"
}
internal data class InternalDataClass(val x: Int, val y: Int)
internal fun box(): String {
return "OK"
}
@@ -0,0 +1,41 @@
/*
* 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
class PublicClassHeir : PublicClass() {
override internal fun baz(): String = "PublicClassHeir.baz()"
}
fun <T> assertEquals(e: T, a: T) {
if (e != a) throw Exception("Expected: $e, actual: $a")
}
fun test() {
assertEquals("CONST", CONST)
assertEquals("foo", PublicClass().foo())
assertEquals("bar", PublicClass().bar)
assertEquals("PublicClass.baz()", PublicClass().baz())
assertEquals("foo", PublicClassHeir().foo())
assertEquals("bar", PublicClassHeir().bar)
assertEquals("PublicClassHeir.baz()", PublicClassHeir().baz())
val data = InternalDataClass(10, 20)
assertEquals(10, data.x)
assertEquals(20, data.y)
}
@@ -0,0 +1,20 @@
import java.io.*;
import javax.script.*;
File file = new File(basedir, "target/js/test-js-accessToInternal.js");
if (!file.exists() || !file.isFile()) {
throw new FileNotFoundException("Could not find generated JS : " + file);
}
File testFile = new File(basedir, "target/test-js/test-js-accessToInternal-tests.js");
if (!testFile.exists() || !testFile.isFile()) {
throw new FileNotFoundException("Could not find generated JS : " + testFile);
}
String basePath = basedir.getPath();
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader(basePath + "/target/js/kotlin.js"));
engine.eval(new FileReader(basePath + "/target/js/test-js-accessToInternal.js"));
engine.eval(new FileReader(basePath + "/target/test-js/test-js-accessToInternal-tests.js"));
engine.eval(new FileReader(basePath + "/check.js"));
@@ -1,5 +1,5 @@
import java.io.*;
import org.mozilla.javascript.tools.shell.Main;
import javax.script.*;
File file = new File(basedir, "target/js/test-js-moduleKind.js");
if (!file.exists() || !file.isFile()) {
@@ -7,9 +7,9 @@ if (!file.exists() || !file.isFile()) {
}
String basePath = basedir.getPath();
Main.main(new String[] {
"-O", "-1",
"-f", basePath + "/amd.js",
"-f", basePath + "/target/js/kotlin.js",
"-f", basePath + "/target/js/test-js-moduleKind.js",
"-f", basePath + "/check.js"})
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader(basePath + "/amd.js"));
engine.eval(new FileReader(basePath + "/target/js/kotlin.js"));
engine.eval(new FileReader(basePath + "/target/js/test-js-moduleKind.js"));
engine.eval(new FileReader(basePath + "/check.js"));
@@ -34,9 +34,11 @@ import org.jetbrains.kotlin.js.JavaScript;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -109,15 +111,11 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase<K2JSCompilerArgument
arguments.sourceMap = sourceMap;
Set<String> collector = getOutputDirectoriesCollector();
if (outputFile != null) {
collector.add(new File(outputFile).getParent());
}
if (metaInfo) {
String output = com.google.common.base.Objects.firstNonNull(outputFile, ""); // fqname here because of J8 compatibility issues
String metaFile = StringsKt.substringBeforeLast(output, JavaScript.DOT_EXTENSION, output) + KotlinJavascriptMetadataUtils.META_JS_SUFFIX;
collector.add(new File(metaFile).getParent());
ConcurrentMap<String, List<String>> collector = getOutputDirectoriesCollector();
String key = project.getArtifactId();
List<String> paths = collector.computeIfAbsent(key, k -> Collections.synchronizedList(new ArrayList<String>()));
paths.add(new File(outputFile).getParent());
}
}
@@ -145,14 +143,16 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase<K2JSCompilerArgument
}
}
for (String path : getOutputDirectoriesCollector()) {
File file = new File(path);
for (List<String> paths : getOutputDirectoriesCollector().values()) {
for (String path : paths) {
File file = new File(path);
if (file.exists() && LibraryUtils.isKotlinJavascriptLibrary(file)) {
libraries.add(file.getAbsolutePath());
}
else {
getLog().debug("JS output directory missing: " + file);
if (file.exists() && LibraryUtils.isKotlinJavascriptLibrary(file)) {
libraries.add(file.getAbsolutePath());
}
else {
getLog().debug("JS output directory missing: " + file);
}
}
}
@@ -177,12 +177,12 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase<K2JSCompilerArgument
}
@SuppressWarnings("unchecked")
private Set<String> getOutputDirectoriesCollector() {
protected ConcurrentMap<String, List<String>> getOutputDirectoriesCollector() {
lock.lock();
try {
Set<String> collector = (Set<String>) getPluginContext().get(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME);
ConcurrentMap<String, List<String>> collector = (ConcurrentMap<String, List<String>>) getPluginContext().get(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME);
if (collector == null) {
collector = new ConcurrentSkipListSet<String>();
collector = new ConcurrentSkipListMap<String, List<String>>();
getPluginContext().put(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME, collector);
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.maven;
import com.intellij.openapi.util.text.StringUtil;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -27,8 +28,9 @@ import org.apache.maven.project.MavenProject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
import java.io.File;
import java.util.List;
import java.util.Collections;
/**
* Converts Kotlin to JavaScript code
*
@@ -79,6 +81,8 @@ public class KotlinTestJSCompilerMojo extends K2JSCompilerMojo {
@Override
protected void configureSpecificCompilerArguments(@NotNull K2JSCompilerArguments arguments) throws MojoExecutionException {
List<String> friends = getOutputDirectoriesCollector().getOrDefault(project.getArtifactId(), Collections.emptyList());
arguments.friendModules = StringUtil.join(friends, File.pathSeparator);
output = testOutput;
super.configureSpecificCompilerArguments(arguments);