[EE-IR] Use reflection to avoid the need for accessors when compiling
evaluator fragments - Refactor JvmIrCodegenFactory to allow for caller's choice of lowerings. - Refactor of SyntheticAccessorLowering to expose the accessibility check used to determine the need for accessors. - Replacement of field, method and constructor uses with corresponding java.lang.reflection API via additional lowering running _before_ the existing IR pipeline. - Emulating super calls in the context of class methods via new compiler intrinsic. - psi2ir: Add support for `_field` suffix on properties in the evaluator for accessing underlying fields when applicable. - Add `JvmGeneratorExtensions` point for determining whether a property accessor has a user-supplied body in order to predict how they are ultimately compiled for the JVM.
This commit is contained in:
committed by
Alexander Udalov
parent
2e535366f1
commit
771b79d045
+13
-4
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.backend.jvm
|
||||
import org.jetbrains.kotlin.analyzer.hasJdkCapability
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
|
||||
import org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.backend.common.phaser.then
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
|
||||
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
|
||||
import org.jetbrains.kotlin.codegen.CodegenFactory
|
||||
@@ -48,7 +50,8 @@ open class JvmIrCodegenFactory(
|
||||
private val externalMangler: JvmDescriptorMangler? = null,
|
||||
private val externalSymbolTable: SymbolTable? = null,
|
||||
private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration),
|
||||
private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null
|
||||
private val prefixPhases: NamedCompilerPhase<JvmBackendContext, IrModuleFragment>? = null,
|
||||
private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null,
|
||||
) : CodegenFactory {
|
||||
data class JvmIrBackendInput(
|
||||
val irModuleFragment: IrModuleFragment,
|
||||
@@ -160,7 +163,8 @@ open class JvmIrCodegenFactory(
|
||||
irProviders,
|
||||
pluginExtensions,
|
||||
expectDescriptorToSymbol = null,
|
||||
fragmentInfo = evaluatorFragmentInfoForPsi2Ir)
|
||||
fragmentInfo = evaluatorFragmentInfoForPsi2Ir
|
||||
)
|
||||
|
||||
irLinker.postProcess()
|
||||
|
||||
@@ -217,7 +221,8 @@ open class JvmIrCodegenFactory(
|
||||
)
|
||||
JvmIrSerializerImpl(state.configuration)
|
||||
else null
|
||||
val phaseConfig = customPhaseConfig ?: PhaseConfig(jvmPhases)
|
||||
val phases = prefixPhases?.then(jvmPhases) ?: jvmPhases
|
||||
val phaseConfig = customPhaseConfig ?: PhaseConfig(phases)
|
||||
val context = JvmBackendContext(
|
||||
state, irModuleFragment.irBuiltins, irModuleFragment, symbolTable, phaseConfig, extensions, backendExtension, irSerializer,
|
||||
notifyCodegenStart,
|
||||
@@ -227,7 +232,11 @@ open class JvmIrCodegenFactory(
|
||||
|
||||
context.state.factory.registerSourceFiles(irModuleFragment.files.map(IrFile::getKtFile))
|
||||
|
||||
jvmPhases.invokeToplevel(phaseConfig, context, irModuleFragment)
|
||||
phases.invokeToplevel(
|
||||
phaseConfig,
|
||||
context,
|
||||
irModuleFragment
|
||||
)
|
||||
|
||||
// TODO: split classes into groups connected by inline calls; call this after every group
|
||||
// and clear `JvmBackendContext.classCodegens`
|
||||
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* 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.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineScopeResolver
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.findInlineCallSites
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.SyntheticAccessorLowering.Companion.isAccessible
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.starProjectedType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
|
||||
// Used from CodeFragmentCompiler for IDE Debugger Plug-In
|
||||
@Suppress("unused")
|
||||
val reflectiveAccessLowering = makeIrModulePhase(
|
||||
::ReflectiveAccessLowering,
|
||||
name = "ReflectiveCalls",
|
||||
description = "Avoid the need for accessors by replacing direct access to inaccessible members with accesses via reflection",
|
||||
prerequisite = setOf()
|
||||
)
|
||||
|
||||
// This lowering replaces member accesses that are illegal according to JVM
|
||||
// accessibility rules with corresponding calls to the java.lang.reflect
|
||||
// API. The primary use-case is to facilitate the design of the "Evaluate
|
||||
// expression..." mechanism in the JVM Debugger. Here, a code fragment is
|
||||
// compiled _as if_ in the context of a breakpoint. Hence, it is compiled
|
||||
// against an existing class hierarchy and any access to private or otherwise
|
||||
// inaccessible members that are "perceived" to be in scope must be
|
||||
// transformed. The ordinary IR pipeline would introduce an accessor next to the
|
||||
// access_ee_, but that is assumed to not be possible here: the accessee is
|
||||
// deserialized from class files that cannot be modified at this point.
|
||||
//
|
||||
// The lowering looks for the following member accesses and determines their
|
||||
// legality through the need for an accessor, had this been an ordinary
|
||||
// compilation:
|
||||
//
|
||||
// - {extension, static, super*} methods, {extension} property accessors,
|
||||
// functions on companion objects
|
||||
// - field accesses
|
||||
// - constructor invocations
|
||||
// - companion object access
|
||||
//
|
||||
// *super calls, private or not, are not allowed from outside the class
|
||||
// hierarchy of the involved classes, so is emulated in fragment compilation by
|
||||
// the use of `invokespecial` - see `invokeSpecialForCall` below.
|
||||
internal class ReflectiveAccessLowering(
|
||||
val context: JvmBackendContext
|
||||
) : IrElementTransformerVoidWithContext(), FileLoweringPass {
|
||||
|
||||
lateinit var inlineScopeResolver: IrInlineScopeResolver
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
inlineScopeResolver = irFile.findInlineCallSites(context)
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
// Wrapper for the logic from SyntheticAccessorLowering
|
||||
private fun IrSymbol.isAccessible(withSuper: Boolean = false): Boolean {
|
||||
return isAccessible(context, currentScope, inlineScopeResolver, withSuper, null)
|
||||
}
|
||||
|
||||
// Fragments are transformed in a post-order traversal: children first,
|
||||
// then parent. This obscures, in particular, dispatch receivers, that go
|
||||
// from `IrGetObjectValue` calls to blocks implementing the corresponding
|
||||
// reflective access . We record these _before_ transformation, in order to
|
||||
// later predict the compilation strategy for fields. See the uses of
|
||||
// `fieldLocationAndReceiver`.
|
||||
val callsOnCompanionObjects: MutableMap<IrCall,IrClassSymbol> = mutableMapOf()
|
||||
|
||||
private fun recordCompanionObjectAsDispatchReceiver(expression: IrCall) {
|
||||
if ((expression.dispatchReceiver as? IrGetObjectValue)?.symbol?.owner?.isCompanion == true) {
|
||||
callsOnCompanionObjects[expression] = (expression.dispatchReceiver!! as IrGetObjectValue).symbol
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment traversal
|
||||
*/
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
recordCompanionObjectAsDispatchReceiver(expression)
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val withSuper = expression.superQualifierSymbol != null
|
||||
val callee = expression.symbol
|
||||
|
||||
if (callee.isAccessible(withSuper)) {
|
||||
return expression
|
||||
}
|
||||
return if (expression.origin == IrStatementOrigin.GET_PROPERTY) {
|
||||
generateReflectiveAccessForGetter(expression)
|
||||
} else if (expression.origin?.isAssignmentOperator() == true) {
|
||||
generateReflectiveAccessForSetter(expression)
|
||||
} else if (expression.dispatchReceiver == null && expression.extensionReceiver == null) {
|
||||
generateReflectiveStaticCall(expression)
|
||||
} else if (withSuper) {
|
||||
generateInvokeSpecialForCall(expression)
|
||||
} else {
|
||||
generateReflectiveMethodInvocation(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val field = expression.symbol
|
||||
return if (field.isAccessible()) {
|
||||
expression
|
||||
} else {
|
||||
generateReflectiveFieldGet(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val field = expression.symbol
|
||||
return if (field.isAccessible()) {
|
||||
expression
|
||||
} else if (field.owner.correspondingPropertySymbol?.owner?.isConst == true || (field.owner.isFromJava() && field.owner.isFinal)) {
|
||||
generateThrowIllegalAccessException(expression)
|
||||
} else {
|
||||
generateReflectiveFieldSet(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val callee = expression.symbol
|
||||
return if (callee.isAccessible()) {
|
||||
expression
|
||||
} else {
|
||||
generateReflectiveConstructorInvocation(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val callee = expression.symbol
|
||||
return if (callee.isAccessible()) {
|
||||
expression
|
||||
} else {
|
||||
generateReflectiveAccessForCompanion(expression)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR Generation for java.lang.reflect.{field, method, constructor} API
|
||||
*/
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
|
||||
private fun IrBuilderWithScope.javaClassObject(klass: IrType): IrExpression =
|
||||
irCall(symbols.kClassJava.owner.getter!!).apply {
|
||||
extensionReceiver =
|
||||
IrClassReferenceImpl(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.kClassClass.starProjectedType,
|
||||
context.irBuiltIns.kClassClass,
|
||||
klass
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getDeclaredField(declaringClass: IrExpression, fieldName: String): IrExpression =
|
||||
irCall(symbols.getDeclaredField).apply {
|
||||
dispatchReceiver = declaringClass
|
||||
putValueArgument(0, irString(fieldName))
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.fieldSetAccessible(field: IrExpression): IrExpression =
|
||||
irCall(symbols.javaLangReflectFieldSetAccessible).apply {
|
||||
dispatchReceiver = field
|
||||
putValueArgument(0, irTrue())
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.fieldSet(fieldObject: IrExpression, receiver: IrExpression, value: IrExpression): IrExpression =
|
||||
irCall(symbols.javaLangReflectFieldSet).apply {
|
||||
dispatchReceiver = fieldObject
|
||||
putValueArgument(0, receiver)
|
||||
putValueArgument(1, value)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.fieldGet(fieldObject: IrExpression, receiver: IrExpression): IrExpression =
|
||||
irCall(symbols.javaLangReflectFieldGet).apply {
|
||||
dispatchReceiver = fieldObject
|
||||
putValueArgument(0, receiver)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getDeclaredMethod(
|
||||
declaringClass: IrExpression,
|
||||
methodName: String,
|
||||
parameterTypes: List<IrType>
|
||||
): IrExpression =
|
||||
irCall(symbols.getDeclaredMethod).apply {
|
||||
dispatchReceiver = declaringClass
|
||||
putValueArgument(0, irString(methodName))
|
||||
putValueArgument(1, irVararg(symbols.javaLangClass.defaultType, parameterTypes.map { javaClassObject(it) }))
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.methodSetAccessible(method: IrExpression): IrExpression =
|
||||
irCall(symbols.javaLangReflectMethodSetAccessible).apply {
|
||||
dispatchReceiver = method
|
||||
putValueArgument(0, irTrue())
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.methodInvoke(
|
||||
method: IrExpression,
|
||||
receiver: IrExpression,
|
||||
arguments: List<IrExpression>
|
||||
): IrExpression =
|
||||
irCall(symbols.javaLangReflectMethodInvoke).apply {
|
||||
dispatchReceiver = method
|
||||
putValueArgument(0, receiver)
|
||||
putValueArgument(1, irVararg(context.irBuiltIns.anyNType, arguments))
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getDeclaredConstructor(
|
||||
declaringClass: IrExpression,
|
||||
parameterTypes: List<IrType>
|
||||
): IrExpression =
|
||||
irCall(symbols.getDeclaredConstructor).apply {
|
||||
dispatchReceiver = declaringClass
|
||||
putValueArgument(0, irVararg(symbols.javaLangClass.defaultType, parameterTypes.map { javaClassObject(it) }))
|
||||
}
|
||||
|
||||
|
||||
private fun IrBuilderWithScope.constructorSetAccessible(constructor: IrExpression): IrExpression =
|
||||
irCall(symbols.javaLangReflectConstructorSetAccessible).apply {
|
||||
dispatchReceiver = constructor
|
||||
putValueArgument(0, irTrue())
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.constructorNewInstance(constructor: IrExpression, arguments: List<IrExpression>): IrExpression =
|
||||
irCall(symbols.javaLangReflectConstructorNewInstance).apply {
|
||||
dispatchReceiver = constructor
|
||||
putValueArgument(0, irVararg(context.irBuiltIns.anyNType, arguments))
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific reflective "patches"
|
||||
*/
|
||||
|
||||
private fun generateReflectiveMethodInvocation(
|
||||
declaringClass: IrType,
|
||||
methodName: String,
|
||||
parameterTypes: List<IrType>,
|
||||
receiver: IrExpression?, // null => static method on `declaringClass`
|
||||
arguments: List<IrExpression>,
|
||||
returnType: IrType,
|
||||
symbol: IrSymbol
|
||||
): IrExpression =
|
||||
context.createJvmIrBuilder(symbol).irBlock(resultType = returnType) {
|
||||
val methodVar =
|
||||
createTmpVariable(
|
||||
getDeclaredMethod(
|
||||
javaClassObject(declaringClass),
|
||||
methodName,
|
||||
parameterTypes
|
||||
),
|
||||
nameHint = "method",
|
||||
irType = symbols.javaLangReflectMethod.defaultType
|
||||
)
|
||||
+methodSetAccessible(irGet(methodVar))
|
||||
+methodInvoke(irGet(methodVar), receiver ?: irNull(), arguments)
|
||||
}
|
||||
|
||||
private fun IrFunctionAccessExpression.getValueArguments(): List<IrExpression> =
|
||||
(0 until valueArgumentsCount).map { getValueArgument(it)!! }
|
||||
|
||||
private fun IrFunctionAccessExpression.valueParameterTypes(): List<IrType> =
|
||||
symbol.owner.valueParameters.map { it.type }
|
||||
|
||||
private fun generateReflectiveMethodInvocation(call: IrCall): IrExpression =
|
||||
generateReflectiveMethodInvocation(
|
||||
call.superQualifierSymbol?.defaultType ?: call.dispatchReceiver!!.type,
|
||||
call.symbol.owner.name.asString(),
|
||||
mutableListOf<IrType>().apply {
|
||||
call.symbol.owner.extensionReceiverParameter?.let { add(it.type) }
|
||||
addAll(call.valueParameterTypes())
|
||||
},
|
||||
call.dispatchReceiver!!,
|
||||
mutableListOf<IrExpression>().apply {
|
||||
call.extensionReceiver?.let { add(it) }
|
||||
addAll(call.getValueArguments())
|
||||
},
|
||||
call.type,
|
||||
call.symbol
|
||||
)
|
||||
|
||||
private fun generateReflectiveStaticCall(call: IrCall): IrExpression {
|
||||
assert(call.dispatchReceiver == null) { "Assumed-to-be static call with a dispatch receiver" }
|
||||
return generateReflectiveMethodInvocation(
|
||||
call.symbol.owner.parentAsClass.defaultType,
|
||||
call.symbol.owner.name.asString(),
|
||||
call.valueParameterTypes(),
|
||||
null, // static call
|
||||
call.getValueArguments(),
|
||||
call.type,
|
||||
call.symbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateReflectiveConstructorInvocation(call: IrConstructorCall): IrExpression =
|
||||
context.createJvmIrBuilder(call.symbol)
|
||||
.irBlock(resultType = call.type) {
|
||||
val constructorVar =
|
||||
createTmpVariable(
|
||||
getDeclaredConstructor(
|
||||
javaClassObject(call.symbol.owner.parentAsClass.defaultType),
|
||||
call.valueParameterTypes()
|
||||
),
|
||||
nameHint = "constructor",
|
||||
irType = symbols.javaLangReflectConstructor.defaultType
|
||||
)
|
||||
+constructorSetAccessible(irGet(constructorVar))
|
||||
+constructorNewInstance(irGet(constructorVar), call.getValueArguments())
|
||||
}
|
||||
|
||||
private fun generateReflectiveFieldGet(
|
||||
declaringClass: IrType,
|
||||
fieldName: String,
|
||||
fieldType: IrType,
|
||||
instance: IrExpression?, // null ==> static field on `declaringClass`
|
||||
symbol: IrSymbol,
|
||||
): IrExpression =
|
||||
context.createJvmIrBuilder(symbol)
|
||||
.irBlock(resultType = fieldType) {
|
||||
val classVar = createTmpVariable(
|
||||
javaClassObject(declaringClass),
|
||||
nameHint = "klass",
|
||||
irType = symbols.kClassJava.owner.getter!!.returnType
|
||||
)
|
||||
val fieldVar = createTmpVariable(
|
||||
getDeclaredField(irGet(classVar), fieldName),
|
||||
nameHint = "field",
|
||||
irType = symbols.javaLangReflectField.defaultType
|
||||
)
|
||||
+fieldSetAccessible(irGet(fieldVar))
|
||||
+fieldGet(irGet(fieldVar), instance ?: irGet(classVar))
|
||||
}
|
||||
|
||||
private fun generateReflectiveFieldGet(getField: IrGetField): IrExpression =
|
||||
generateReflectiveFieldGet(
|
||||
getField.symbol.owner.parentClassOrNull!!.defaultType,
|
||||
getField.symbol.owner.name.asString(),
|
||||
getField.type,
|
||||
getField.receiver,
|
||||
getField.symbol
|
||||
)
|
||||
|
||||
private fun generateReflectiveFieldSet(
|
||||
declaringClass: IrType,
|
||||
fieldName: String,
|
||||
value: IrExpression,
|
||||
type: IrType,
|
||||
instance: IrExpression?,
|
||||
symbol: IrSymbol
|
||||
): IrExpression {
|
||||
return context.createJvmIrBuilder(symbol)
|
||||
.irBlock(resultType = type) {
|
||||
val fieldVar =
|
||||
createTmpVariable(
|
||||
getDeclaredField(
|
||||
javaClassObject(declaringClass),
|
||||
fieldName
|
||||
),
|
||||
nameHint = "field",
|
||||
irType = symbols.javaLangReflectField.defaultType
|
||||
)
|
||||
+fieldSetAccessible(irGet(fieldVar))
|
||||
+fieldSet(irGet(fieldVar), instance ?: irNull(), value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateReflectiveFieldSet(setField: IrSetField): IrExpression =
|
||||
generateReflectiveFieldSet(
|
||||
setField.symbol.owner.parentClassOrNull!!.defaultType,
|
||||
setField.symbol.owner.name.asString(),
|
||||
setField.value,
|
||||
setField.type,
|
||||
setField.receiver,
|
||||
setField.symbol,
|
||||
)
|
||||
|
||||
private fun shouldUseAccessor(accessor: IrSimpleFunction): Boolean {
|
||||
return (context.generatorExtensions as StubGeneratorExtensions).isAccessorWithExplicitImplementation(accessor)
|
||||
}
|
||||
|
||||
// Returns a pair of the _type_ containing the field and the _instance_ on
|
||||
// which the field should be accessed. The instance is `null` if the field
|
||||
// is static. If the field is on a companion object it will be generated on
|
||||
// the corresponding owning class (recall, at this point the field has been
|
||||
// absolutely determined to be inaccessible to outside code).
|
||||
private fun fieldLocationAndReceiver(call: IrCall): Pair<IrType, IrExpression?> {
|
||||
callsOnCompanionObjects[call]?.let {
|
||||
val parentAsClass = it.owner.parentAsClass
|
||||
if (!parentAsClass.isJvmInterface) {
|
||||
return parentAsClass.defaultType to null
|
||||
}
|
||||
}
|
||||
|
||||
return call.dispatchReceiver!!.type to call.dispatchReceiver!!
|
||||
}
|
||||
|
||||
private fun generateReflectiveAccessForGetter(call: IrCall): IrExpression {
|
||||
val getter = call.symbol.owner
|
||||
val property = getter.correspondingPropertySymbol!!.owner
|
||||
|
||||
if (shouldUseAccessor(getter)) {
|
||||
return generateReflectiveMethodInvocation(
|
||||
getter.parentAsClass.defaultType,
|
||||
JvmAbi.getterName(propertyName = property.name.asString()),
|
||||
getter.extensionReceiverParameter?.let { listOf(it.type) } ?: listOf(),
|
||||
call.dispatchReceiver!!,
|
||||
listOfNotNull(call.extensionReceiver),
|
||||
getter.returnType,
|
||||
call.symbol
|
||||
)
|
||||
}
|
||||
|
||||
val (fieldLocation, instance) = fieldLocationAndReceiver(call)
|
||||
return generateReflectiveFieldGet(
|
||||
fieldLocation,
|
||||
property.name.asString(),
|
||||
getter.returnType,
|
||||
instance,
|
||||
call.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateReflectiveAccessForSetter(call: IrCall): IrExpression {
|
||||
val setter = call.symbol.owner
|
||||
val property = setter.correspondingPropertySymbol!!.owner
|
||||
|
||||
if (shouldUseAccessor(setter)) {
|
||||
return generateReflectiveMethodInvocation(
|
||||
setter.parentAsClass.defaultType,
|
||||
JvmAbi.setterName(propertyName = property.name.asString()),
|
||||
mutableListOf<IrType>().apply {
|
||||
setter.extensionReceiverParameter?.let { add(it.type) }
|
||||
addAll(call.valueParameterTypes())
|
||||
},
|
||||
call.dispatchReceiver!!,
|
||||
mutableListOf<IrExpression>().apply {
|
||||
call.extensionReceiver?.let { add(it) }
|
||||
addAll(call.getValueArguments())
|
||||
},
|
||||
setter.returnType,
|
||||
call.symbol
|
||||
)
|
||||
}
|
||||
|
||||
val (fieldLocation, receiver) = fieldLocationAndReceiver(call)
|
||||
return generateReflectiveFieldSet(
|
||||
fieldLocation,
|
||||
call.symbol.owner.correspondingPropertySymbol!!.owner.name.asString(),
|
||||
call.getValueArgument(0)!!,
|
||||
call.type,
|
||||
receiver,
|
||||
call.symbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateThrowIllegalAccessException(setField: IrSetField): IrExpression {
|
||||
return context.createJvmIrBuilder(setField.symbol).irBlock {
|
||||
+irCall(symbols.throwIllegalAccessException).apply {
|
||||
putValueArgument(0, irString("Can not set final field"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is needed to coerce the codegen to emit a very specific
|
||||
// invokespecial instruction to target a super-call that is otherwise
|
||||
// illegal on the JVM. However! The byte code from this compilation is
|
||||
// not run on a JVM: it is interpreted by eval4j. Eval4j handles
|
||||
// invokespecial via JDI from which it *is* possible to do the required
|
||||
// super call.
|
||||
private fun generateInvokeSpecialForCall(expression: IrCall): IrExpression {
|
||||
val jvmSignature = context.methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner)
|
||||
val owner = expression.superQualifierSymbol!!.owner
|
||||
val builder = context.createJvmIrBuilder(expression.symbol)
|
||||
|
||||
// invokeSpecial(owner: String, name: String, descriptor: String, isInterface: Boolean): T
|
||||
return builder.irCall(context.irIntrinsics.symbols.jvmDebuggerInvokeSpecialIntrinsic).apply {
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
this.type = expression.symbol.owner.returnType
|
||||
putValueArgument(0, builder.irString("${owner.packageFqName}/${owner.name}"))
|
||||
putValueArgument(1, builder.irString(jvmSignature.asmMethod.name))
|
||||
putValueArgument(2, builder.irString(jvmSignature.asmMethod.descriptor))
|
||||
putValueArgument(3, builder.irFalse())
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateReflectiveAccessForCompanion(call: IrGetObjectValue): IrExpression =
|
||||
generateReflectiveFieldGet(
|
||||
call.symbol.owner.parentAsClass.defaultType,
|
||||
"Companion",
|
||||
call.type,
|
||||
null,
|
||||
call.symbol
|
||||
)
|
||||
}
|
||||
+59
-47
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ScopeWithIr
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedString
|
||||
import org.jetbrains.kotlin.backend.common.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
@@ -51,6 +52,59 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : FileL
|
||||
(accessor.parent as IrDeclarationContainer).declarations.add(accessor)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun IrSymbol.isAccessible(
|
||||
context: JvmBackendContext,
|
||||
currentScope: ScopeWithIr?,
|
||||
inlineScopeResolver: IrInlineScopeResolver,
|
||||
withSuper: Boolean, thisObjReference: IrClassSymbol?): Boolean {
|
||||
/// We assume that IR code that reaches us has been checked for correctness at the frontend.
|
||||
/// This function needs to single out those cases where Java accessibility rules differ from Kotlin's.
|
||||
val declarationRaw = owner as IrDeclarationWithVisibility
|
||||
|
||||
// If this expression won't actually result in a JVM instruction call, access modifiers don't matter.
|
||||
if (declarationRaw is IrFunction && (declarationRaw.isInline || context.irIntrinsics.getIntrinsic(declarationRaw.symbol) != null))
|
||||
return true
|
||||
|
||||
// Enum entry constructors are generated as package-private and are accessed only from corresponding enum class
|
||||
if (declarationRaw is IrConstructor && declarationRaw.constructedClass.isEnumEntry) return true
|
||||
|
||||
// Public declarations are already accessible. However, `super` calls are subclass-only.
|
||||
val jvmVisibility = AsmUtil.getVisibilityAccessFlag(declarationRaw.visibility.delegate)
|
||||
if (jvmVisibility == Opcodes.ACC_PUBLIC && !withSuper) return true
|
||||
|
||||
// `toArray` is always accessible cause mapped to public functions
|
||||
if (declarationRaw is IrSimpleFunction && (declarationRaw.isNonGenericToArray() || declarationRaw.isGenericToArray(context)) &&
|
||||
declarationRaw.parentAsClass.isCollectionSubClass
|
||||
) return true
|
||||
|
||||
// `$assertionsDisabled` is accessed only from the same class, even in an inline function
|
||||
// (the inliner will generate it at the call site if necessary).
|
||||
if (declarationRaw is IrField && declarationRaw.isAssertionsDisabledField(context)) return true
|
||||
|
||||
val declaration = when (declarationRaw) {
|
||||
is IrSimpleFunction -> declarationRaw.resolveFakeOverride(allowAbstract = true)!!
|
||||
is IrField -> declarationRaw.resolveFakeOverride()
|
||||
else -> declarationRaw
|
||||
}
|
||||
|
||||
val ownerClass = declaration.parent as? IrClass ?: return true // locals are always accessible
|
||||
val scopeClassOrPackage = inlineScopeResolver.findContainer(currentScope!!.irElement) ?: return false
|
||||
val samePackage = ownerClass.getPackageFragment()?.fqName == scopeClassOrPackage.getPackageFragment()?.fqName
|
||||
return when {
|
||||
jvmVisibility == 0 /* package only */ -> samePackage
|
||||
jvmVisibility == Opcodes.ACC_PRIVATE -> ownerClass == scopeClassOrPackage
|
||||
// JVM `protected`, unlike Kotlin `protected`, permits accesses from the same package.
|
||||
!withSuper && samePackage -> true
|
||||
// Super calls and cross-package protected accesses are both only possible from a subclass of the declaration
|
||||
// owner. Also, the target of a non-static call must be assignable to the current class. This is a verification
|
||||
// constraint: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.1.8
|
||||
else -> (scopeClassOrPackage is IrClass && scopeClassOrPackage.isSubclassOf(ownerClass)) &&
|
||||
(thisObjReference == null || thisObjReference.owner.isSubclassOf(scopeClassOrPackage))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SyntheticAccessorTransformer(
|
||||
@@ -71,6 +125,11 @@ private class SyntheticAccessorTransformer(
|
||||
private val getterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
|
||||
private val setterMap = mutableMapOf<FieldKey, IrSimpleFunctionSymbol>()
|
||||
|
||||
private fun IrSymbol.isAccessible(withSuper: Boolean, thisObjReference: IrClassSymbol?): Boolean =
|
||||
with(SyntheticAccessorLowering) {
|
||||
isAccessible(context, currentScope, inlineScopeResolver, withSuper, thisObjReference)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
if (expression.usesDefaultArguments()) {
|
||||
return super.visitFunctionAccess(expression)
|
||||
@@ -702,53 +761,6 @@ private class SyntheticAccessorTransformer(
|
||||
// we generate a suffix to distinguish access to field with different receiver types in the super hierarchy.
|
||||
return "p" + if (isStatic && visibility.isProtected) "\$s" + parentAsClass.syntheticAccessorToSuperSuffix() else ""
|
||||
}
|
||||
|
||||
private fun IrSymbol.isAccessible(withSuper: Boolean, thisObjReference: IrClassSymbol?): Boolean {
|
||||
/// We assume that IR code that reaches us has been checked for correctness at the frontend.
|
||||
/// This function needs to single out those cases where Java accessibility rules differ from Kotlin's.
|
||||
val declarationRaw = owner as IrDeclarationWithVisibility
|
||||
|
||||
// If this expression won't actually result in a JVM instruction call, access modifiers don't matter.
|
||||
if (declarationRaw is IrFunction && (declarationRaw.isInline || context.irIntrinsics.getIntrinsic(declarationRaw.symbol) != null))
|
||||
return true
|
||||
|
||||
// Enum entry constructors are generated as package-private and are accessed only from corresponding enum class
|
||||
if (declarationRaw is IrConstructor && declarationRaw.constructedClass.isEnumEntry) return true
|
||||
|
||||
// Public declarations are already accessible. However, `super` calls are subclass-only.
|
||||
val jvmVisibility = AsmUtil.getVisibilityAccessFlag(declarationRaw.visibility.delegate)
|
||||
if (jvmVisibility == Opcodes.ACC_PUBLIC && !withSuper) return true
|
||||
|
||||
// `toArray` is always accessible cause mapped to public functions
|
||||
if (declarationRaw is IrSimpleFunction && (declarationRaw.isNonGenericToArray() || declarationRaw.isGenericToArray(context)) &&
|
||||
declarationRaw.parentAsClass.isCollectionSubClass
|
||||
) return true
|
||||
|
||||
// `$assertionsDisabled` is accessed only from the same class, even in an inline function
|
||||
// (the inliner will generate it at the call site if necessary).
|
||||
if (declarationRaw is IrField && declarationRaw.isAssertionsDisabledField(context)) return true
|
||||
|
||||
val declaration = when (declarationRaw) {
|
||||
is IrSimpleFunction -> declarationRaw.resolveFakeOverride(allowAbstract = true)!!
|
||||
is IrField -> declarationRaw.resolveFakeOverride()
|
||||
else -> declarationRaw
|
||||
}
|
||||
|
||||
val ownerClass = declaration.parent as? IrClass ?: return true // locals are always accessible
|
||||
val scopeClassOrPackage = inlineScopeResolver.findContainer(currentScope!!.irElement) ?: return false
|
||||
val samePackage = ownerClass.getPackageFragment()?.fqName == scopeClassOrPackage.getPackageFragment()?.fqName
|
||||
return when {
|
||||
jvmVisibility == 0 /* package only */ -> samePackage
|
||||
jvmVisibility == Opcodes.ACC_PRIVATE -> ownerClass == scopeClassOrPackage
|
||||
// JVM `protected`, unlike Kotlin `protected`, permits accesses from the same package.
|
||||
!withSuper && samePackage -> true
|
||||
// Super calls and cross-package protected accesses are both only possible from a subclass of the declaration
|
||||
// owner. Also, the target of a non-static call must be assignable to the current class. This is a verification
|
||||
// constraint: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.1.8
|
||||
else -> (scopeClassOrPackage is IrClass && scopeClassOrPackage.isSubclassOf(ownerClass)) &&
|
||||
(thisObjReference == null || thisObjReference.owner.isSubclassOf(scopeClassOrPackage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.syntheticAccessorToSuperSuffix(): String =
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
|
||||
interface JvmGeneratorExtensions {
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_CALL_RESULT_NAME
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_CREATE_METHOD_NAME
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -60,6 +60,7 @@ class JvmSymbols(
|
||||
private val kotlinReflectPackage: IrPackageFragment = createPackage(FqName("kotlin.reflect"))
|
||||
private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang"))
|
||||
private val javaLangInvokePackage: IrPackageFragment = createPackage(FqName("java.lang.invoke"))
|
||||
private val javaLangReflectPackage: IrPackageFragment = createPackage(FqName("java.lang.reflect"))
|
||||
private val javaUtilPackage: IrPackageFragment = createPackage(FqName("java.util"))
|
||||
|
||||
|
||||
@@ -104,6 +105,7 @@ class JvmSymbols(
|
||||
"kotlin.reflect" -> kotlinReflectPackage
|
||||
"java.lang" -> javaLangPackage
|
||||
"java.lang.invoke" -> javaLangInvokePackage
|
||||
"java.lang.reflect" -> javaLangReflectPackage
|
||||
"java.util" -> javaUtilPackage
|
||||
"kotlin.internal" -> kotlinInternalPackage
|
||||
else -> error("Other packages are not supported yet: $fqName")
|
||||
@@ -121,6 +123,9 @@ class JvmSymbols(
|
||||
klass.addFunction("throwTypeCastException", irBuiltIns.nothingType, isStatic = true).apply {
|
||||
addValueParameter("message", irBuiltIns.stringType)
|
||||
}
|
||||
klass.addFunction("throwIllegalAccessException", irBuiltIns.nothingType, isStatic = true).apply {
|
||||
addValueParameter("message", irBuiltIns.stringType)
|
||||
}
|
||||
klass.addFunction("throwUnsupportedOperationException", irBuiltIns.nothingType, isStatic = true).apply {
|
||||
addValueParameter("message", irBuiltIns.stringType)
|
||||
}
|
||||
@@ -171,6 +176,9 @@ class JvmSymbols(
|
||||
override val throwTypeCastException: IrSimpleFunctionSymbol =
|
||||
intrinsicsClass.functions.single { it.owner.name.asString() == "throwTypeCastException" }
|
||||
|
||||
val throwIllegalAccessException: IrSimpleFunctionSymbol =
|
||||
intrinsicsClass.functions.single { it.owner.name.asString() == "throwIllegalAccessException" }
|
||||
|
||||
val throwUnsupportedOperationException: IrSimpleFunctionSymbol =
|
||||
intrinsicsClass.functions.single { it.owner.name.asString() == "throwUnsupportedOperationException" }
|
||||
|
||||
@@ -224,12 +232,103 @@ class JvmSymbols(
|
||||
private val kDeclarationContainer: IrClassSymbol =
|
||||
createClass(StandardNames.FqNames.kDeclarationContainer.toSafe(), ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
val javaLangReflectField: IrClassSymbol =
|
||||
createClass(FqName("java.lang.reflect.Field")) { klass ->
|
||||
klass.addFunction("setAccessible", irBuiltIns.unitType).apply {
|
||||
addValueParameter("isAccessible", irBuiltIns.booleanType)
|
||||
}
|
||||
klass.addFunction("get", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("receiver", irBuiltIns.anyNType)
|
||||
}
|
||||
klass.addFunction("set", irBuiltIns.unitType).apply {
|
||||
addValueParameter("receiver", irBuiltIns.anyNType)
|
||||
addValueParameter("value", irBuiltIns.anyNType)
|
||||
}
|
||||
}
|
||||
|
||||
val javaLangReflectMethod: IrClassSymbol =
|
||||
createClass(FqName("java.lang.reflect.Method")) { klass ->
|
||||
klass.addFunction("setAccessible", irBuiltIns.unitType).apply {
|
||||
addValueParameter("isAccessible", irBuiltIns.booleanType)
|
||||
}
|
||||
klass.addFunction("invoke", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("receiver", irBuiltIns.anyNType)
|
||||
addValueParameter {
|
||||
name = Name.identifier("args")
|
||||
type = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType)
|
||||
varargElementType = irBuiltIns.anyNType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val javaLangReflectConstructor: IrClassSymbol =
|
||||
createClass(FqName("java.lang.reflect.Constructor")) { klass ->
|
||||
klass.addFunction("setAccessible", irBuiltIns.unitType).apply {
|
||||
addValueParameter("isAccessible", irBuiltIns.booleanType)
|
||||
}
|
||||
klass.addFunction("newInstance", irBuiltIns.anyNType).apply {
|
||||
addValueParameter {
|
||||
name = Name.identifier("args")
|
||||
type = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType)
|
||||
varargElementType = irBuiltIns.anyNType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val javaLangReflectFieldSetAccessible: IrSimpleFunctionSymbol =
|
||||
javaLangReflectField.functionByName("setAccessible")
|
||||
|
||||
val javaLangReflectMethodSetAccessible: IrSimpleFunctionSymbol =
|
||||
javaLangReflectMethod.functionByName("setAccessible")
|
||||
|
||||
val javaLangReflectConstructorSetAccessible: IrSimpleFunctionSymbol =
|
||||
javaLangReflectConstructor.functionByName("setAccessible")
|
||||
|
||||
val javaLangClass: IrClassSymbol =
|
||||
createClass(FqName("java.lang.Class")) { klass ->
|
||||
klass.addTypeParameter("T", irBuiltIns.anyNType, Variance.INVARIANT)
|
||||
klass.addFunction("desiredAssertionStatus", irBuiltIns.booleanType)
|
||||
klass.addFunction("getDeclaredMethod", javaLangReflectMethod.defaultType.makeNullable()).apply {
|
||||
addValueParameter("methodName", irBuiltIns.stringType.makeNullable())
|
||||
addValueParameter {
|
||||
name = Name.identifier("args")
|
||||
type = irBuiltIns.arrayClass.typeWith(klass.defaultType).makeNullable()
|
||||
varargElementType = klass.defaultType
|
||||
}
|
||||
}
|
||||
klass.addFunction("getDeclaredField", javaLangReflectField.defaultType).apply {
|
||||
addValueParameter("fieldName", irBuiltIns.stringType)
|
||||
}
|
||||
klass.addFunction("getDeclaredConstructor", javaLangReflectConstructor.defaultType.makeNullable()).apply {
|
||||
addValueParameter {
|
||||
name = Name.identifier("args")
|
||||
type = irBuiltIns.arrayClass.typeWith(klass.defaultType).makeNullable()
|
||||
varargElementType = klass.defaultType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val getDeclaredField: IrSimpleFunctionSymbol =
|
||||
javaLangClass.functionByName("getDeclaredField")
|
||||
|
||||
val getDeclaredMethod: IrSimpleFunctionSymbol =
|
||||
javaLangClass.functionByName("getDeclaredMethod")
|
||||
|
||||
val getDeclaredConstructor: IrSimpleFunctionSymbol =
|
||||
javaLangClass.functionByName("getDeclaredConstructor")
|
||||
|
||||
val javaLangReflectFieldGet: IrSimpleFunctionSymbol =
|
||||
javaLangReflectField.functionByName("get")
|
||||
|
||||
val javaLangReflectFieldSet: IrSimpleFunctionSymbol =
|
||||
javaLangReflectField.functionByName("set")
|
||||
|
||||
val javaLangReflectMethodInvoke: IrSimpleFunctionSymbol =
|
||||
javaLangReflectMethod.functionByName("invoke")
|
||||
|
||||
val javaLangReflectConstructorNewInstance: IrSimpleFunctionSymbol =
|
||||
javaLangReflectConstructor.functionByName("newInstance")
|
||||
|
||||
private val javaLangDeprecatedWithDeprecatedFlag: IrClassSymbol =
|
||||
createClass(FqName("java.lang.Deprecated"), classKind = ClassKind.ANNOTATION_CLASS) { klass ->
|
||||
klass.addConstructor { isPrimary = true }
|
||||
@@ -735,6 +834,19 @@ class JvmSymbols(
|
||||
returnType = irBuiltIns.anyType
|
||||
}.symbol
|
||||
|
||||
val jvmDebuggerInvokeSpecialIntrinsic: IrSimpleFunctionSymbol =
|
||||
irFactory.buildFun {
|
||||
name = Name.special("<jvm-debugger-invokespecial>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
addValueParameter("owner", irBuiltIns.stringType)
|
||||
addValueParameter("name", irBuiltIns.stringType)
|
||||
addValueParameter("descriptor", irBuiltIns.stringType)
|
||||
addValueParameter("isInterface", irBuiltIns.booleanType)
|
||||
returnType = irBuiltIns.anyNType
|
||||
}.symbol
|
||||
|
||||
private val collectionToArrayClass: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.CollectionToArray")) { klass ->
|
||||
klass.origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||
|
||||
|
||||
+2
@@ -86,8 +86,10 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
|
||||
symbols.throwNullPointerException.toKey()!! to ThrowException(Type.getObjectType("java/lang/NullPointerException")),
|
||||
symbols.throwTypeCastException.toKey()!! to ThrowException(Type.getObjectType("kotlin/TypeCastException")),
|
||||
symbols.throwUnsupportedOperationException.toKey()!! to ThrowException(Type.getObjectType("java/lang/UnsupportedOperationException")),
|
||||
symbols.throwIllegalAccessException.toKey()!! to ThrowException(Type.getObjectType("java/lang/IllegalAccessException")),
|
||||
symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException,
|
||||
symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic,
|
||||
symbols.jvmDebuggerInvokeSpecialIntrinsic.toKey()!! to JvmDebuggerInvokeSpecial,
|
||||
symbols.intPostfixIncr.toKey()!! to PostfixIinc(1),
|
||||
symbols.intPostfixDecr.toKey()!! to PostfixIinc(-1)
|
||||
) +
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
// This intrinsic enables IR lowerings to force the generation of a particular
|
||||
// invokeSpecial instruction in the resulting JVM bytecode.
|
||||
//
|
||||
// The need for this is to coerce the IR codegen backend to generate an
|
||||
// otherwise illegal invokeSpecial for the express purpose of being
|
||||
// _interpreted_ by eval4j in the fragment evaluator and not actually run on
|
||||
// the JVM. This allows the "evaluate expression" functionality of the Kotlin
|
||||
// JVM Debugger Plug-in in IntelliJ to simulate the invocation of `super` calls
|
||||
// in the context of a breakpoint.
|
||||
//
|
||||
// It uses the "trick" of encoding the desired operands as constants passed as
|
||||
// arguments to the intrinsic, opening a direct line from the producing
|
||||
// lowering straight through to JVM codegen without interference from
|
||||
// lowerings in between.
|
||||
object JvmDebuggerInvokeSpecial : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
fun fail(message: String): Nothing =
|
||||
throw AssertionError("$message; expression:\n${expression.dump()}")
|
||||
|
||||
val owner = expression.getValueArgument(0)?.getStringConst()
|
||||
?: fail("'owner' is expected to be a string const")
|
||||
val name = expression.getValueArgument(1)?.getStringConst()
|
||||
?: fail("'name' is expected to be a string const")
|
||||
val descriptor = expression.getValueArgument(2)?.getStringConst()
|
||||
?: fail("'descriptor' is expected to be a string const")
|
||||
val isInterface = expression.getValueArgument(3)?.getBooleanConst()
|
||||
?: fail("'isInterface' is expected to be a boolean const")
|
||||
|
||||
expression.dispatchReceiver!!.accept(codegen, data).materialize()
|
||||
codegen.mv.invokespecial(owner, name, descriptor, isInterface)
|
||||
|
||||
return MaterialValue(codegen, Type.getReturnType(descriptor), expression.type)
|
||||
}
|
||||
}
|
||||
@@ -202,17 +202,18 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator
|
||||
val irType = descriptor.type.toIrType()
|
||||
|
||||
return if (getMethodDescriptor == null) {
|
||||
call.callReceiver.call { dispatchReceiverValue, _ ->
|
||||
val superQualifierSymbol = (call.superQualifier ?: descriptor.containingDeclaration as? ClassDescriptor)?.let {
|
||||
if (it is ScriptDescriptor) null // otherwise it creates a reference to script as class; TODO: check if correct
|
||||
else context.symbolTable.referenceClass(it)
|
||||
}
|
||||
val fieldSymbol = context.symbolTable.referenceField(descriptor.resolveFakeOverride().original)
|
||||
val superQualifierSymbol = (call.superQualifier ?: descriptor.containingDeclaration as? ClassDescriptor)?.let {
|
||||
if (it is ScriptDescriptor) null // otherwise it creates a reference to script as class; TODO: check if correct
|
||||
else context.symbolTable.referenceClass(it)
|
||||
}
|
||||
val fieldSymbol =
|
||||
context.symbolTable.referenceField(context.extensions.remapDebuggerFieldPropertyDescriptor(descriptor.resolveFakeOverride().original))
|
||||
call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
|
||||
IrGetFieldImpl(
|
||||
startOffset, endOffset,
|
||||
fieldSymbol,
|
||||
irType,
|
||||
dispatchReceiverValue?.load(),
|
||||
dispatchReceiverValue?.load() ?: extensionReceiverValue?.load(),
|
||||
IrStatementOrigin.GET_PROPERTY,
|
||||
superQualifierSymbol
|
||||
).also { context.callToSubstitutedDescriptorMap[it] = descriptor }
|
||||
|
||||
+2
@@ -42,4 +42,6 @@ open class GeneratorExtensions : StubGeneratorExtensions() {
|
||||
open fun getPreviousScripts(): List<IrScriptSymbol>? = null
|
||||
|
||||
open fun unwrapSyntheticJavaProperty(descriptor: PropertyDescriptor): Pair<FunctionDescriptor, FunctionDescriptor?>? = null
|
||||
|
||||
open fun remapDebuggerFieldPropertyDescriptor(propertyDescriptor: PropertyDescriptor): PropertyDescriptor = propertyDescriptor
|
||||
}
|
||||
|
||||
-2
@@ -15,9 +15,7 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.util.IdSignatureComposer
|
||||
import org.jetbrains.kotlin.ir.util.NameProvider
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
|
||||
|
||||
// Used from CodeFragmentCompiler for IDE Debugger Plug-In
|
||||
|
||||
@@ -121,3 +121,14 @@ fun IrStatementOrigin.isAssignmentOperatorWithResult() =
|
||||
else ->
|
||||
false
|
||||
}
|
||||
|
||||
fun IrStatementOrigin.isAssignmentOperator(): Boolean =
|
||||
when (this) {
|
||||
IrStatementOrigin.EQ,
|
||||
IrStatementOrigin.PLUSEQ,
|
||||
IrStatementOrigin.MINUSEQ,
|
||||
IrStatementOrigin.MULTEQ,
|
||||
IrStatementOrigin.DIVEQ,
|
||||
IrStatementOrigin.PERCEQ -> true
|
||||
else -> isAssignmentOperatorWithResult()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
@@ -35,6 +34,15 @@ open class StubGeneratorExtensions {
|
||||
// intercept and supply "fake" deserialized sources.
|
||||
open fun getContainerSource(descriptor: DeclarationDescriptor): DeserializedContainerSource? = null
|
||||
|
||||
// Extension point for the JVM Debugger IDEA plug-in: to replace accesses
|
||||
// to private properties _without_ accessor implementations, the fragment
|
||||
// compiler needs to predict the compilation output for properties.
|
||||
// To do this, we need to know whether the property accessors have explicit
|
||||
// bodies, information that is _not_ present in the IR structure, but _is_
|
||||
// available in the corresponding PSI. See `CodeFragmentCompiler` in the
|
||||
// plug-in for the implementation.
|
||||
open fun isAccessorWithExplicitImplementation(accessor: IrSimpleFunction): Boolean = false
|
||||
|
||||
open fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = false
|
||||
|
||||
open fun isStaticFunction(descriptor: FunctionDescriptor): Boolean = false
|
||||
|
||||
Reference in New Issue
Block a user