JS: add support of JsQualifier annotation. See KT-15905

This commit is contained in:
Alexey Andreev
2017-01-24 14:43:21 +03:00
parent 6624736ccf
commit cc67f6c9f7
13 changed files with 165 additions and 10 deletions
@@ -42,6 +42,7 @@ 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 static final FqName JS_NON_MODULE_ANNOTATION = new FqName("kotlin.js.JsNonModule");
private static final FqName JS_QUALIFIER_ANNOTATION = new FqName("kotlin.js.JsQualifier");
private AnnotationsUtils() {
}
@@ -197,21 +198,34 @@ public final class AnnotationsUtils {
@Nullable
public static String getModuleName(@NotNull DeclarationDescriptor declaration) {
AnnotationDescriptor annotation = declaration.getAnnotations().findAnnotation(JS_MODULE_ANNOTATION);
return annotation != null ? extractJsModuleName(annotation) : null;
return annotation != null ? extractSingleStringArgument(annotation) : null;
}
@Nullable
public static String getFileModuleName(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor declaration) {
return getSingleStringAnnotationArgument(bindingContext, declaration, JS_MODULE_ANNOTATION);
}
@Nullable
public static String getFileQualifier(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor declaration) {
return getSingleStringAnnotationArgument(bindingContext, declaration, JS_QUALIFIER_ANNOTATION);
}
@Nullable
private static String getSingleStringAnnotationArgument(
@NotNull BindingContext bindingContext,
@NotNull DeclarationDescriptor declaration,
@NotNull FqName annotationFqName
) {
for (AnnotationDescriptor annotation : getContainingFileAnnotations(bindingContext, declaration)) {
DeclarationDescriptor annotationType = annotation.getType().getConstructor().getDeclarationDescriptor();
if (annotationType == null) continue;
FqNameUnsafe fqName = DescriptorUtils.getFqName(annotation.getType().getConstructor().getDeclarationDescriptor());
if (fqName.equals(JS_MODULE_ANNOTATION.toUnsafe())) {
return extractJsModuleName(annotation);
if (fqName.equals(annotationFqName.toUnsafe())) {
return extractSingleStringArgument(annotation);
}
}
return null;
}
@@ -237,7 +251,7 @@ public final class AnnotationsUtils {
}
@Nullable
private static String extractJsModuleName(@NotNull AnnotationDescriptor annotation) {
private static String extractSingleStringArgument(@NotNull AnnotationDescriptor annotation) {
if (annotation.getAllValueArguments().isEmpty()) return null;
ConstantValue<?> importValue = annotation.getAllValueArguments().values().iterator().next();
+5 -1
View File
@@ -54,4 +54,8 @@ annotation class JsModule(val import: String)
@Retention(AnnotationRetention.BINARY)
@Target(CLASS, PROPERTY, FUNCTION, FILE)
annotation class JsNonModule
annotation class JsNonModule
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FILE)
annotation class JsQualifier(val value: String)
@@ -144,9 +144,6 @@ object KotlinJavascriptSerializationUtil {
}
}, skip)
writeFun(KotlinJavascriptSerializedResourcePaths.getFileListFilePath(fqName),
serializeFiles(fileRegistry, bindingContext, AnnotationSerializer(serializer.stringTable)))
val packageStream = ByteArrayOutputStream()
val fragments = packageView.fragments
val members = fragments
@@ -158,6 +155,9 @@ object KotlinJavascriptSerializationUtil {
writeFun(KotlinJavascriptSerializedResourcePaths.getPackageFilePath(fqName), packageStream.toByteArray())
}
writeFun(KotlinJavascriptSerializedResourcePaths.getFileListFilePath(fqName),
serializeFiles(fileRegistry, bindingContext, AnnotationSerializer(serializer.stringTable)))
val strings = serializerExtension.stringTable
serializeClassNamesInPackage(fqName, fragments, strings, skip, writeFun)
@@ -5238,6 +5238,39 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
}
}
@TestMetadata("js/js.translator/testData/box/jsQualifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JsQualifier extends AbstractBoxJsTest {
public void testAllFilesPresentInJsQualifier() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/jsQualifier"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsQualifier/classes.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsQualifier/simple.kt");
doTest(fileName);
}
@TestMetadata("umdFallback.kt")
public void testUmdFallback() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsQualifier/umdFallback.kt");
doTest(fileName);
}
@TestMetadata("withModule.kt")
public void testWithModule() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsQualifier/withModule.kt");
doTest(fileName);
}
}
@TestMetadata("js/js.translator/testData/box/labels")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.context;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.LinkedHashMap;
import org.jetbrains.annotations.NotNull;
@@ -287,7 +288,14 @@ public final class StaticContext {
String fileModuleName = AnnotationsUtils.getFileModuleName(getBindingContext(), suggested.getDescriptor());
if (fileModuleName != null) {
JsName moduleJsName = getImportedModule(fileModuleName, null).internalName;
expression = pureFqn(moduleJsName, null);
expression = pureFqn(moduleJsName, expression);
}
String qualifier = AnnotationsUtils.getFileQualifier(getBindingContext(), suggested.getDescriptor());
if (qualifier != null) {
for (String qualifierPart : StringUtil.split(qualifier, ".")) {
expression = pureFqn(qualifierPart, expression);
}
}
}
+16
View File
@@ -0,0 +1,16 @@
var pkg = function() {
function C() {
}
C.prototype.o = function() {
return "O";
};
function D() {
}
D.prototype.k = function() {
return "K";
};
C.D = D;
return {
C: C
};
}();
+15
View File
@@ -0,0 +1,15 @@
// MODULE: lib
// FILE: lib.kt
@file:JsQualifier("pkg")
external class C {
fun o(): String
class D {
fun k(): String
}
}
// MODULE: main(lib)
// FILE: main.kt
fun box() = C().o() + C.D().k()
+8
View File
@@ -0,0 +1,8 @@
var a = {
b: {
c: function() {
return "O";
},
d: "K"
}
};
+15
View File
@@ -0,0 +1,15 @@
// MODULE: lib
// FILE: lib.kt
@file:JsQualifier("a.b")
package ab
external fun c(): String
external val d: String
// MODULE: main(lib)
// FILE: main.kt
package main
fun box() = ab.c() + ab.d
@@ -0,0 +1,5 @@
var foo = {
bar: function() {
return "OK";
}
};
@@ -0,0 +1,13 @@
// MODULE: lib
// FILE: lib.kt
// MODULE_KIND: UMD
// NO_JS_MODULE_SYSTEM
@file:JsQualifier("foo")
external fun bar(): String
// MODULE: main(lib)
// FILE: main.kt
// MODULE_KIND: UMD
// NO_JS_MODULE_SYSTEM
fun box() = bar()
@@ -0,0 +1,9 @@
define("libjs", ["exports"], function(exports) {
exports.a = {
b: {
c: function () {
return "OK";
}
}
};
});
+15
View File
@@ -0,0 +1,15 @@
// MODULE: lib
// FILE: lib.kt
// MODULE_KIND: AMD
@file:JsQualifier("a.b")
@file:JsModule("libjs")
package ab
external fun c(): String
// MODULE: main(lib)
// FILE: main.kt
// MODULE_KIND: AMD
package main
fun box() = ab.c()