JS backend: add the ability to get JS constructor function by class name and from instance. Support it for reified type parameters too. Add ability to get name of class.
#KT-5987 Fixed #KT-4115 Fixed
This commit is contained in:
@@ -1129,6 +1129,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractCastTest>() {
|
||||
model("expression/cast/cases")
|
||||
}
|
||||
|
||||
testClass<AbstractLightReflectionTest>() {
|
||||
model("reflection/light/cases")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("js/js.tests/test", "compiler/testData") {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.js
|
||||
|
||||
@native
|
||||
interface JsClass<T> {
|
||||
val name: String
|
||||
}
|
||||
|
||||
@native
|
||||
fun <T> jsClass(): JsClass<T> = noImpl
|
||||
|
||||
val <T> T.jsClass: JsClass<T>
|
||||
get() = js("Object").getPrototypeOf(this).constructor
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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/reflection/light/cases")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class LightReflectionTestGenerated extends AbstractLightReflectionTest {
|
||||
public void testAllFilesPresentInCases() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/reflection/light/cases"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("jsClass.kt")
|
||||
public void testJsClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/reflection/light/cases/jsClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jsClassName.kt")
|
||||
public void testJsClassName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/reflection/light/cases/jsClassName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jsClassOnReifiedType.kt")
|
||||
public void testJsClassOnReifiedType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/reflection/light/cases/jsClassOnReifiedType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.test.semantics
|
||||
|
||||
import org.jetbrains.kotlin.js.test.*
|
||||
import org.jetbrains.kotlin.js.test.AbstractSingleFileTranslationWithDirectivesTest
|
||||
import org.jetbrains.kotlin.js.test.KotlinJSMultiFileTest
|
||||
import org.jetbrains.kotlin.js.test.MultipleModulesTranslationTest
|
||||
import org.jetbrains.kotlin.js.test.SingleFileTranslationTest
|
||||
|
||||
abstract class AbstractBlackBoxTest(d: String) : SingleFileTranslationTest(d) {
|
||||
override fun doTest(filename: String) = checkBlackBoxIsOkByPath(filename)
|
||||
@@ -60,6 +63,8 @@ abstract class AbstractLocalClassesTest : AbstractBlackBoxTest("localClasses/")
|
||||
|
||||
abstract class AbstractNonLocalReturnsTest : KotlinJSMultiFileTest("inline.generated/nonLocalReturns/")
|
||||
|
||||
public abstract class AbstractRttiTest : SingleFileTranslationTest("rtti/")
|
||||
abstract class AbstractRttiTest : SingleFileTranslationTest("rtti/")
|
||||
|
||||
public abstract class AbstractCastTest : SingleFileTranslationTest("expression/cast/")
|
||||
abstract class AbstractCastTest : SingleFileTranslationTest("expression/cast/")
|
||||
|
||||
abstract class AbstractLightReflectionTest : SingleFileTranslationTest("reflection/light/")
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -128,6 +128,9 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
|
||||
for (TypeParameterDescriptor type : descriptor.getTypeParameters()) {
|
||||
if (type.isReified()) {
|
||||
JsName paramNameForType = context().getNameForDescriptor(type);
|
||||
jsParameters.add(new JsParameter(paramNameForType));
|
||||
|
||||
String suggestedName = Namer.isInstanceSuggestedName(type);
|
||||
JsName paramName = functionObject.getScope().declareName(suggestedName);
|
||||
jsParameters.add(new JsParameter(paramName));
|
||||
|
||||
+30
@@ -22,8 +22,10 @@ import com.google.dart.compiler.backend.js.ast.JsNew;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
|
||||
import org.jetbrains.kotlin.js.patterns.NamePredicate;
|
||||
@@ -35,6 +37,7 @@ import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntri
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
||||
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.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtQualifiedExpression;
|
||||
@@ -45,6 +48,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
|
||||
import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
|
||||
@@ -140,6 +144,30 @@ public final class TopLevelFIF extends CompositeFIF {
|
||||
}
|
||||
};
|
||||
|
||||
private static final FunctionIntrinsic JS_CLASS_FUN_INTRINSIC = new FunctionIntrinsic() {
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression apply(
|
||||
@NotNull CallInfo callInfo, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context
|
||||
) {
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = callInfo.getResolvedCall();
|
||||
Map<TypeParameterDescriptor, KotlinType> typeArguments = resolvedCall.getTypeArguments();
|
||||
|
||||
assert typeArguments.size() == 1;
|
||||
KotlinType type = typeArguments.values().iterator().next();
|
||||
|
||||
return UtilsKt.getReferenceToJsClass(type, context);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression apply(
|
||||
@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context
|
||||
) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
|
||||
|
||||
@@ -166,6 +194,8 @@ public final class TopLevelFIF extends CompositeFIF {
|
||||
add(pattern("kotlin.js", "Json", "set"), ArrayFIF.SET_INTRINSIC);
|
||||
|
||||
add(pattern("kotlin", "Throwable", "getMessage"), MESSAGE_PROPERTY_INTRINSIC);
|
||||
|
||||
add(pattern("kotlin.js", "jsClass"), JS_CLASS_FUN_INTRINSIC);
|
||||
}
|
||||
|
||||
private abstract static class NativeMapGetSet extends CallParametersAwareFunctionIntrinsic {
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -316,6 +317,8 @@ private fun Map<TypeParameterDescriptor, KotlinType>.addReifiedTypeArgsTo(
|
||||
|
||||
val argumentType = get(param) ?: continue
|
||||
|
||||
reifiedTypeArguments.add(getReferenceToJsClass(argumentType, context))
|
||||
|
||||
val isCheckCallable = patternTranslator.getIsTypeCheckCallable(argumentType)
|
||||
reifiedTypeArguments.add(isCheckCallable)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -17,13 +17,16 @@
|
||||
package org.jetbrains.kotlin.js.translate.utils
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
fun generateDelegateCall(
|
||||
fromDescriptor: FunctionDescriptor,
|
||||
@@ -76,3 +79,29 @@ fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> {
|
||||
result += Pair(subList(lastIndex, size), lastClass)
|
||||
return result
|
||||
}
|
||||
|
||||
fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsNameRef {
|
||||
val referenceToJsClass: JsNameRef
|
||||
|
||||
val classifierDescriptor = type.constructor.declarationDescriptor
|
||||
|
||||
if (classifierDescriptor is ClassDescriptor) {
|
||||
val reference = context.getQualifiedReference(classifierDescriptor)
|
||||
if (classifierDescriptor.kind == ClassKind.OBJECT) {
|
||||
referenceToJsClass = JsAstUtils.pureFqn("constructor", JsInvocation(JsNameRef("getPrototypeOf", JsNameRef("Object")), reference))
|
||||
}
|
||||
else {
|
||||
referenceToJsClass = reference
|
||||
}
|
||||
}
|
||||
else if (classifierDescriptor is TypeParameterDescriptor) {
|
||||
assert(classifierDescriptor.isReified)
|
||||
|
||||
referenceToJsClass = context.getNameForDescriptor(classifierDescriptor).makeRef()
|
||||
}
|
||||
else {
|
||||
throw IllegalStateException("Can't get reference for $type")
|
||||
}
|
||||
|
||||
return referenceToJsClass
|
||||
}
|
||||
|
||||
+4
-3
@@ -192,7 +192,7 @@
|
||||
}
|
||||
|
||||
Kotlin.createTraitNow = function (bases, properties, staticProperties) {
|
||||
var obj = function () {};
|
||||
var obj = {};
|
||||
|
||||
obj.$metadata$ = computeMetadata(bases, properties, staticProperties);
|
||||
obj.$metadata$.type = Kotlin.TYPE.TRAIT;
|
||||
@@ -301,6 +301,7 @@
|
||||
Kotlin.createTrait = function (basesFun, properties, staticProperties) {
|
||||
function $o() {
|
||||
var klass = Kotlin.createTraitNow(getBases(basesFun), properties, staticProperties);
|
||||
klass.name = $o.className;
|
||||
Object.defineProperty(this, $o.className, {value: klass});
|
||||
return klass;
|
||||
}
|
||||
@@ -320,8 +321,8 @@
|
||||
Kotlin.createObject = function (basesFun, constructor, functions, staticProperties) {
|
||||
constructor = constructor || function() {};
|
||||
function $o() {
|
||||
var klass = Kotlin.createClassNow(getBases(basesFun), null, functions, staticProperties);
|
||||
var obj = new klass();
|
||||
var klass = Kotlin.createClassNow(getBases(basesFun), constructor, functions, staticProperties);
|
||||
var obj = Object.create(klass.prototype);
|
||||
var metadata = klass.$metadata$;
|
||||
metadata.type = Kotlin.TYPE.OBJECT;
|
||||
Object.defineProperty(this, $o.className, {value: obj});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package foo
|
||||
|
||||
open class A
|
||||
|
||||
class B : A() {
|
||||
val a = 1
|
||||
}
|
||||
|
||||
object O
|
||||
|
||||
interface I
|
||||
|
||||
enum class E {
|
||||
X,
|
||||
Y {
|
||||
val a = 1
|
||||
},
|
||||
Z {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package foo
|
||||
|
||||
fun <T> check(x: JsClass<T>, y: JsClass<T>, shouldBeEquals: Boolean = true) {
|
||||
assertNotEquals(null, x)
|
||||
assertNotEquals(null, y)
|
||||
if (shouldBeEquals)
|
||||
assertEquals(x, y)
|
||||
else
|
||||
assertNotEquals(x, y)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
check(jsClass<A>(), A().jsClass)
|
||||
check(jsClass<B>(), B().jsClass)
|
||||
check(jsClass<O>(), O.jsClass)
|
||||
assertNotEquals(null, jsClass<I>())
|
||||
check(jsClass<E>(), E.X.jsClass)
|
||||
check(jsClass<E>(), E.Y.jsClass, shouldBeEquals = false)
|
||||
// TODO uncomment after KT-13338 is fixed
|
||||
// check(jsClass<E>(), E.Z.jsClass)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package foo
|
||||
|
||||
fun testWithInstance() {
|
||||
assertEquals("A", A().jsClass.name)
|
||||
assertEquals("B", B().jsClass.name)
|
||||
assertEquals("O", O.jsClass.name)
|
||||
assertEquals("E", E.X.jsClass.name)
|
||||
assertEquals("Y", E.Y.jsClass.name)
|
||||
// TODO uncomment after KT-13338 is fixed
|
||||
// assertEquals("E", E.Z.jsClass.name)
|
||||
}
|
||||
|
||||
fun testWithClassReference() {
|
||||
assertEquals("A", jsClass<A>().name)
|
||||
assertEquals("B", jsClass<B>().name)
|
||||
assertEquals("O", jsClass<O>().name)
|
||||
assertEquals("I", jsClass<I>().name)
|
||||
assertEquals("E", jsClass<E>().name)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
testWithInstance()
|
||||
testWithClassReference()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package foo
|
||||
|
||||
inline fun <reified T> foo(): JsClass<T> {
|
||||
val T = 1
|
||||
return jsClass<T>()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(jsClass<A>(), foo<A>())
|
||||
assertEquals(jsClass<B>(), foo<B>())
|
||||
assertEquals(jsClass<O>(), foo<O>())
|
||||
assertEquals(jsClass<E>(), foo<E>())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user