diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt index 964109c1676..200eebc11a6 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt @@ -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? = 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` diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt new file mode 100644 index 00000000000..f55f4f9dbd5 --- /dev/null +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt @@ -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 = 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 + ): 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 = + irCall(symbols.javaLangReflectMethodInvoke).apply { + dispatchReceiver = method + putValueArgument(0, receiver) + putValueArgument(1, irVararg(context.irBuiltIns.anyNType, arguments)) + } + + private fun IrBuilderWithScope.getDeclaredConstructor( + declaringClass: IrExpression, + parameterTypes: List + ): 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 = + 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, + receiver: IrExpression?, // null => static method on `declaringClass` + arguments: List, + 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 = + (0 until valueArgumentsCount).map { getValueArgument(it)!! } + + private fun IrFunctionAccessExpression.valueParameterTypes(): List = + 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().apply { + call.symbol.owner.extensionReceiverParameter?.let { add(it.type) } + addAll(call.valueParameterTypes()) + }, + call.dispatchReceiver!!, + mutableListOf().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 { + 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().apply { + setter.extensionReceiverParameter?.let { add(it.type) } + addAll(call.valueParameterTypes()) + }, + call.dispatchReceiver!!, + mutableListOf().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 + ) +} diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index 33f4b4527be..daf3db0f735 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -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() private val setterMap = mutableMapOf() + 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 = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensions.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensions.kt index ffe2bf6b66b..1830bfc35f0 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensions.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensions.kt @@ -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 { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index 6865ccfc901..785be96aab6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -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("") + 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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index 0b11fe623f3..ccfd4f41603 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -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) ) + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmDebuggerInvokeSpecial.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmDebuggerInvokeSpecial.kt new file mode 100644 index 00000000000..e7653fa2eea --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmDebuggerInvokeSpecial.kt @@ -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) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index fcf7c0d2e13..44e705e7224 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -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 } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorExtensions.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorExtensions.kt index f40dc1bee30..bac4d456bea 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorExtensions.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorExtensions.kt @@ -42,4 +42,6 @@ open class GeneratorExtensions : StubGeneratorExtensions() { open fun getPreviousScripts(): List? = null open fun unwrapSyntheticJavaProperty(descriptor: PropertyDescriptor): Pair? = null + + open fun remapDebuggerFieldPropertyDescriptor(propertyDescriptor: PropertyDescriptor): PropertyDescriptor = propertyDescriptor } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt index 387caa489f0..afd6791d944 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt index 9798b057a05..9ee4ebbe248 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt @@ -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() + } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt index 7cfcc01c126..32b8de02ffc 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt @@ -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