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");