[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.
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,8 @@ class CallsLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
EnumIntrinsicsTransformer(context),
|
||||
ExceptionHelperCallsTransformer(context),
|
||||
BuiltInConstructorCalls(context),
|
||||
JsonIntrinsics(context)
|
||||
JsonIntrinsics(context),
|
||||
NativeGetterSetterTransformer(context),
|
||||
)
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -14,38 +14,14 @@ import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
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 {
|
||||
|
||||
fun generateMemberAccess(): IrExpression {
|
||||
val obj = call.dispatchReceiver!!
|
||||
val propertyName = call.getValueArgument(0)!!
|
||||
return IrDynamicOperatorExpressionImpl(
|
||||
call.startOffset,
|
||||
call.endOffset,
|
||||
context.irBuiltIns.anyNType,
|
||||
operator = IrDynamicOperator.ARRAY_ACCESS
|
||||
).also {
|
||||
it.receiver = obj
|
||||
it.arguments.add(propertyName)
|
||||
}
|
||||
return when (call.symbol.owner.fqNameWhenAvailable) {
|
||||
FqName("kotlin.js.Json.get") -> call.transformToIndexedRead()
|
||||
FqName("kotlin.js.Json.set") -> call.transformToIndexedWrite()
|
||||
else -> call
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -62,7 +62,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
|
||||
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
|
||||
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return false
|
||||
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return false
|
||||
@@ -101,8 +101,8 @@ fun translateCall(
|
||||
}
|
||||
}
|
||||
|
||||
if (isNativeInvoke(jsDispatchReceiver, expression)) {
|
||||
return JsInvocation(jsDispatchReceiver!!, arguments)
|
||||
if (isFunctionTypeInvoke(jsDispatchReceiver, expression) || expression.symbol.owner.isJsNativeInvoke()) {
|
||||
return JsInvocation(jsDispatchReceiver ?: jsExtensionReceiver!!, arguments)
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,9 @@ object JsAnnotations {
|
||||
val jsNameFqn = FqName("kotlin.js.JsName")
|
||||
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
||||
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")
|
||||
@@ -42,6 +45,12 @@ fun IrAnnotationContainer.getJsName(): String? =
|
||||
fun IrAnnotationContainer.isJsExport(): Boolean =
|
||||
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 =
|
||||
when (val jsName = getJsName()) {
|
||||
null -> name
|
||||
@@ -57,4 +66,4 @@ fun IrConstructorCall.associatedObject(): IrClass? {
|
||||
if (!symbol.owner.constructedClass.isAssociatedObjectAnnotatedAnnotation) return null
|
||||
val klass = ((getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)?.owner ?: return 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
|
||||
package foo
|
||||
|
||||
@@ -91,4 +89,4 @@ fun box(): String {
|
||||
testExtensions(b, 5, undefined, t)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
+17
-15
@@ -1,40 +1,42 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS_IR_ES6
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
// EXPECTED_REACHABLE_NODES: 1244
|
||||
package foo
|
||||
|
||||
external class Function(vararg argsAndCode: String) {
|
||||
external fun Function(vararg argsAndCode: String): Function
|
||||
|
||||
external interface Function {
|
||||
@nativeInvoke
|
||||
operator fun invoke(a: Any?): Any? = definedExternally
|
||||
operator fun invoke(a: Any?): Any?
|
||||
|
||||
@nativeInvoke
|
||||
fun baz(a: Any?, b: Any?): Any? = definedExternally
|
||||
fun baz(a: Any?, b: Any? = definedExternally, c: Any? = definedExternally): Any?
|
||||
}
|
||||
|
||||
@nativeInvoke
|
||||
operator fun Function.invoke(a: Any?, b: Any?): Any? = definedExternally
|
||||
|
||||
@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{}
|
||||
|
||||
fun box(): String {
|
||||
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("ok", f("ok"))
|
||||
assertEquals(t, f(t))
|
||||
|
||||
assertEquals(5, g(1, 4))
|
||||
assertEquals("ok34", g("ok", 34))
|
||||
assertEquals(105, g(1, 4))
|
||||
assertEquals("ok34100", g("ok", 34))
|
||||
|
||||
assertEquals(5, g.baz(1, 4))
|
||||
assertEquals("ok34", g.baz("ok", 34))
|
||||
assertEquals(105, g.baz(1, 4))
|
||||
assertEquals("ok34100", g.baz("ok", 34))
|
||||
assertEquals("ok1034", g.baz("ok", c = 34))
|
||||
|
||||
assertEquals(5, g.bar(1, 4))
|
||||
assertEquals("ok34", g.bar("ok", 34))
|
||||
assertEquals(105, g.bar(1, 4))
|
||||
assertEquals("ok34100", g.bar("ok", 34))
|
||||
assertEquals("ok1034", g.bar("ok", c = 34))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user