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:
Alexander Udalov
2012-10-01 20:15:59 +04:00
parent 1cf5e92981
commit 95ec2448eb
13 changed files with 252 additions and 25 deletions
@@ -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);