From 9b4b7960d3af9026fd11bd247b01769d286796d0 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 22 Dec 2016 13:19:07 +0300 Subject: [PATCH] JS: prevent compiler from importing module more than once if not necessary. See KT-15260 --- .../js/test/semantics/BoxJsTestGenerated.java | 12 +++++++++++ .../js/test/utils/DirectiveTestUtils.java | 12 +++++++++++ .../js/translate/context/StaticContext.java | 15 ++++++------- .../box/jsModule/importCountCommonJS.js | 12 +++++++++++ .../box/jsModule/importCountCommonJS.kt | 17 +++++++++++++++ .../testData/box/jsModule/importCountUmd.js | 11 ++++++++++ .../testData/box/jsModule/importCountUmd.kt | 21 +++++++++++++++++++ 7 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 js/js.translator/testData/box/jsModule/importCountCommonJS.js create mode 100644 js/js.translator/testData/box/jsModule/importCountCommonJS.kt create mode 100644 js/js.translator/testData/box/jsModule/importCountUmd.js create mode 100644 js/js.translator/testData/box/jsModule/importCountUmd.kt diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index b8e1da4ad3e..42ae15562b6 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -5083,6 +5083,18 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/externalProperty.kt"); doTest(fileName); } + + @TestMetadata("importCountCommonJS.kt") + public void testImportCountCommonJS() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/importCountCommonJS.kt"); + doTest(fileName); + } + + @TestMetadata("importCountUmd.kt") + public void testImportCountUmd() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/importCountUmd.kt"); + doTest(fileName); + } } @TestMetadata("js/js.translator/testData/box/jsName") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java index 9f664268d51..248a26ff4cd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java @@ -92,6 +92,17 @@ public class DirectiveTestUtils { } }; + private static final DirectiveHandler FUNCTION_CALLED_TIMES = new DirectiveHandler("FUNCTION_CALLED_TIMES") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + int expectedCount = Integer.parseInt(arguments.getNamedArgument("count")); + String functionName = arguments.getFirst(); + CallCounter counter = CallCounter.countCalls(ast); + int actualCount = counter.getUnqualifiedCallsCount(functionName); + assertEquals("Function " + functionName, expectedCount, actualCount); + } + }; + private static boolean parseBooleanArgument(@NotNull ArgumentsHelper arguments, @NotNull String name, boolean defaultValue) { String value = arguments.findNamedArgument(name); return value != null ? Boolean.parseBoolean(value) : defaultValue; @@ -253,6 +264,7 @@ public class DirectiveTestUtils { private static final List DIRECTIVE_HANDLERS = Arrays.asList( FUNCTION_CONTAINS_NO_CALLS, FUNCTION_NOT_CALLED, + FUNCTION_CALLED_TIMES, PROPERTY_NOT_USED, PROPERTY_NOT_READ_FROM, PROPERTY_NOT_WRITTEN_TO, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 21db13e2caa..bab9f699bee 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -661,12 +661,13 @@ public final class StaticContext { @NotNull private ImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) { - ImportedModuleKey key = new ImportedModuleKey(baseName, descriptor); + JsName plainName = descriptor != null && config.getModuleKind() == ModuleKind.UMD ? + rootScope.declareName(getPlainId(descriptor)) : null; + ImportedModuleKey key = new ImportedModuleKey(baseName, plainName); ImportedModule module = importedModules.get(key); if (module == null) { JsName internalName = rootScope.declareTemporaryName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName)); - JsName plainName = descriptor != null ? rootScope.declareName(getPlainId(descriptor)) : null; module = new ImportedModule(baseName, internalName, plainName != null ? pureFqn(plainName, null) : null); importedModules.put(key, module); } @@ -835,11 +836,11 @@ public final class StaticContext { private final String baseName; @Nullable - private final DeclarationDescriptor declaration; + private final JsName plainName; - public ImportedModuleKey(@NotNull String baseName, @Nullable DeclarationDescriptor declaration) { + public ImportedModuleKey(@NotNull String baseName, @Nullable JsName plainName) { this.baseName = baseName; - this.declaration = declaration; + this.plainName = plainName; } @Override @@ -850,7 +851,7 @@ public final class StaticContext { ImportedModuleKey key = (ImportedModuleKey) o; if (!baseName.equals(key.baseName)) return false; - if (declaration != null ? !declaration.equals(key.declaration) : key.declaration != null) return false; + if (plainName != null ? !plainName.equals(key.plainName) : key.plainName != null) return false; return true; } @@ -858,7 +859,7 @@ public final class StaticContext { @Override public int hashCode() { int result = baseName.hashCode(); - result = 31 * result + (declaration != null ? declaration.hashCode() : 0); + result = 31 * result + (plainName != null ? plainName.hashCode() : 0); return result; } } diff --git a/js/js.translator/testData/box/jsModule/importCountCommonJS.js b/js/js.translator/testData/box/jsModule/importCountCommonJS.js new file mode 100644 index 00000000000..5f96f6e4605 --- /dev/null +++ b/js/js.translator/testData/box/jsModule/importCountCommonJS.js @@ -0,0 +1,12 @@ +__beginModule__(); +module.exports = function(param) { + switch (typeof param) { + case "number": + return "a"; + case "string": + return "b"; + default: + return "c"; + } +}; +__endModule__("lib"); \ No newline at end of file diff --git a/js/js.translator/testData/box/jsModule/importCountCommonJS.kt b/js/js.translator/testData/box/jsModule/importCountCommonJS.kt new file mode 100644 index 00000000000..d98470ea4d7 --- /dev/null +++ b/js/js.translator/testData/box/jsModule/importCountCommonJS.kt @@ -0,0 +1,17 @@ +// MODULE_KIND: COMMON_JS +// FUNCTION_CALLED_TIMES: require count=2 + +@JsModule("lib") +external fun f(x: Int): String + +@JsModule("lib") +external fun f(x: String): String + +@JsModule("lib") +external fun g(x: Boolean): String + +fun box(): String { + val result = f(23) + f("foo") + g(true) + if (result != "abc") return "fail: $result" + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/jsModule/importCountUmd.js b/js/js.translator/testData/box/jsModule/importCountUmd.js new file mode 100644 index 00000000000..632c6ee4abc --- /dev/null +++ b/js/js.translator/testData/box/jsModule/importCountUmd.js @@ -0,0 +1,11 @@ +var f = function(param) { + switch (typeof param) { + case "number": + return "a"; + case "string": + return "b"; + default: + return "c"; + } +}; +var g = f; \ No newline at end of file diff --git a/js/js.translator/testData/box/jsModule/importCountUmd.kt b/js/js.translator/testData/box/jsModule/importCountUmd.kt new file mode 100644 index 00000000000..d0d59b0c5b2 --- /dev/null +++ b/js/js.translator/testData/box/jsModule/importCountUmd.kt @@ -0,0 +1,21 @@ +// MODULE_KIND: UMD +// NO_JS_MODULE_SYSTEM +// FUNCTION_CALLED_TIMES: require count=3 + +@JsModule("lib") +@JsNonModule +external fun f(x: Int): String + +@JsModule("lib") +@JsNonModule +external fun f(x: String): String + +@JsModule("lib") +@JsNonModule +external fun g(x: Boolean): String + +fun box(): String { + val result = f(23) + f("foo") + g(true) + if (result != "abc") return "fail: $result" + return "OK" +} \ No newline at end of file