JS backend: added the support nativeInvoke, nativeGetter, nativeSetter.
This commit is contained in:
@@ -16,7 +16,17 @@
|
||||
|
||||
package org.jetbrains.k2js
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public enum class PredefinedAnnotation(public val fqName: String) {
|
||||
LIBRARY : PredefinedAnnotation("kotlin.js.library")
|
||||
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
|
||||
public annotation class native(public val name: String = "")
|
||||
native
|
||||
public annotation class nativeGetter
|
||||
native
|
||||
public annotation class nativeSetter
|
||||
native
|
||||
public annotation class nativeInvoke
|
||||
|
||||
native
|
||||
public annotation class library(public val name: String = "")
|
||||
native
|
||||
|
||||
@@ -134,4 +134,12 @@ public final class NativeInteropTest extends SingleFileTranslationTest {
|
||||
public void testPrint() throws Exception {
|
||||
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.JsNameRef
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation
|
||||
import java.util.Collections
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.k2js.translate.context.Namer
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import com.google.dart.compiler.backend.js.ast.JsNew
|
||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
||||
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.JsName
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext
|
||||
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.psi.Call.CallType
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayAccess
|
||||
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> {
|
||||
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 {
|
||||
fun canApply(callInfo: FunctionCallInfo): Boolean {
|
||||
if (!callInfo.callableDescriptor.getName().asString().equals("invoke"))
|
||||
@@ -212,6 +236,14 @@ fun FunctionCallInfo.translateFunctionCall(): JsExpression {
|
||||
return when {
|
||||
intrinsic != null ->
|
||||
intrinsic
|
||||
|
||||
NativeInvokeCallCase.canApply(this) ->
|
||||
NativeInvokeCallCase.translate(this)
|
||||
NativeGetterCallCase.canApply(this) ->
|
||||
NativeGetterCallCase.translate(this)
|
||||
NativeSetterCallCase.canApply(this) ->
|
||||
NativeSetterCallCase.translate(this)
|
||||
|
||||
InvokeIntrinsic.canApply(this) ->
|
||||
InvokeIntrinsic.translate(this)
|
||||
ConstructorCallCase.canApply(this) ->
|
||||
|
||||
@@ -40,8 +40,10 @@ public final class AnnotationsUtils {
|
||||
private AnnotationsUtils() {
|
||||
}
|
||||
|
||||
private static boolean hasAnnotation(@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull PredefinedAnnotation annotation) {
|
||||
public static boolean hasAnnotation(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull PredefinedAnnotation annotation
|
||||
) {
|
||||
return getAnnotationByName(descriptor, annotation) != null;
|
||||
}
|
||||
|
||||
@@ -95,7 +97,7 @@ public final class AnnotationsUtils {
|
||||
}
|
||||
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
|
||||
for (PredefinedAnnotation annotation : PredefinedAnnotation.OBJECT$.getWITH_CUSTOM_NAME()) {
|
||||
if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
|
||||
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