[JS IR BE] Bridges construction

This commit is contained in:
Svyatoslav Kuzmich
2018-06-28 17:30:48 +03:00
parent a2f89cc711
commit 77ad97a39e
41 changed files with 217 additions and 46 deletions
@@ -89,6 +89,7 @@ fun JsIrBackendContext.lower(file: IrFile) {
PropertiesLowering().lower(file)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file)
MultipleCatchesLowering(this).lower(file)
BridgesConstruction(this).runOnFilePostfix(file)
TypeOperatorLowering(this).lower(file)
BlockDecomposerLowering(this).runOnFilePostfix(file)
SecondaryCtorLowering(this).runOnFilePostfix(file)
@@ -0,0 +1,191 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle
import org.jetbrains.kotlin.backend.common.bridges.generateBridges
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.isStatic
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.createParameterDeclarations
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
// Constructs bridges for inherited generic functions
//
// Example: for given class hierarchy
//
// class C<T> {
// fun foo(t: T) = ...
// }
//
// class D : C<Int> {
// override fun foo(t: Int) = impl
// }
//
// it adds method D that delegates generic calls to implementation:
//
// class D : C<Int> {
// override fun foo(t: Int) = impl
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
// }
//
class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
irClass.declarations
.filterIsInstance<IrSimpleFunction>()
.filter { !it.isStatic }
.forEach { generateBridges(it, irClass) }
irClass.declarations
.filterIsInstance<IrProperty>()
.flatMap { listOfNotNull(it.getter, it.setter) }
.forEach { generateBridges(it, irClass) }
}
private fun generateBridges(function: IrSimpleFunction, irClass: IrClass) {
// equals(Any?), hashCode(), toString() never need bridges
if (DescriptorUtils.isMethodOfAny(function.descriptor))
return
val bridgesToGenerate = generateBridges(
function = IrBasedFunctionHandle(function),
signature = { FunctionAndSignature(it.function) }
)
for ((from, to) in bridgesToGenerate) {
if (to.function.visibility == Visibilities.INVISIBLE_FAKE)
continue
if (!from.function.parentAsClass.isInterface &&
from.function.isReal &&
from.function.modality != Modality.ABSTRACT &&
!to.function.isReal
) {
continue
}
irClass.declarations.add(createBridge(function, from.function, to.function))
}
}
// Ported from from jvm.lower.BridgeLowering
private fun createBridge(
function: IrSimpleFunction,
bridge: IrSimpleFunction,
delegateTo: IrSimpleFunction
): IrFunction {
val containingClass = function.parentAsClass.descriptor
val bridgeDescriptorForIrFunction = SimpleFunctionDescriptorImpl.create(
containingClass,
Annotations.EMPTY,
bridge.name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
function.descriptor.source)
// TODO: should copy modality
bridgeDescriptorForIrFunction.initialize(
bridge.descriptor.extensionReceiverParameter?.returnType, containingClass.thisAsReceiverParameter,
bridge.descriptor.typeParameters,
bridge.descriptor.valueParameters.map { it.copy(bridgeDescriptorForIrFunction, it.name, it.index) },
bridge.descriptor.returnType, Modality.OPEN, function.visibility
)
// TODO: Support offsets for debug info
val irFunction = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, bridgeDescriptorForIrFunction)
irFunction.createParameterDeclarations()
// TODO: Add type casts
context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) {
val call = irCall(delegateTo.symbol)
call.dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!)
irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irGet(it)
}
irFunction.valueParameters.mapIndexed { i, valueParameter ->
call.putValueArgument(i, irGet(valueParameter))
}
+irReturn(call)
}.apply {
irFunction.body = this
}
return irFunction
}
}
// Handle for common.bridges
data class IrBasedFunctionHandle(val function: IrSimpleFunction) : FunctionHandle {
override val isDeclaration: Boolean = true
override val isAbstract: Boolean =
function.modality == Modality.ABSTRACT
override val isInterfaceDeclaration=
function.parentAsClass.isInterface
override fun getOverridden()=
function.overriddenSymbols.map { IrBasedFunctionHandle(it.owner) }
}
// Wrapper around function that compares and hashCodes it based on signature
// Designed to be used as a Signature type parameter in backend.common.bridges
class FunctionAndSignature(val function: IrSimpleFunction) {
// TODO: Use type-upper-bound-based signature instead of Strings
// Currently strings are used for compatibility with a hack-based name generator
private data class Signature(
val name: Name,
val extensionReceiverType: String?,
val valueParameters: List<String?>
)
private val signature = Signature(
function.name,
function.extensionReceiverParameter?.type?.toKotlinType()?.toString(),
function.valueParameters.map { it.type.toKotlinType()?.toString() }
)
override fun equals(other: Any?) =
signature == (other as FunctionAndSignature).signature
override fun hashCode(): Int = signature.hashCode()
}
@@ -34,6 +34,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
interface JvmLoweredStatementOrigin : IrStatementOrigin {
object DEFAULT_IMPLS_DELEGATION : IrStatementOriginImpl("DEFAULT_IMPL_DELEGATION")
object BRIDGE_DELEGATION : IrStatementOriginImpl("BRIDGE_DELEGATION")
object TO_ARRAY : IrDeclarationOriginImpl("TO_ARRAY")
}
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
import org.jetbrains.kotlin.backend.jvm.descriptors.DefaultImplsClassDescriptor
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmFunctionDescriptorImpl
import org.jetbrains.kotlin.codegen.AsmUtil.isAbstractMethod
@@ -37,6 +36,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
@@ -219,7 +219,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
implementation.returnType!!.toIrType()!!,
implementation,
implementation.typeParametersCount,
JvmLoweredStatementOrigin.BRIDGE_DELEGATION,
IrStatementOrigin.BRIDGE_DELEGATION,
if (isStubDeclarationWithDelegationToSuper) getSuperClassDescriptor(
descriptor.containingDeclaration as ClassDescriptor
) else null
@@ -228,14 +228,14 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
irFunction.dispatchReceiverParameter!!.symbol,
JvmLoweredStatementOrigin.BRIDGE_DELEGATION
IrStatementOrigin.BRIDGE_DELEGATION
)
irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = IrGetValueImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
it.symbol,
JvmLoweredStatementOrigin.BRIDGE_DELEGATION
IrStatementOrigin.BRIDGE_DELEGATION
)
}
irFunction.valueParameters.mapIndexed { i, valueParameter ->
@@ -245,7 +245,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
valueParameter.symbol,
JvmLoweredStatementOrigin.BRIDGE_DELEGATION
IrStatementOrigin.BRIDGE_DELEGATION
)
)
}
@@ -279,7 +279,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
bridgeFunction.valueParameters[i].symbol,
JvmLoweredStatementOrigin.BRIDGE_DELEGATION
IrStatementOrigin.BRIDGE_DELEGATION
)
if (delegateParameterTypes == null || OBJECT_TYPE == delegateParameterTypes[i]) {
irNotEquals(checkValue, irNull())
@@ -93,6 +93,8 @@ interface IrStatementOrigin {
object PROPERTY_REFERENCE_FOR_DELEGATE : IrStatementOriginImpl("PROPERTY_REFERENCE_FOR_DELEGATE")
object BRIDGE_DELEGATION : IrStatementOriginImpl("BRIDGE_DELEGATION")
data class COMPONENT_N private constructor(val index: Int) : IrStatementOriginImpl("COMPONENT_$index") {
companion object {
private val precreatedComponents = Array(32) { i -> COMPONENT_N(i + 1) }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T, U> {
fun foo(t: T, u: U) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// KT-3985
interface Trait<T> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T): String
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T, U, V> {
open fun foo(t: T, u: U, v: V) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T, U> {
fun foo(t: T, u: U) = "A"
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun <U> foo(t: T, u: U) = "A"
}
-2
View File
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T : Number> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
abstract class A<T> {
abstract fun foo(t: T): String
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T : U, U> {
open fun foo(t: T, u: U) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun id(t: T): T
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
public open class A<T> {
fun foo(x: T) = "O"
fun foo(x: A<T>) = "K"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun <U> foo(t: T, u: U) = "A"
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
open class A<T : Number> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T) = "A"
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
interface A<T> {
fun foo(t: T): String
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
interface A<T: Number> {
fun foo(t: T): String
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
var GUEST_USER_ID = 3
val USER_ID =
try {
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A {
fun foo() {}
fun bar(f: A.() -> Unit = {}) {}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
abstract class A<T> {
abstract fun test(a: T, b:Boolean = false) : String
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
fun unsupportedEx() {
if (true) throw UnsupportedOperationException()
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
fun unsupportedEx() {
if (true) throw UnsupportedOperationException()
}
-2
View File
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
package org.example
interface SomeTrait {}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A(
val a: String = {
open class B() {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
var global = 0
fun sideEffect() = global++
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isAny;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.*;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
@@ -634,4 +635,15 @@ public class DescriptorUtils {
: descriptor;
}
public static boolean isMethodOfAny(@NotNull FunctionDescriptor descriptor) {
String name = descriptor.getName().asString();
List<ValueParameterDescriptor> parameters = descriptor.getValueParameters();
if (parameters.isEmpty()) {
return name.equals("hashCode") || name.equals("toString");
}
else if (parameters.size() == 1 && name.equals("equals")) {
return isNullableAny(parameters.get(0).getType());
}
return false;
}
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1118
// This test was adapted from compiler/testData/codegen/box/classes
package foo
@@ -49,6 +49,11 @@ open class AssertionError(message: String?, cause: Throwable?) : Exception(messa
}
open class UnsupportedOperationException(message: String?, cause: Throwable?) : RuntimeException(message, cause) {
constructor() : this(null, null)
constructor(message: String?) : this(message, null)
constructor(cause: Throwable?) : this(null, cause)
}
// TODO: fix function names to satisfy style convention (depends on built-in names)
fun THROW_CCE() {