Nullability assertions for extension receiver
In Kotlin 1.1 and before, there were no nullability assertions on extension receivers, because receiver is resolved with NO_EXPECTED_TYPE. So, if an expression of platform type is passed as an extension receiver to a non-private function, it would fail with IllegalArgumentException. However, if the function is private, then we generated no parameter assertions under assumption that such function can be called from Kotlin only, and all arguments are checked on the call site. Thus 'null' could propagate indefinitely. In Kotlin 1.2, we do the following: - Generate nullability assertions for expression receivers. NB nullability assertions are stored for ReceiverValue instances, not for expressions: given expression can act as receiver in different calls, each with an expected receiver type of its own. - Generate nullability assertions for extension receivers of private operator functions. NB it still can throw NPE for some particular "optimized" cases, but at least those nulls would not propagate indefinitely. This behavior is disabled by an "advanced" command-line option '-Xno-receiver-assertions'.
This commit is contained in:
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.codegen.serialization.JvmStringTable;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
@@ -660,8 +661,23 @@ public class AsmUtil {
|
||||
// currently when resuming a suspend function we pass default values instead of real arguments (i.e. nulls for references)
|
||||
if (descriptor.isSuspend()) return;
|
||||
|
||||
// Private method is not accessible from other classes, no assertions needed
|
||||
if (getVisibilityAccessFlag(descriptor) == ACC_PRIVATE) return;
|
||||
if (getVisibilityAccessFlag(descriptor) == ACC_PRIVATE) {
|
||||
// Private method is not accessible from other classes, no assertions needed,
|
||||
// unless we have a private operator function, in which we should generate a parameter assertion for an extension receiver.
|
||||
|
||||
// HACK: this provides "fail fast" behavior for operator functions.
|
||||
// Such functions can be invoked in operator conventions desugaring,
|
||||
// which is currently done on ad hoc basis in ExpressionCodegen.
|
||||
|
||||
if (state.isReceiverAssertionsDisabled()) return;
|
||||
if (descriptor.isOperator()) {
|
||||
ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
|
||||
if (receiverParameter != null) {
|
||||
genParamAssertion(v, state.getTypeMapper(), frameMap, receiverParameter, "$receiver");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
|
||||
if (receiverParameter != null) {
|
||||
@@ -677,18 +693,19 @@ public class AsmUtil {
|
||||
@NotNull InstructionAdapter v,
|
||||
@NotNull KotlinTypeMapper typeMapper,
|
||||
@NotNull FrameMap frameMap,
|
||||
@NotNull CallableDescriptor parameter,
|
||||
@NotNull ParameterDescriptor parameter,
|
||||
@NotNull String name
|
||||
) {
|
||||
KotlinType type = parameter.getReturnType();
|
||||
if (type == null || isNullableType(type)) return;
|
||||
KotlinType type = parameter.getType();
|
||||
if (isNullableType(type)) return;
|
||||
|
||||
int index = frameMap.getIndex(parameter);
|
||||
Type asmType = typeMapper.mapType(type);
|
||||
if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
|
||||
v.load(index, asmType);
|
||||
v.visitLdcInsn(name);
|
||||
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkParameterIsNotNull",
|
||||
String checkMethod = "checkParameterIsNotNull";
|
||||
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, checkMethod,
|
||||
"(Ljava/lang/Object;Ljava/lang/String;)V", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
|
||||
import org.jetbrains.kotlin.config.ApiVersion;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
@@ -2466,7 +2467,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return generateExtensionReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
|
||||
}
|
||||
else if (receiverValue instanceof ExpressionReceiver) {
|
||||
return gen(((ExpressionReceiver) receiverValue).getExpression());
|
||||
ExpressionReceiver expressionReceiver = (ExpressionReceiver) receiverValue;
|
||||
StackValue stackValue = gen(expressionReceiver.getExpression());
|
||||
if (!state.isReceiverAssertionsDisabled()) {
|
||||
RuntimeAssertionInfo runtimeAssertionInfo =
|
||||
bindingContext.get(JvmBindingContextSlices.RECEIVER_RUNTIME_ASSERTION_INFO, expressionReceiver);
|
||||
stackValue = genNotNullAssertions(state, stackValue, runtimeAssertionInfo);
|
||||
}
|
||||
return stackValue;
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue);
|
||||
|
||||
@@ -156,7 +156,12 @@ class GenerationState @JvmOverloads constructor(
|
||||
var hasResult: Boolean = false
|
||||
}
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
|
||||
val isCallAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS)
|
||||
val isReceiverAssertionsDisabled: Boolean =
|
||||
configuration.getBoolean(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS) ||
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.NullabilityAssertionOnExtensionReceiver)
|
||||
val isParamAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS)
|
||||
val isInlineDisabled: Boolean = configuration.getBoolean(CommonConfigurationKeys.DISABLE_INLINE)
|
||||
val useTypeTableInSerializer: Boolean = configuration.getBoolean(JVMConfigurationKeys.USE_TYPE_TABLE)
|
||||
@@ -168,7 +173,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
|
||||
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
|
||||
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||
|
||||
init {
|
||||
|
||||
Reference in New Issue
Block a user