[JS IR] Support nativeGetter, nativeSetter and nativeInvoke
#KT-41081 fixed
This commit is contained in:
+3
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -28,7 +28,8 @@ class CallsLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
|||||||
EnumIntrinsicsTransformer(context),
|
EnumIntrinsicsTransformer(context),
|
||||||
ExceptionHelperCallsTransformer(context),
|
ExceptionHelperCallsTransformer(context),
|
||||||
BuiltInConstructorCalls(context),
|
BuiltInConstructorCalls(context),
|
||||||
JsonIntrinsics(context)
|
JsonIntrinsics(context),
|
||||||
|
NativeGetterSetterTransformer(context),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
|
|||||||
+7
-31
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -14,38 +14,14 @@ import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
|
||||||
class JsonIntrinsics(val context: JsIrBackendContext) : CallsTransformer {
|
// TODO use nativeX annotations on kotlin.js.Json instead
|
||||||
|
class JsonIntrinsics(context: JsIrBackendContext) : NativeGetterSetterTransformer(context) {
|
||||||
|
|
||||||
override fun transformFunctionAccess(call: IrFunctionAccessExpression): IrExpression {
|
override fun transformFunctionAccess(call: IrFunctionAccessExpression): IrExpression {
|
||||||
|
return when (call.symbol.owner.fqNameWhenAvailable) {
|
||||||
fun generateMemberAccess(): IrExpression {
|
FqName("kotlin.js.Json.get") -> call.transformToIndexedRead()
|
||||||
val obj = call.dispatchReceiver!!
|
FqName("kotlin.js.Json.set") -> call.transformToIndexedWrite()
|
||||||
val propertyName = call.getValueArgument(0)!!
|
else -> call
|
||||||
return IrDynamicOperatorExpressionImpl(
|
|
||||||
call.startOffset,
|
|
||||||
call.endOffset,
|
|
||||||
context.irBuiltIns.anyNType,
|
|
||||||
operator = IrDynamicOperator.ARRAY_ACCESS
|
|
||||||
).also {
|
|
||||||
it.receiver = obj
|
|
||||||
it.arguments.add(propertyName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when (call.symbol.owner.fqNameWhenAvailable) {
|
|
||||||
FqName("kotlin.js.Json.get") ->
|
|
||||||
return generateMemberAccess()
|
|
||||||
|
|
||||||
FqName("kotlin.js.Json.set") -> {
|
|
||||||
val value = call.getValueArgument(1)!!
|
|
||||||
|
|
||||||
return IrDynamicOperatorExpressionImpl(call.startOffset, call.endOffset, call.type, IrDynamicOperator.EQ).also {
|
|
||||||
it.receiver = generateMemberAccess()
|
|
||||||
it.arguments.add(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return call
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.isJsNativeGetter
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.isJsNativeSetter
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrDynamicOperator
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl
|
||||||
|
|
||||||
|
|
||||||
|
open class NativeGetterSetterTransformer(val context: JsIrBackendContext) : CallsTransformer {
|
||||||
|
override fun transformFunctionAccess(call: IrFunctionAccessExpression): IrExpression {
|
||||||
|
val callee = call.symbol.owner
|
||||||
|
|
||||||
|
return when {
|
||||||
|
callee.isJsNativeGetter() -> call.transformToIndexedRead()
|
||||||
|
callee.isJsNativeSetter() -> call.transformToIndexedWrite()
|
||||||
|
// @nativeInvoke is supported separately to simplify processing default arguments,
|
||||||
|
// it's harder to support using dynamic operator since the last one doesn't allow arguments with holes.
|
||||||
|
// The feature is implemented in `translateCall` in ir/backend/js/transformers/irToJs/jsAstUtils.kt
|
||||||
|
// callee.isJsNativeInvoke() -> {}
|
||||||
|
else -> call
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun IrFunctionAccessExpression.transformToIndexedRead(): IrExpression {
|
||||||
|
val obj = dispatchReceiver ?: extensionReceiver!!
|
||||||
|
val propertyName = getValueArgument(0)!!
|
||||||
|
return IrDynamicOperatorExpressionImpl(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
context.irBuiltIns.anyNType,
|
||||||
|
operator = IrDynamicOperator.ARRAY_ACCESS
|
||||||
|
).also {
|
||||||
|
it.receiver = obj
|
||||||
|
it.arguments.add(propertyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun IrFunctionAccessExpression.transformToIndexedWrite(): IrExpression {
|
||||||
|
val value = getValueArgument(1)!!
|
||||||
|
|
||||||
|
return IrDynamicOperatorExpressionImpl(startOffset, endOffset, type, IrDynamicOperator.EQ).also {
|
||||||
|
it.receiver = transformToIndexedRead()
|
||||||
|
it.arguments.add(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
|
|||||||
return function
|
return function
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNativeInvoke(receiver: JsExpression?, call: IrCall): Boolean {
|
private fun isFunctionTypeInvoke(receiver: JsExpression?, call: IrCall): Boolean {
|
||||||
if (receiver == null || receiver is JsThisRef) return false
|
if (receiver == null || receiver is JsThisRef) return false
|
||||||
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return false
|
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return false
|
||||||
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return false
|
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return false
|
||||||
@@ -101,8 +101,8 @@ fun translateCall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNativeInvoke(jsDispatchReceiver, expression)) {
|
if (isFunctionTypeInvoke(jsDispatchReceiver, expression) || expression.symbol.owner.isJsNativeInvoke()) {
|
||||||
return JsInvocation(jsDispatchReceiver!!, arguments)
|
return JsInvocation(jsDispatchReceiver ?: jsExtensionReceiver!!, arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
expression.superQualifierSymbol?.let { superQualifier ->
|
expression.superQualifierSymbol?.let { superQualifier ->
|
||||||
|
|||||||
+11
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -21,6 +21,9 @@ object JsAnnotations {
|
|||||||
val jsNameFqn = FqName("kotlin.js.JsName")
|
val jsNameFqn = FqName("kotlin.js.JsName")
|
||||||
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
||||||
val jsExportFqn = FqName("kotlin.js.JsExport")
|
val jsExportFqn = FqName("kotlin.js.JsExport")
|
||||||
|
val jsNativeGetter = FqName("kotlin.js.nativeGetter")
|
||||||
|
val jsNativeSetter = FqName("kotlin.js.nativeSetter")
|
||||||
|
val jsNativeInvoke = FqName("kotlin.js.nativeInvoke")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -42,6 +45,12 @@ fun IrAnnotationContainer.getJsName(): String? =
|
|||||||
fun IrAnnotationContainer.isJsExport(): Boolean =
|
fun IrAnnotationContainer.isJsExport(): Boolean =
|
||||||
hasAnnotation(JsAnnotations.jsExportFqn)
|
hasAnnotation(JsAnnotations.jsExportFqn)
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.isJsNativeGetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeGetter)
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.isJsNativeSetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeSetter)
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.isJsNativeInvoke(): Boolean = hasAnnotation(JsAnnotations.jsNativeInvoke)
|
||||||
|
|
||||||
fun IrDeclarationWithName.getJsNameOrKotlinName(): Name =
|
fun IrDeclarationWithName.getJsNameOrKotlinName(): Name =
|
||||||
when (val jsName = getJsName()) {
|
when (val jsName = getJsName()) {
|
||||||
null -> name
|
null -> name
|
||||||
@@ -57,4 +66,4 @@ fun IrConstructorCall.associatedObject(): IrClass? {
|
|||||||
if (!symbol.owner.constructedClass.isAssociatedObjectAnnotatedAnnotation) return null
|
if (!symbol.owner.constructedClass.isAssociatedObjectAnnotatedAnnotation) return null
|
||||||
val klass = ((getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)?.owner ?: return null
|
val klass = ((getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)?.owner ?: return null
|
||||||
return if (klass.isObject) klass else null
|
return if (klass.isObject) klass else null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1291
|
// EXPECTED_REACHABLE_NODES: 1291
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
@@ -91,4 +89,4 @@ fun box(): String {
|
|||||||
testExtensions(b, 5, undefined, t)
|
testExtensions(b, 5, undefined, t)
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-15
@@ -1,40 +1,42 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
// EXPECTED_REACHABLE_NODES: 1244
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1286
|
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
external class Function(vararg argsAndCode: String) {
|
external fun Function(vararg argsAndCode: String): Function
|
||||||
|
|
||||||
|
external interface Function {
|
||||||
@nativeInvoke
|
@nativeInvoke
|
||||||
operator fun invoke(a: Any?): Any? = definedExternally
|
operator fun invoke(a: Any?): Any?
|
||||||
|
|
||||||
@nativeInvoke
|
@nativeInvoke
|
||||||
fun baz(a: Any?, b: Any?): Any? = definedExternally
|
fun baz(a: Any?, b: Any? = definedExternally, c: Any? = definedExternally): Any?
|
||||||
}
|
}
|
||||||
|
|
||||||
@nativeInvoke
|
@nativeInvoke
|
||||||
operator fun Function.invoke(a: Any?, b: Any?): Any? = definedExternally
|
operator fun Function.invoke(a: Any?, b: Any?): Any? = definedExternally
|
||||||
|
|
||||||
@nativeInvoke
|
@nativeInvoke
|
||||||
fun Function.bar(a: Any?, b: Any?): Any? = definedExternally
|
fun Function.bar(a: Any?, b: Any? = definedExternally, c: Any? = definedExternally): Any? = definedExternally
|
||||||
|
|
||||||
object t{}
|
object t{}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val f = Function("a", "return a")
|
val f = Function("a", "return a")
|
||||||
val g = Function("a", "b", "return a + b")
|
val g = Function("a", "b", "c", "return a + (b || 10) + (c || 100)")
|
||||||
|
|
||||||
assertEquals(1, f(1))
|
assertEquals(1, f(1))
|
||||||
assertEquals("ok", f("ok"))
|
assertEquals("ok", f("ok"))
|
||||||
assertEquals(t, f(t))
|
assertEquals(t, f(t))
|
||||||
|
|
||||||
assertEquals(5, g(1, 4))
|
assertEquals(105, g(1, 4))
|
||||||
assertEquals("ok34", g("ok", 34))
|
assertEquals("ok34100", g("ok", 34))
|
||||||
|
|
||||||
assertEquals(5, g.baz(1, 4))
|
assertEquals(105, g.baz(1, 4))
|
||||||
assertEquals("ok34", g.baz("ok", 34))
|
assertEquals("ok34100", g.baz("ok", 34))
|
||||||
|
assertEquals("ok1034", g.baz("ok", c = 34))
|
||||||
|
|
||||||
assertEquals(5, g.bar(1, 4))
|
assertEquals(105, g.bar(1, 4))
|
||||||
assertEquals("ok34", g.bar("ok", 34))
|
assertEquals("ok34100", g.bar("ok", 34))
|
||||||
|
assertEquals("ok1034", g.bar("ok", c = 34))
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user