Generate not-null assertions after method calls
If a method comes from Java and is annotated as returning NotNull, after calling it we should check if it actually returned something other than null. Introduce checkReturnedValueIsNotNull() in jet/runtime/Intrinsics which does exactly that. CallableMethod's invoke() and invokeDefault() are now private, use asserted versions instead
This commit is contained in:
@@ -27,12 +27,14 @@ import org.jetbrains.asm4.MethodVisitor;
|
||||
import org.jetbrains.asm4.Type;
|
||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||
import org.jetbrains.jet.codegen.state.JetTypeMapper;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
|
||||
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.java.*;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
@@ -417,4 +419,35 @@ public class AsmUtil {
|
||||
public static void genStubCode(MethodVisitor mv) {
|
||||
genMethodThrow(mv, STUB_EXCEPTION, STUB_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
public static void genNotNullAssertionForMethod(
|
||||
@NotNull InstructionAdapter v,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull ResolvedCall resolvedCall
|
||||
) {
|
||||
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
|
||||
if (descriptor instanceof ConstructorDescriptor) return;
|
||||
|
||||
genNotNullAssertion(v, state, descriptor, "checkReturnedValueIsNotNull");
|
||||
}
|
||||
|
||||
private static void genNotNullAssertion(
|
||||
@NotNull InstructionAdapter v,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull CallableDescriptor descriptor,
|
||||
@NotNull String assertMethodToCall
|
||||
) {
|
||||
if (!state.isGenerateNotNullAssertions()) return;
|
||||
|
||||
JetType type = descriptor.getReturnType();
|
||||
if (type == null || type.isNullable()) return;
|
||||
|
||||
Type asmType = state.getTypeMapper().mapReturnType(type);
|
||||
if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
|
||||
v.dup();
|
||||
v.visitLdcInsn(descriptor.getContainingDeclaration().getName().getName());
|
||||
v.visitLdcInsn(descriptor.getName().getName());
|
||||
v.invokestatic("jet/runtime/Intrinsics", assertMethodToCall, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Type;
|
||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
|
||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
|
||||
@@ -96,17 +98,26 @@ public class CallableMethod implements Callable {
|
||||
return receiverParameterType;
|
||||
}
|
||||
|
||||
void invoke(InstructionAdapter v) {
|
||||
private void invoke(InstructionAdapter v) {
|
||||
v.visitMethodInsn(getInvokeOpcode(), owner.getInternalName(), getSignature().getAsmMethod().getName(),
|
||||
getSignature().getAsmMethod().getDescriptor());
|
||||
}
|
||||
|
||||
public void invokeWithNotNullAssertion(
|
||||
@NotNull InstructionAdapter v,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull ResolvedCall resolvedCall
|
||||
) {
|
||||
invoke(v);
|
||||
AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Type getGenerateCalleeType() {
|
||||
return generateCalleeType;
|
||||
}
|
||||
|
||||
public void invokeWithDefault(InstructionAdapter v, int mask) {
|
||||
private void invokeDefault(InstructionAdapter v, int mask) {
|
||||
if (defaultImplOwner == null || defaultImplParam == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@@ -125,6 +136,16 @@ public class CallableMethod implements Callable {
|
||||
}
|
||||
}
|
||||
|
||||
public void invokeDefaultWithNotNullAssertion(
|
||||
@NotNull InstructionAdapter v,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull ResolvedCall resolvedCall,
|
||||
int mask
|
||||
) {
|
||||
invokeDefault(v, mask);
|
||||
AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
|
||||
}
|
||||
|
||||
public boolean isNeedsThis() {
|
||||
return thisClass != null && generateCalleeType == null;
|
||||
}
|
||||
|
||||
@@ -1863,10 +1863,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
|
||||
int mask = pushMethodArguments(resolvedCall, callableMethod.getValueParameterTypes());
|
||||
if (mask == 0) {
|
||||
callableMethod.invoke(v);
|
||||
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||
}
|
||||
else {
|
||||
callableMethod.invokeWithDefault(v, mask);
|
||||
callableMethod.invokeDefaultWithNotNullAssertion(v, state, resolvedCall, mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2499,7 +2499,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
|
||||
pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
|
||||
callable.invoke(v);
|
||||
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||
if (keepReturnValue) {
|
||||
value.store(callable.getReturnType(), v);
|
||||
}
|
||||
@@ -2570,7 +2570,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
|
||||
Type type = expressionType(expression.getBaseExpression());
|
||||
value.put(type, v);
|
||||
callableMethod.invoke(v);
|
||||
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||
|
||||
value.store(callableMethod.getReturnType(), v);
|
||||
value.put(type, v);
|
||||
return StackValue.onStack(type);
|
||||
@@ -2588,7 +2589,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
assert resolvedCall != null;
|
||||
genThisAndReceiverFromResolvedCall(StackValue.none(), resolvedCall, callable);
|
||||
pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
|
||||
callable.invoke(v);
|
||||
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||
|
||||
return returnValueAsStackValue(op, callable.getSignature().getAsmMethod().getReturnType());
|
||||
}
|
||||
|
||||
@@ -2676,7 +2678,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
|
||||
CallableMethod callableMethod = (CallableMethod) callable;
|
||||
callableMethod.invoke(v);
|
||||
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||
|
||||
value.store(callableMethod.getReturnType(), v);
|
||||
return StackValue.onStack(type);
|
||||
}
|
||||
|
||||
@@ -1341,7 +1341,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
iv.load(nextVar, t);
|
||||
nextVar += t.getSize();
|
||||
}
|
||||
superCallable.invoke(codegen.v);
|
||||
superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
|
||||
}
|
||||
else {
|
||||
codegen.invokeMethodWithArguments(superCallable, (JetCallElement) superCall, StackValue.none());
|
||||
|
||||
@@ -39,8 +39,7 @@ import org.jetbrains.jet.lexer.JetTokens;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.asm4.Opcodes.*;
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.boxType;
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.isIntPrimitive;
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
||||
|
||||
/**
|
||||
@@ -678,7 +677,7 @@ public abstract class StackValue {
|
||||
throw new UnsupportedOperationException("no getter specified");
|
||||
}
|
||||
if (getter instanceof CallableMethod) {
|
||||
((CallableMethod) getter).invoke(v);
|
||||
((CallableMethod) getter).invokeWithNotNullAssertion(v, state, resolvedGetCall);
|
||||
}
|
||||
else {
|
||||
((IntrinsicMethod) getter).generate(codegen, v, type, null, null, null, state);
|
||||
@@ -696,7 +695,7 @@ public abstract class StackValue {
|
||||
Method asmMethod = method.getSignature().getAsmMethod();
|
||||
Type[] argumentTypes = asmMethod.getArgumentTypes();
|
||||
coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
|
||||
method.invoke(v);
|
||||
method.invokeWithNotNullAssertion(v, state, resolvedSetCall);
|
||||
Type returnType = asmMethod.getReturnType();
|
||||
if (returnType != Type.VOID_TYPE) {
|
||||
pop(returnType, v);
|
||||
|
||||
Reference in New Issue
Block a user