diff --git a/.idea/dictionaries/bashor.xml b/.idea/dictionaries/bashor.xml
index fa362eaa919..b3a119e13fe 100644
--- a/.idea/dictionaries/bashor.xml
+++ b/.idea/dictionaries/bashor.xml
@@ -3,6 +3,7 @@
ctor
interner
+ kclass
lookups
unescape
diff --git a/js/js.libraries/src/core/reflection.kt b/js/js.libraries/src/reflect/JsClass.kt
similarity index 64%
rename from js/js.libraries/src/core/reflection.kt
rename to js/js.libraries/src/reflect/JsClass.kt
index f9d3cc1cc4b..a373ab74ecd 100644
--- a/js/js.libraries/src/core/reflection.kt
+++ b/js/js.libraries/src/reflect/JsClass.kt
@@ -16,13 +16,24 @@
package kotlin.js
+import kotlin.reflect.KClass
+import kotlin.reflect.js.internal.KClassImpl
+
@native
interface JsClass {
val name: String
}
@native
+@Deprecated("Use class literal and extension property `js` instead.", replaceWith = ReplaceWith("T::class.js"))
fun jsClass(): JsClass = noImpl
+@Deprecated("Use class literal and extension property `js` instead.", replaceWith = ReplaceWith("this::class.js"))
val T.jsClass: JsClass
get() = js("Object").getPrototypeOf(this).constructor
+
+val KClass.js: JsClass
+ get() = (this as KClassImpl).jClass
+
+val JsClass.kotlin: KClass
+ get() = KClassImpl(this)
diff --git a/js/js.libraries/src/reflect/KClassImpl.kt b/js/js.libraries/src/reflect/KClassImpl.kt
new file mode 100644
index 00000000000..b85773887a1
--- /dev/null
+++ b/js/js.libraries/src/reflect/KClassImpl.kt
@@ -0,0 +1,81 @@
+/*
+ * 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 kotlin.reflect.js.internal
+
+import kotlin.reflect.*
+
+internal class KClassImpl(
+ internal val jClass: JsClass
+) : KClass {
+
+ private val metadata = jClass.asDynamic().`$metadata$`
+ // TODO: use FQN
+ private val hashCode = simpleName?.hashCode() ?: 0
+
+ override val simpleName: String?
+ get() = metadata?.simpleName
+
+ override val annotations: List
+ get() = TODO()
+ override val constructors: Collection>
+ get() = TODO()
+ override val isAbstract: Boolean
+ get() = TODO()
+ override val isCompanion: Boolean
+ get() = TODO()
+ override val isData: Boolean
+ get() = TODO()
+ override val isFinal: Boolean
+ get() = TODO()
+ override val isInner: Boolean
+ get() = TODO()
+ override val isOpen: Boolean
+ get() = TODO()
+ override val isSealed: Boolean
+ get() = TODO()
+ override val members: Collection>
+ get() = TODO()
+ override val nestedClasses: Collection>
+ get() = TODO()
+ override val objectInstance: T?
+ get() = TODO()
+ override val qualifiedName: String?
+ get() = TODO()
+ override val supertypes: List
+ get() = TODO()
+ override val typeParameters: List
+ get() = TODO()
+ override val visibility: KVisibility?
+ get() = TODO()
+
+ override fun equals(other: Any?): Boolean {
+ return other is KClassImpl<*> && jClass == other.jClass
+ }
+
+ override fun hashCode(): Int {
+ return hashCode
+ }
+
+ override fun isInstance(value: Any?): Boolean {
+ TODO()
+ }
+
+ override fun toString(): String {
+ // TODO: use FQN
+ return "class $simpleName"
+ }
+}
diff --git a/js/js.libraries/src/reflect/reflection.kt b/js/js.libraries/src/reflect/reflection.kt
new file mode 100644
index 00000000000..e5ed63f3c04
--- /dev/null
+++ b/js/js.libraries/src/reflect/reflection.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+// a package is omitted to get declarations directly under the module
+
+import kotlin.reflect.KClass
+import kotlin.reflect.js.internal.KClassImpl
+
+@JsName("getKClass")
+private fun getKClass(jClass: JsClass): KClass = getOrCreateKClass(jClass)
+@JsName("getKClassFromExpression")
+private fun getKClassFromExpression(e: T): KClass = getOrCreateKClass(e.jsClass)
+
+private fun getOrCreateKClass(jClass: JsClass): KClass {
+ val metadata = jClass.asDynamic().`$metadata$`
+
+ return if (metadata != null) {
+ if (metadata.`$kClass$` == null) {
+ val kClass = KClassImpl(jClass)
+ metadata.`$kClass$` = kClass
+ kClass
+ }
+ else {
+ metadata.`$kClass$`
+ }
+ }
+ else {
+ KClassImpl(jClass)
+ }
+}
+
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 f63b6963971..13096e0a2f7 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
@@ -6252,6 +6252,30 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/jsClassSimpleName.kt");
doTest(fileName);
}
+
+ @TestMetadata("kClass.kt")
+ public void testKClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/kClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("kClassOnReifiedType.kt")
+ public void testKClassOnReifiedType() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/kClassOnReifiedType.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("kClassSimpleName.kt")
+ public void testKClassSimpleName() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/kClassSimpleName.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("kClassToAndFromJsClass.kt")
+ public void testKClassToAndFromJsClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt");
+ doTest(fileName);
+ }
}
@TestMetadata("js/js.translator/testData/box/regression")
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
index 99683a0e7f1..ceb3443dbb4 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
@@ -67,6 +67,8 @@ public final class Namer {
public static final String IS_CHAR = "isChar";
public static final String IS_NUMBER = "isNumber";
public static final String IS_CHAR_SEQUENCE = "isCharSequence";
+ public static final String GET_KCLASS = "getKClass";
+ public static final String GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression";
public static final String CALLEE_NAME = "$fun";
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java
index 7c2948d1039..b417706f78e 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java
@@ -29,14 +29,15 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
-import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
+import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
import org.jetbrains.kotlin.js.translate.operation.BinaryOperationTranslator;
import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.kotlin.js.translate.reference.*;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
+import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
@@ -44,17 +45,16 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
-import org.jetbrains.kotlin.resolve.constants.ConstantValue;
-import org.jetbrains.kotlin.resolve.constants.NullValue;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
-import org.jetbrains.kotlin.types.KotlinType;
-import org.jetbrains.kotlin.types.TypeUtils;
+import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
import java.util.ArrayList;
import java.util.List;
+import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS;
+import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS_FROM_EXPRESSION;
import static org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor;
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.*;
@@ -63,8 +63,7 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatem
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar;
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateInitializerForProperty;
-import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR;
-import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET;
+import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure;
import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt.getResolvedCallWithAssert;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression;
@@ -226,6 +225,25 @@ public final class ExpressionVisitor extends TranslatorVisitor {
return CallableReferenceTranslator.INSTANCE.translate(expression, context);
}
+ @Override
+ public JsNode visitClassLiteralExpression(
+ @NotNull KtClassLiteralExpression expression, TranslationContext context
+ ) {
+ KtExpression receiverExpression = expression.getReceiverExpression();
+ assert receiverExpression != null : "Class literal expression should have a left-hand side";
+
+ DoubleColonLHS lhs = context.bindingContext().get(DOUBLE_COLON_LHS, receiverExpression);
+ assert lhs != null : "Class literal expression should have LHS resolved";
+
+ if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObject()) {
+ JsExpression receiver = translateAsExpression(receiverExpression, context);
+ return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
+ }
+
+ return new JsInvocation(context.namer().kotlin(GET_KCLASS), UtilsKt.getReferenceToJsClass(lhs.getType(), context));
+ }
+
+
@Override
@NotNull
public JsNode visitCallExpression(
diff --git a/js/js.translator/testData/box/reflection/_common.kt b/js/js.translator/testData/box/reflection/_common.kt
index 129cfe42758..76885c9bba9 100644
--- a/js/js.translator/testData/box/reflection/_common.kt
+++ b/js/js.translator/testData/box/reflection/_common.kt
@@ -20,3 +20,18 @@ enum class E {
@JsName("Q")
class R
+
+fun check(x: Any, y: Any, shouldBeEqual: Boolean = true, shouldBeSame: Boolean = true) {
+ assertNotEquals(null, x)
+ assertNotEquals(null, y)
+ if (shouldBeEqual) {
+ assertEquals(x, y)
+
+ if (shouldBeSame && x !== y) {
+ fail("Expected same instances, got expected = '$x', actual = '$y'")
+ }
+ }
+ else {
+ assertNotEquals(x, y)
+ }
+}
diff --git a/js/js.translator/testData/box/reflection/jsClass.kt b/js/js.translator/testData/box/reflection/jsClass.kt
index 0097f0a7992..73431b882a8 100644
--- a/js/js.translator/testData/box/reflection/jsClass.kt
+++ b/js/js.translator/testData/box/reflection/jsClass.kt
@@ -1,21 +1,12 @@
package foo
-fun check(x: JsClass<*>, y: JsClass<*>, shouldBeEquals: Boolean = true) {
- assertNotEquals(null, x)
- assertNotEquals(null, y)
- if (shouldBeEquals)
- assertEquals(x, y)
- else
- assertNotEquals(x, y)
-}
-
fun box(): String {
check(jsClass(), A().jsClass)
check(jsClass(), B().jsClass)
check(jsClass(), O.jsClass)
assertNotEquals(null, jsClass())
check(jsClass(), E.X.jsClass)
- check(jsClass(), E.Y.jsClass, shouldBeEquals = false)
+ check(jsClass(), E.Y.jsClass, shouldBeEqual = false)
// TODO uncomment after KT-13338 is fixed
// check(jsClass(), E.Z.jsClass)
diff --git a/js/js.translator/testData/box/reflection/jsClassOnReifiedType.kt b/js/js.translator/testData/box/reflection/jsClassOnReifiedType.kt
index a5675dc484f..d58c4ce10f5 100644
--- a/js/js.translator/testData/box/reflection/jsClassOnReifiedType.kt
+++ b/js/js.translator/testData/box/reflection/jsClassOnReifiedType.kt
@@ -6,10 +6,10 @@ inline fun foo(): JsClass {
}
fun box(): String {
- assertEquals(jsClass(), foo())
- assertEquals(jsClass(), foo())
- assertEquals(jsClass(), foo())
- assertEquals(jsClass(), foo())
+ check(jsClass(), foo())
+ check(jsClass(), foo())
+ check(jsClass(), foo())
+ check(jsClass(), foo())
return "OK"
}
diff --git a/js/js.translator/testData/box/reflection/kClass.kt b/js/js.translator/testData/box/reflection/kClass.kt
new file mode 100644
index 00000000000..80c91bb2116
--- /dev/null
+++ b/js/js.translator/testData/box/reflection/kClass.kt
@@ -0,0 +1,14 @@
+package foo
+
+fun box(): String {
+ check(A::class, A()::class)
+ check(B::class, B()::class)
+ check(O::class, (O)::class)
+ assertNotEquals(null, I::class)
+ check(E::class, E.X::class)
+ check(E::class, E.Y::class, shouldBeEqual = false)
+// TODO uncomment after KT-13338 is fixed
+// check(E::class, E.Z::class)
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt b/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt
new file mode 100644
index 00000000000..fd1c6ba3f63
--- /dev/null
+++ b/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt
@@ -0,0 +1,19 @@
+package foo
+
+import kotlin.reflect.KClass
+
+inline fun foo(b: Boolean = false): KClass {
+ if (b) {
+ val T = 1
+ }
+ return T::class
+}
+
+fun box(): String {
+ check(A::class, foo())
+ check(B::class, foo())
+ check(O::class, foo())
+ check(E::class, foo())
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/box/reflection/kClassSimpleName.kt b/js/js.translator/testData/box/reflection/kClassSimpleName.kt
new file mode 100644
index 00000000000..3d2047acef1
--- /dev/null
+++ b/js/js.translator/testData/box/reflection/kClassSimpleName.kt
@@ -0,0 +1,40 @@
+package foo
+
+class undefined
+
+class Outer {
+ class Nested
+
+ inner class Inner
+}
+
+fun testWithInstance() {
+ assertEquals("A", A()::class.simpleName)
+ assertEquals("B", B()::class.simpleName)
+ assertEquals("O", (O)::class.simpleName)
+ assertEquals("E", E.X::class.simpleName)
+ assertEquals("Y", E.Y::class.simpleName)
+// TODO uncomment after KT-13338 is fixed
+// assertEquals("E", E.Z::class.simpleName)
+ assertEquals("undefined", undefined()::class.simpleName)
+ assertEquals("Nested", Outer.Nested()::class.simpleName)
+ assertEquals("Inner", Outer().Inner()::class.simpleName)
+}
+
+fun testWithClassReference() {
+ assertEquals("A", A::class.simpleName)
+ assertEquals("B", B::class.simpleName)
+ assertEquals("O", O::class.simpleName)
+ assertEquals("I", I::class.simpleName)
+ assertEquals("E", E::class.simpleName)
+ assertEquals("undefined", foo.undefined::class.simpleName)
+ assertEquals("Nested", Outer.Nested::class.simpleName)
+ assertEquals("Inner", Outer.Inner::class.simpleName)
+}
+
+fun box(): String {
+ testWithInstance()
+ testWithClassReference()
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt b/js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt
new file mode 100644
index 00000000000..8fd0e9d7bbb
--- /dev/null
+++ b/js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt
@@ -0,0 +1,29 @@
+package foo
+
+import kotlin.reflect.KClass
+
+fun check(k: KClass<*>, j: JsClass<*>) {
+ assertNotEquals(null, k)
+ assertNotEquals(null, j)
+
+ assertEquals(k.js, j)
+ assertEquals(k, j.kotlin)
+ assertEquals(j, j.kotlin.js)
+ assertEquals(k, k.js.kotlin)
+}
+
+fun jsClassbyName(name: String) = js("JS_TESTS").foo[name]
+
+fun box(): String {
+ check(A::class, jsClassbyName("A"))
+ check(B::class, jsClassbyName("B"))
+ check(O::class, jsClassbyName("O").constructor)
+ check(I::class, jsClassbyName("I"))
+ check(E::class, jsClassbyName("E"))
+ check(E.X::class, jsClassbyName("E").X.constructor)
+ check(E.Y::class, jsClassbyName("E").Y.constructor)
+// TODO uncomment after KT-13338 is fixed
+// check(E.Z::class, jsClassbyName("E").Z.constructor)
+
+ return "OK"
+}