From 6df40559f003d5fa017697193063dd4866a74403 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 28 Jun 2016 14:40:49 +0300 Subject: [PATCH] KT-12877: initial simple implementation of JsModule. Remove unnecessary ModuleDescriptor from several classes in pipeline --- .../js/translate/utils/AnnotationsUtils.java | 11 ++++ js/js.libraries/src/core/annotations.kt | 4 ++ .../test/semantics/JsModuleTestGenerated.java | 61 +++++++++++++++++++ .../kotlin/js/facade/K2JSTranslator.java | 2 +- .../js/translate/context/StaticContext.java | 25 ++++++-- .../js/translate/general/Translation.java | 7 +-- .../testData/jsModule/cases/externalClass.kt | 12 ++++ .../jsModule/cases/externalFunction.kt | 8 +++ .../testData/jsModule/cases/externalObject.kt | 13 ++++ .../jsModule/cases/externalProperty.kt | 8 +++ .../testData/jsModule/native/externalClass.js | 10 +++ .../jsModule/native/externalFunction.js | 5 ++ .../jsModule/native/externalObject.js | 10 +++ .../jsModule/native/externalProperty.js | 3 + 14 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsModuleTestGenerated.java create mode 100644 js/js.translator/testData/jsModule/cases/externalClass.kt create mode 100644 js/js.translator/testData/jsModule/cases/externalFunction.kt create mode 100644 js/js.translator/testData/jsModule/cases/externalObject.kt create mode 100644 js/js.translator/testData/jsModule/cases/externalProperty.kt create mode 100644 js/js.translator/testData/jsModule/native/externalClass.js create mode 100644 js/js.translator/testData/jsModule/native/externalFunction.js create mode 100644 js/js.translator/testData/jsModule/native/externalObject.js create mode 100644 js/js.translator/testData/jsModule/native/externalProperty.js diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java index cea133e8524..68e941337ff 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.constants.ConstantValue; public final class AnnotationsUtils { private static final String JS_NAME = "kotlin.js.JsName"; + private static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule"); private AnnotationsUtils() { } @@ -169,4 +170,14 @@ public final class AnnotationsUtils { } return false; } + + @Nullable + public static String getModuleName(@NotNull DeclarationDescriptor declaration) { + AnnotationDescriptor annotation = declaration.getAnnotations().findAnnotation(JS_MODULE_ANNOTATION); + if (annotation == null) return null; + + ConstantValue importValue = annotation.getAllValueArguments().values().iterator().next(); + assert importValue != null : "JsModule annotation should have at least one argument"; + return (String) importValue.getValue(); + } } diff --git a/js/js.libraries/src/core/annotations.kt b/js/js.libraries/src/core/annotations.kt index de12b11cab0..ac879b1c767 100644 --- a/js/js.libraries/src/core/annotations.kt +++ b/js/js.libraries/src/core/annotations.kt @@ -49,3 +49,7 @@ internal annotation class marker @Retention(AnnotationRetention.BINARY) @Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER) annotation class JsName(val name: String) + +@Retention(AnnotationRetention.BINARY) +@Target(CLASS, PROPERTY, FUNCTION, FILE) +annotation class JsModule(val import: String) \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsModuleTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsModuleTestGenerated.java new file mode 100644 index 00000000000..380a3dcff80 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsModuleTestGenerated.java @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2016 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.js.test.semantics; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("js/js.translator/testData/jsModule/cases") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class JsModuleTestGenerated extends AbstractJsModuleTest { + public void testAllFilesPresentInCases() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/jsModule/cases"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("externalClass.kt") + public void testExternalClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/jsModule/cases/externalClass.kt"); + doTest(fileName); + } + + @TestMetadata("externalFunction.kt") + public void testExternalFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/jsModule/cases/externalFunction.kt"); + doTest(fileName); + } + + @TestMetadata("externalObject.kt") + public void testExternalObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/jsModule/cases/externalObject.kt"); + doTest(fileName); + } + + @TestMetadata("externalProperty.kt") + public void testExternalProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/jsModule/cases/externalProperty.kt"); + doTest(fileName); + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java index 769dd9c5e14..be8763dd08c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java @@ -79,7 +79,7 @@ public final class K2JSTranslator { ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor(); Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics(); - TranslationContext context = Translation.generateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); + TranslationContext context = Translation.generateAst(bindingTrace, files, mainCallParameters, config); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); 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 a545931c61e..d1f0ce0a54c 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 @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.js.translate.context.generator.Generator; import org.jetbrains.kotlin.js.translate.context.generator.Rule; import org.jetbrains.kotlin.js.translate.declaration.InterfaceFunctionCopier; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqNameUnsafe; @@ -395,6 +396,9 @@ public final class StaticContext { return config; } + // TODO: add this to NameSuggestion: + // String moduleName = AnnotationsUtils.getModuleName(descriptor); + @NotNull public JsName importDeclaration(@NotNull String suggestedName, @NotNull JsExpression declaration) { // Adding prefix is a workaround for a problem with scopes. @@ -595,13 +599,22 @@ public final class StaticContext { if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null; - JsName moduleId = moduleName.equals(Namer.KOTLIN_LOWER_NAME) ? rootScope.declareName(Namer.KOTLIN_NAME) : - importedModules.get(moduleName); - if (moduleId == null) { - moduleId = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(moduleName)); - importedModules.put(moduleName, moduleId); - } + return getModuleReference(moduleName); + } + @NotNull + private JsNameRef getModuleReference(@NotNull String baseName) { + return JsAstUtils.pureFqn(getModuleInternalName(baseName), null); + } + + @NotNull + private JsName getModuleInternalName(@NotNull String baseName) { + JsName moduleId = baseName.equals(Namer.KOTLIN_LOWER_NAME) ? rootScope.declareName(Namer.KOTLIN_NAME) : + importedModules.get(baseName); + if (moduleId == null) { + moduleId = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName)); + importedModules.put(baseName, moduleId); + } return moduleId; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index cef9f941463..b51be3ba030 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.idea.MainFunctionDetector; import org.jetbrains.kotlin.js.config.JSConfigurationKeys; import org.jetbrains.kotlin.js.config.JsConfig; @@ -229,11 +228,10 @@ public final class Translation { @NotNull BindingTrace bindingTrace, @NotNull Collection files, @NotNull MainCallParameters mainCallParameters, - @NotNull ModuleDescriptor moduleDescriptor, @NotNull JsConfig config ) throws TranslationException { try { - return doGenerateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); + return doGenerateAst(bindingTrace, files, mainCallParameters, config); } catch (UnsupportedOperationException e) { throw new UnsupportedFeatureException("Unsupported feature used.", e); @@ -248,10 +246,9 @@ public final class Translation { @NotNull BindingTrace bindingTrace, @NotNull Collection files, @NotNull MainCallParameters mainCallParameters, - @NotNull ModuleDescriptor moduleDescriptor, @NotNull JsConfig config ) { - StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor); + StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config); JsProgram program = staticContext.getProgram(); JsName rootPackageName = program.getRootScope().declareName(Namer.getRootPackageName()); diff --git a/js/js.translator/testData/jsModule/cases/externalClass.kt b/js/js.translator/testData/jsModule/cases/externalClass.kt new file mode 100644 index 00000000000..bc519353308 --- /dev/null +++ b/js/js.translator/testData/jsModule/cases/externalClass.kt @@ -0,0 +1,12 @@ +package foo + +@JsModule("lib") @native class A(@native val x: Int = noImpl) { + @native fun foo(y: Int): Int = noImpl +} + +fun box(): String { + val a = A(23) + assertEquals(23, a.x) + assertEquals(65, a.foo(42)) + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/cases/externalFunction.kt b/js/js.translator/testData/jsModule/cases/externalFunction.kt new file mode 100644 index 00000000000..962af3c212d --- /dev/null +++ b/js/js.translator/testData/jsModule/cases/externalFunction.kt @@ -0,0 +1,8 @@ +package foo + +@JsModule("lib") @native fun foo(y: Int): Int = noImpl + +fun box(): String { + assertEquals(65, foo(42)) + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/cases/externalObject.kt b/js/js.translator/testData/jsModule/cases/externalObject.kt new file mode 100644 index 00000000000..849047d3a61 --- /dev/null +++ b/js/js.translator/testData/jsModule/cases/externalObject.kt @@ -0,0 +1,13 @@ +package foo + +@JsModule("lib") @native object A { + @native val x: Int = noImpl + + @native fun foo(y: Int): Int = noImpl +} + +fun box(): String { + assertEquals(23, A.x) + assertEquals(65, A.foo(42)) + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/cases/externalProperty.kt b/js/js.translator/testData/jsModule/cases/externalProperty.kt new file mode 100644 index 00000000000..d2889d863d4 --- /dev/null +++ b/js/js.translator/testData/jsModule/cases/externalProperty.kt @@ -0,0 +1,8 @@ +package foo + +@JsModule("lib") @native val foo: Int = noImpl + +fun box(): String { + assertEquals(23, foo) + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/native/externalClass.js b/js/js.translator/testData/jsModule/native/externalClass.js new file mode 100644 index 00000000000..676c34773db --- /dev/null +++ b/js/js.translator/testData/jsModule/native/externalClass.js @@ -0,0 +1,10 @@ +define("lib", [], function() { + function A(x) { + this.x = x; + } + A.prototype.foo = function (y) { + return this.x + y; + }; + + return A; +}); \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/native/externalFunction.js b/js/js.translator/testData/jsModule/native/externalFunction.js new file mode 100644 index 00000000000..4309205c55c --- /dev/null +++ b/js/js.translator/testData/jsModule/native/externalFunction.js @@ -0,0 +1,5 @@ +define("lib", [], function() { + return function(y) { + return 23 + y; + }; +}); \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/native/externalObject.js b/js/js.translator/testData/jsModule/native/externalObject.js new file mode 100644 index 00000000000..968d2d99957 --- /dev/null +++ b/js/js.translator/testData/jsModule/native/externalObject.js @@ -0,0 +1,10 @@ +define("lib", [], function() { + A = { + x: 23, + foo: function(y) { + return this.x + y; + } + }; + + return A; +}); \ No newline at end of file diff --git a/js/js.translator/testData/jsModule/native/externalProperty.js b/js/js.translator/testData/jsModule/native/externalProperty.js new file mode 100644 index 00000000000..e761af87333 --- /dev/null +++ b/js/js.translator/testData/jsModule/native/externalProperty.js @@ -0,0 +1,3 @@ +define("lib", [], function() { + return 23; +}); \ No newline at end of file