JS backend: added the support nativeInvoke, nativeGetter, nativeSetter.

This commit is contained in:
Zalim Bashorov
2014-11-24 18:25:08 +03:00
parent e6b69e48d9
commit b06096469a
8 changed files with 209 additions and 9 deletions
@@ -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) }
}
}
+7
View File
@@ -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();
}
}
@@ -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
};
};