JS backend: added the support nativeInvoke, nativeGetter, nativeSetter.
This commit is contained in:
@@ -16,7 +16,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.k2js
|
package org.jetbrains.k2js
|
||||||
|
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
public enum class PredefinedAnnotation(public val fqName: String) {
|
public enum class PredefinedAnnotation(public val fqName: String) {
|
||||||
LIBRARY : PredefinedAnnotation("kotlin.js.library")
|
LIBRARY : PredefinedAnnotation("kotlin.js.library")
|
||||||
NATIVE : PredefinedAnnotation("kotlin.js.native")
|
NATIVE : PredefinedAnnotation("kotlin.js.native")
|
||||||
|
NATIVE_INVOKE : PredefinedAnnotation("kotlin.js.nativeInvoke")
|
||||||
|
NATIVE_GETTER : PredefinedAnnotation("kotlin.js.nativeGetter")
|
||||||
|
NATIVE_SETTER : PredefinedAnnotation("kotlin.js.nativeSetter")
|
||||||
|
|
||||||
|
class object {
|
||||||
|
// TODO: replace with straight assignment when KT-5761 will be fixed
|
||||||
|
val WITH_CUSTOM_NAME by Delegates.lazy { setOf(LIBRARY, NATIVE) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ package kotlin.js
|
|||||||
|
|
||||||
native
|
native
|
||||||
public annotation class native(public val name: String = "")
|
public annotation class native(public val name: String = "")
|
||||||
|
native
|
||||||
|
public annotation class nativeGetter
|
||||||
|
native
|
||||||
|
public annotation class nativeSetter
|
||||||
|
native
|
||||||
|
public annotation class nativeInvoke
|
||||||
|
|
||||||
native
|
native
|
||||||
public annotation class library(public val name: String = "")
|
public annotation class library(public val name: String = "")
|
||||||
native
|
native
|
||||||
|
|||||||
@@ -134,4 +134,12 @@ public final class NativeInteropTest extends SingleFileTranslationTest {
|
|||||||
public void testPrint() throws Exception {
|
public void testPrint() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testNativeInvoke() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNativeGetterAndNativeSetter() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-6
@@ -19,24 +19,22 @@ package org.jetbrains.k2js.translate.callTranslator
|
|||||||
import com.google.dart.compiler.backend.js.ast.JsExpression
|
import com.google.dart.compiler.backend.js.ast.JsExpression
|
||||||
import com.google.dart.compiler.backend.js.ast.JsNameRef
|
import com.google.dart.compiler.backend.js.ast.JsNameRef
|
||||||
import com.google.dart.compiler.backend.js.ast.JsInvocation
|
import com.google.dart.compiler.backend.js.ast.JsInvocation
|
||||||
import java.util.Collections
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import org.jetbrains.k2js.translate.context.Namer
|
import org.jetbrains.k2js.translate.context.Namer
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||||
import com.google.dart.compiler.backend.js.ast.JsNew
|
import com.google.dart.compiler.backend.js.ast.JsNew
|
||||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
import org.jetbrains.k2js.translate.general.Translation
|
|
||||||
import com.google.dart.compiler.backend.js.ast.JsLiteral
|
import com.google.dart.compiler.backend.js.ast.JsLiteral
|
||||||
import com.google.dart.compiler.backend.js.ast.JsName
|
import com.google.dart.compiler.backend.js.ast.JsName
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext
|
import org.jetbrains.k2js.translate.context.TranslationContext
|
||||||
import org.jetbrains.k2js.translate.reference.CallArgumentTranslator
|
import org.jetbrains.k2js.translate.reference.CallArgumentTranslator
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
|
|
||||||
import org.jetbrains.jet.lang.descriptors.Visibilities
|
import org.jetbrains.jet.lang.descriptors.Visibilities
|
||||||
import org.jetbrains.jet.lang.psi.Call.CallType
|
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
|
import com.google.dart.compiler.backend.js.ast.JsArrayAccess
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
import org.jetbrains.k2js.translate.utils.JsAstUtils
|
||||||
|
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
|
||||||
|
import org.jetbrains.k2js.PredefinedAnnotation
|
||||||
|
|
||||||
public fun addReceiverToArgs(receiver: JsExpression, arguments: List<JsExpression>): List<JsExpression> {
|
public fun addReceiverToArgs(receiver: JsExpression, arguments: List<JsExpression>): List<JsExpression> {
|
||||||
if (arguments.isEmpty())
|
if (arguments.isEmpty())
|
||||||
@@ -140,6 +138,32 @@ object DelegateFunctionIntrinsic : DelegateIntrinsic<FunctionCallInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class AnnotatedAsNativeXCallCase(val annotation: PredefinedAnnotation) : FunctionCallCase {
|
||||||
|
abstract fun translateCall(receiver: JsExpression, argumentsInfo: CallArgumentTranslator.ArgumentsInfo): JsExpression
|
||||||
|
|
||||||
|
fun canApply(callInfo: FunctionCallInfo): Boolean = AnnotationsUtils.hasAnnotation(callInfo.callableDescriptor, annotation)
|
||||||
|
|
||||||
|
final override fun FunctionCallInfo.dispatchReceiver() = translateCall(dispatchReceiver!!, argumentsInfo)
|
||||||
|
final override fun FunctionCallInfo.extensionReceiver() = translateCall(extensionReceiver!!, argumentsInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
object NativeInvokeCallCase : AnnotatedAsNativeXCallCase(PredefinedAnnotation.NATIVE_INVOKE) {
|
||||||
|
override fun translateCall(receiver: JsExpression, argumentsInfo: CallArgumentTranslator.ArgumentsInfo) =
|
||||||
|
JsInvocation(receiver, argumentsInfo.getTranslateArguments())
|
||||||
|
}
|
||||||
|
|
||||||
|
object NativeGetterCallCase : AnnotatedAsNativeXCallCase(PredefinedAnnotation.NATIVE_GETTER) {
|
||||||
|
override fun translateCall(receiver: JsExpression, argumentsInfo: CallArgumentTranslator.ArgumentsInfo) =
|
||||||
|
JsArrayAccess(receiver, argumentsInfo.getTranslateArguments()[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
object NativeSetterCallCase : AnnotatedAsNativeXCallCase(PredefinedAnnotation.NATIVE_SETTER) {
|
||||||
|
override fun translateCall(receiver: JsExpression, argumentsInfo: CallArgumentTranslator.ArgumentsInfo): JsExpression {
|
||||||
|
val args = argumentsInfo.getTranslateArguments()
|
||||||
|
return JsAstUtils.assignment(JsArrayAccess(receiver, args[0]), args[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object InvokeIntrinsic : FunctionCallCase {
|
object InvokeIntrinsic : FunctionCallCase {
|
||||||
fun canApply(callInfo: FunctionCallInfo): Boolean {
|
fun canApply(callInfo: FunctionCallInfo): Boolean {
|
||||||
if (!callInfo.callableDescriptor.getName().asString().equals("invoke"))
|
if (!callInfo.callableDescriptor.getName().asString().equals("invoke"))
|
||||||
@@ -212,6 +236,14 @@ fun FunctionCallInfo.translateFunctionCall(): JsExpression {
|
|||||||
return when {
|
return when {
|
||||||
intrinsic != null ->
|
intrinsic != null ->
|
||||||
intrinsic
|
intrinsic
|
||||||
|
|
||||||
|
NativeInvokeCallCase.canApply(this) ->
|
||||||
|
NativeInvokeCallCase.translate(this)
|
||||||
|
NativeGetterCallCase.canApply(this) ->
|
||||||
|
NativeGetterCallCase.translate(this)
|
||||||
|
NativeSetterCallCase.canApply(this) ->
|
||||||
|
NativeSetterCallCase.translate(this)
|
||||||
|
|
||||||
InvokeIntrinsic.canApply(this) ->
|
InvokeIntrinsic.canApply(this) ->
|
||||||
InvokeIntrinsic.translate(this)
|
InvokeIntrinsic.translate(this)
|
||||||
ConstructorCallCase.canApply(this) ->
|
ConstructorCallCase.canApply(this) ->
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ public final class AnnotationsUtils {
|
|||||||
private AnnotationsUtils() {
|
private AnnotationsUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasAnnotation(@NotNull DeclarationDescriptor descriptor,
|
public static boolean hasAnnotation(
|
||||||
@NotNull PredefinedAnnotation annotation) {
|
@NotNull DeclarationDescriptor descriptor,
|
||||||
|
@NotNull PredefinedAnnotation annotation
|
||||||
|
) {
|
||||||
return getAnnotationByName(descriptor, annotation) != null;
|
return getAnnotationByName(descriptor, annotation) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ public final class AnnotationsUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (DeclarationDescriptor descriptor : descriptors) {
|
for (DeclarationDescriptor descriptor : descriptors) {
|
||||||
for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
|
for (PredefinedAnnotation annotation : PredefinedAnnotation.OBJECT$.getWITH_CUSTOM_NAME()) {
|
||||||
if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
|
if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
native("Object")
|
||||||
|
class JsObject {
|
||||||
|
nativeGetter
|
||||||
|
fun get(a: String): Any? = noImpl
|
||||||
|
|
||||||
|
nativeSetter
|
||||||
|
fun set(a: String, v: Any?): Unit = noImpl
|
||||||
|
|
||||||
|
nativeGetter
|
||||||
|
fun take(a: Int): Any? = noImpl
|
||||||
|
|
||||||
|
nativeSetter
|
||||||
|
fun put(a: Int, v: Any?): Unit = noImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeGetter
|
||||||
|
fun JsObject.get(a: Int): Any? = noImpl
|
||||||
|
|
||||||
|
nativeSetter
|
||||||
|
fun JsObject.set(a: Int, v: Any?): Unit = noImpl
|
||||||
|
|
||||||
|
nativeGetter
|
||||||
|
fun JsObject.take(a: String): Any? = noImpl
|
||||||
|
|
||||||
|
nativeSetter
|
||||||
|
fun JsObject.put(a: String, v: Any?): Unit = noImpl
|
||||||
|
|
||||||
|
|
||||||
|
object t{}
|
||||||
|
|
||||||
|
native
|
||||||
|
val undefined: Any = noImpl
|
||||||
|
|
||||||
|
native
|
||||||
|
fun getTestObject(): JsObject = noImpl
|
||||||
|
|
||||||
|
fun test(obj: JsObject, key: String, oldValue: Any, newValue: Any) {
|
||||||
|
assertEquals(oldValue, obj[key])
|
||||||
|
obj[key] = newValue
|
||||||
|
assertEquals(newValue, obj[key])
|
||||||
|
obj[key] = null
|
||||||
|
assertEquals(null, obj[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(obj: JsObject, key: Int, oldValue: Any, newValue: Any) {
|
||||||
|
assertEquals(oldValue, obj.take(key))
|
||||||
|
obj.put(key, newValue)
|
||||||
|
assertEquals(newValue, obj.take(key))
|
||||||
|
obj.put(key, null)
|
||||||
|
assertEquals(null, obj.take(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testExtensions(obj: JsObject, key: Int, oldValue: Any, newValue: Any) {
|
||||||
|
assertEquals(oldValue, obj[key])
|
||||||
|
obj[key] = newValue
|
||||||
|
assertEquals(newValue, obj[key])
|
||||||
|
obj[key] = null
|
||||||
|
assertEquals(null, obj[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testExtensions(obj: JsObject, key: String, oldValue: Any, newValue: Any) {
|
||||||
|
assertEquals(oldValue, obj.take(key))
|
||||||
|
obj.put(key, newValue)
|
||||||
|
assertEquals(newValue, obj.take(key))
|
||||||
|
obj.put(key, null)
|
||||||
|
assertEquals(null, obj.take(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a = getTestObject()
|
||||||
|
|
||||||
|
test(a, "foo", "boo", "moo")
|
||||||
|
test(a, "bar", 35, 67)
|
||||||
|
test(a, "baz", undefined, 34)
|
||||||
|
test(a, "qoox", undefined, t)
|
||||||
|
test(a, 0, "ok", "OK!")
|
||||||
|
test(a, 1, 2, 3)
|
||||||
|
test(a, 2, undefined, "HI")
|
||||||
|
test(a, 5, undefined, t)
|
||||||
|
|
||||||
|
val b = getTestObject()
|
||||||
|
|
||||||
|
testExtensions(b, "foo", "boo", "moo")
|
||||||
|
testExtensions(b, "bar", 35, 67)
|
||||||
|
testExtensions(b, "baz", undefined, 34)
|
||||||
|
testExtensions(b, "qoox", undefined, t)
|
||||||
|
testExtensions(b, 0, "ok", "OK!")
|
||||||
|
testExtensions(b, 1, 2, 3)
|
||||||
|
testExtensions(b, 2, undefined, "HI")
|
||||||
|
testExtensions(b, 5, undefined, t)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
native
|
||||||
|
class Function(vararg argsAndCode: String) {
|
||||||
|
nativeInvoke
|
||||||
|
fun invoke(a: Any?): Any? = noImpl
|
||||||
|
|
||||||
|
nativeInvoke
|
||||||
|
fun baz(a: Any?, b: Any?): Any? = noImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeInvoke
|
||||||
|
fun Function.invoke(a: Any?, b: Any?): Any? = noImpl
|
||||||
|
|
||||||
|
nativeInvoke
|
||||||
|
fun Function.bar(a: Any?, b: Any?): Any? = noImpl
|
||||||
|
|
||||||
|
object t{}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val f = Function("a", "return a")
|
||||||
|
val g = Function("a", "b", "return a + b")
|
||||||
|
|
||||||
|
assertEquals(1, f(1))
|
||||||
|
assertEquals("ok", f("ok"))
|
||||||
|
assertEquals(t, f(t))
|
||||||
|
|
||||||
|
assertEquals(5, g(1, 4))
|
||||||
|
assertEquals("ok34", g("ok", 34))
|
||||||
|
|
||||||
|
assertEquals(5, g.baz(1, 4))
|
||||||
|
assertEquals("ok34", g.baz("ok", 34))
|
||||||
|
|
||||||
|
assertEquals(5, g.bar(1, 4))
|
||||||
|
assertEquals("ok34", g.bar("ok", 34))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
function getTestObject() {
|
||||||
|
return {
|
||||||
|
"foo" : "boo",
|
||||||
|
"bar" : 35,
|
||||||
|
0 : "ok",
|
||||||
|
1 : 2
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user