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:
@@ -126,6 +126,9 @@ public class SpecialFiles {
|
|||||||
excludedFiles.add("kt529.kt"); // Bug
|
excludedFiles.add("kt529.kt"); // Bug
|
||||||
|
|
||||||
excludedFiles.add("noClassObjectForJavaClass.kt");
|
excludedFiles.add("noClassObjectForJavaClass.kt");
|
||||||
|
|
||||||
|
excludedFiles.add("doGenerateAssertions.kt"); // Multi-file + Java
|
||||||
|
excludedFiles.add("doNotGenerateAssertions.kt"); // Multi-file + Java
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpecialFiles() {
|
private SpecialFiles() {
|
||||||
|
|||||||
@@ -27,12 +27,14 @@ import org.jetbrains.asm4.MethodVisitor;
|
|||||||
import org.jetbrains.asm4.Type;
|
import org.jetbrains.asm4.Type;
|
||||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||||
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
|
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.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
|
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
import org.jetbrains.jet.lang.resolve.java.*;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
|
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.lang.types.lang.JetStandardLibrary;
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
|
|
||||||
@@ -417,4 +419,35 @@ public class AsmUtil {
|
|||||||
public static void genStubCode(MethodVisitor mv) {
|
public static void genStubCode(MethodVisitor mv) {
|
||||||
genMethodThrow(mv, STUB_EXCEPTION, STUB_EXCEPTION_MESSAGE);
|
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.Type;
|
||||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||||
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
|
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.JvmAbi;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||||
|
|
||||||
@@ -96,17 +98,26 @@ public class CallableMethod implements Callable {
|
|||||||
return receiverParameterType;
|
return receiverParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void invoke(InstructionAdapter v) {
|
private void invoke(InstructionAdapter v) {
|
||||||
v.visitMethodInsn(getInvokeOpcode(), owner.getInternalName(), getSignature().getAsmMethod().getName(),
|
v.visitMethodInsn(getInvokeOpcode(), owner.getInternalName(), getSignature().getAsmMethod().getName(),
|
||||||
getSignature().getAsmMethod().getDescriptor());
|
getSignature().getAsmMethod().getDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void invokeWithNotNullAssertion(
|
||||||
|
@NotNull InstructionAdapter v,
|
||||||
|
@NotNull GenerationState state,
|
||||||
|
@NotNull ResolvedCall resolvedCall
|
||||||
|
) {
|
||||||
|
invoke(v);
|
||||||
|
AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Type getGenerateCalleeType() {
|
public Type getGenerateCalleeType() {
|
||||||
return generateCalleeType;
|
return generateCalleeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invokeWithDefault(InstructionAdapter v, int mask) {
|
private void invokeDefault(InstructionAdapter v, int mask) {
|
||||||
if (defaultImplOwner == null || defaultImplParam == null) {
|
if (defaultImplOwner == null || defaultImplParam == null) {
|
||||||
throw new IllegalStateException();
|
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() {
|
public boolean isNeedsThis() {
|
||||||
return thisClass != null && generateCalleeType == null;
|
return thisClass != null && generateCalleeType == null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1863,10 +1863,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
int mask = pushMethodArguments(resolvedCall, callableMethod.getValueParameterTypes());
|
int mask = pushMethodArguments(resolvedCall, callableMethod.getValueParameterTypes());
|
||||||
if (mask == 0) {
|
if (mask == 0) {
|
||||||
callableMethod.invoke(v);
|
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||||
}
|
}
|
||||||
else {
|
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());
|
pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
|
||||||
callable.invoke(v);
|
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||||
if (keepReturnValue) {
|
if (keepReturnValue) {
|
||||||
value.store(callable.getReturnType(), v);
|
value.store(callable.getReturnType(), v);
|
||||||
}
|
}
|
||||||
@@ -2570,7 +2570,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
Type type = expressionType(expression.getBaseExpression());
|
Type type = expressionType(expression.getBaseExpression());
|
||||||
value.put(type, v);
|
value.put(type, v);
|
||||||
callableMethod.invoke(v);
|
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||||
|
|
||||||
value.store(callableMethod.getReturnType(), v);
|
value.store(callableMethod.getReturnType(), v);
|
||||||
value.put(type, v);
|
value.put(type, v);
|
||||||
return StackValue.onStack(type);
|
return StackValue.onStack(type);
|
||||||
@@ -2588,7 +2589,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
assert resolvedCall != null;
|
assert resolvedCall != null;
|
||||||
genThisAndReceiverFromResolvedCall(StackValue.none(), resolvedCall, callable);
|
genThisAndReceiverFromResolvedCall(StackValue.none(), resolvedCall, callable);
|
||||||
pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
|
pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
|
||||||
callable.invoke(v);
|
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||||
|
|
||||||
return returnValueAsStackValue(op, callable.getSignature().getAsmMethod().getReturnType());
|
return returnValueAsStackValue(op, callable.getSignature().getAsmMethod().getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2676,7 +2678,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
CallableMethod callableMethod = (CallableMethod) callable;
|
CallableMethod callableMethod = (CallableMethod) callable;
|
||||||
callableMethod.invoke(v);
|
callableMethod.invokeWithNotNullAssertion(v, state, resolvedCall);
|
||||||
|
|
||||||
value.store(callableMethod.getReturnType(), v);
|
value.store(callableMethod.getReturnType(), v);
|
||||||
return StackValue.onStack(type);
|
return StackValue.onStack(type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1341,7 +1341,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
iv.load(nextVar, t);
|
iv.load(nextVar, t);
|
||||||
nextVar += t.getSize();
|
nextVar += t.getSize();
|
||||||
}
|
}
|
||||||
superCallable.invoke(codegen.v);
|
superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
codegen.invokeMethodWithArguments(superCallable, (JetCallElement) superCall, StackValue.none());
|
codegen.invokeMethodWithArguments(superCallable, (JetCallElement) superCall, StackValue.none());
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ import org.jetbrains.jet.lexer.JetTokens;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.asm4.Opcodes.*;
|
import static org.jetbrains.asm4.Opcodes.*;
|
||||||
import static org.jetbrains.jet.codegen.AsmUtil.boxType;
|
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.jet.codegen.AsmUtil.isIntPrimitive;
|
|
||||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -678,7 +677,7 @@ public abstract class StackValue {
|
|||||||
throw new UnsupportedOperationException("no getter specified");
|
throw new UnsupportedOperationException("no getter specified");
|
||||||
}
|
}
|
||||||
if (getter instanceof CallableMethod) {
|
if (getter instanceof CallableMethod) {
|
||||||
((CallableMethod) getter).invoke(v);
|
((CallableMethod) getter).invokeWithNotNullAssertion(v, state, resolvedGetCall);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
((IntrinsicMethod) getter).generate(codegen, v, type, null, null, null, state);
|
((IntrinsicMethod) getter).generate(codegen, v, type, null, null, null, state);
|
||||||
@@ -696,7 +695,7 @@ public abstract class StackValue {
|
|||||||
Method asmMethod = method.getSignature().getAsmMethod();
|
Method asmMethod = method.getSignature().getAsmMethod();
|
||||||
Type[] argumentTypes = asmMethod.getArgumentTypes();
|
Type[] argumentTypes = asmMethod.getArgumentTypes();
|
||||||
coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
|
coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
|
||||||
method.invoke(v);
|
method.invokeWithNotNullAssertion(v, state, resolvedSetCall);
|
||||||
Type returnType = asmMethod.getReturnType();
|
Type returnType = asmMethod.getReturnType();
|
||||||
if (returnType != Type.VOID_TYPE) {
|
if (returnType != Type.VOID_TYPE) {
|
||||||
pop(returnType, v);
|
pop(returnType, v);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import jet.runtime.typeinfo.KotlinSignature;
|
||||||
|
|
||||||
|
public class A {
|
||||||
|
@KotlinSignature("fun foo() : String")
|
||||||
|
public String foo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@KotlinSignature("fun staticFoo() : String")
|
||||||
|
public static String staticFoo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@KotlinSignature("fun plus(a: A) : A")
|
||||||
|
public A plus(A a) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@KotlinSignature("fun inc() : A")
|
||||||
|
public A inc() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@KotlinSignature("fun get(o: Any) : Any")
|
||||||
|
public Object get(Object o) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
class AssertionChecker(val illegalStateExpected: Boolean) {
|
||||||
|
fun invoke(name: String, f: () -> Unit) {
|
||||||
|
try {
|
||||||
|
f()
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
if (!illegalStateExpected) throw AssertionError("Unexpected IllegalStateException on calling $name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (illegalStateExpected) throw AssertionError("IllegalStateException expected on calling $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
trait Tr {
|
||||||
|
fun foo(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived : A(), Tr {
|
||||||
|
override fun foo() = super<A>.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Delegated : Tr by Derived() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun checkAssertions(val illegalStateExpected: Boolean) {
|
||||||
|
val check = AssertionChecker(illegalStateExpected)
|
||||||
|
|
||||||
|
// simple call
|
||||||
|
check("foo") { A().foo() }
|
||||||
|
|
||||||
|
// simple static call
|
||||||
|
check("staticFoo") { A.staticFoo() }
|
||||||
|
|
||||||
|
// supercall
|
||||||
|
check("foo") { Derived().foo() }
|
||||||
|
|
||||||
|
// delegated call
|
||||||
|
check("foo") { Delegated().foo() }
|
||||||
|
|
||||||
|
// collection element
|
||||||
|
check("get") { A()[""] }
|
||||||
|
|
||||||
|
// binary expression
|
||||||
|
check("plus") { A() + A() }
|
||||||
|
|
||||||
|
// postfix expression
|
||||||
|
check("inc") { var a = A(); a++ }
|
||||||
|
|
||||||
|
// prefix expression
|
||||||
|
check("inc") { var a = A(); ++a }
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fun box(): String {
|
||||||
|
checkAssertions(true)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fun box(): String {
|
||||||
|
checkAssertions(false)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ import org.jetbrains.jet.lang.resolve.ScriptNameUtil;
|
|||||||
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
|
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
|
||||||
import org.jetbrains.jet.parsing.JetParsingTest;
|
import org.jetbrains.jet.parsing.JetParsingTest;
|
||||||
import org.jetbrains.jet.utils.ExceptionUtils;
|
import org.jetbrains.jet.utils.ExceptionUtils;
|
||||||
|
import org.jetbrains.jet.utils.Progress;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -356,7 +357,11 @@ public abstract class CodegenTestCase extends UsefulTestCase {
|
|||||||
BuiltinsScopeExtensionMode.ALL);
|
BuiltinsScopeExtensionMode.ALL);
|
||||||
analyzeExhaust.throwIfError();
|
analyzeExhaust.throwIfError();
|
||||||
AnalyzingUtils.throwExceptionOnErrors(analyzeExhaust.getBindingContext());
|
AnalyzingUtils.throwExceptionOnErrors(analyzeExhaust.getBindingContext());
|
||||||
alreadyGenerated = new GenerationState(myEnvironment.getProject(), classBuilderFactory, analyzeExhaust, myFiles.getPsiFiles());
|
alreadyGenerated = new GenerationState(
|
||||||
|
myEnvironment.getProject(), classBuilderFactory, Progress.DEAF, analyzeExhaust, myFiles.getPsiFiles(),
|
||||||
|
myEnvironment.getConfiguration().get(JVMConfigurationKeys.BUILTIN_TO_JAVA_TYPES_MAPPING_KEY, BuiltinToJavaTypesMapping.ENABLED),
|
||||||
|
myEnvironment.getConfiguration().get(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, true)
|
||||||
|
);
|
||||||
GenerationStrategy.STANDARD.compileCorrectFiles(alreadyGenerated, CompilationErrorHandler.THROW_EXCEPTION);
|
GenerationStrategy.STANDARD.compileCorrectFiles(alreadyGenerated, CompilationErrorHandler.THROW_EXCEPTION);
|
||||||
return alreadyGenerated;
|
return alreadyGenerated;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.codegen;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.jet.CompileCompilerDependenciesTest;
|
||||||
|
import org.jetbrains.jet.ConfigurationKind;
|
||||||
|
import org.jetbrains.jet.JetTestUtils;
|
||||||
|
import org.jetbrains.jet.TestJdkKind;
|
||||||
|
import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
|
||||||
|
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
|
||||||
|
import org.jetbrains.jet.config.CompilerConfiguration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author udalov
|
||||||
|
*/
|
||||||
|
public class GenerateNotNullAssertionsTest extends CodegenTestCase {
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpEnvironment(boolean generateAssertions, File... extraClassPath) {
|
||||||
|
CompilerConfiguration configuration = CompileCompilerDependenciesTest.compilerConfigurationForTests(
|
||||||
|
ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK, extraClassPath);
|
||||||
|
|
||||||
|
configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, generateAssertions);
|
||||||
|
|
||||||
|
myEnvironment = new JetCoreEnvironment(getTestRootDisposable(), configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestGenerateAssertions(boolean generateAssertions, String ktFile) throws Exception {
|
||||||
|
File javaClassesTempDirectory = compileJava("notNullAssertions/A.java");
|
||||||
|
|
||||||
|
setUpEnvironment(generateAssertions, javaClassesTempDirectory);
|
||||||
|
|
||||||
|
blackBoxMultiFile("OK", false, "notNullAssertions/AssertionChecker.kt", ktFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGenerateAssertions() throws Exception {
|
||||||
|
doTestGenerateAssertions(true, "notNullAssertions/doGenerateAssertions.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoNotGenerateAssertions() throws Exception {
|
||||||
|
doTestGenerateAssertions(false, "notNullAssertions/doNotGenerateAssertions.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,9 +18,7 @@ package jet.runtime;
|
|||||||
|
|
||||||
import jet.Function0;
|
import jet.Function0;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author alex.tkachman
|
* @author alex.tkachman
|
||||||
@@ -37,6 +35,14 @@ public class Intrinsics {
|
|||||||
throw new JetNullPointerException();
|
throw new JetNullPointerException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkReturnedValueIsNotNull(Object value, String className, String methodName) {
|
||||||
|
if (value == null) {
|
||||||
|
IllegalStateException exception =
|
||||||
|
new IllegalStateException("Method specified as non-null returned null: " + className + "." + methodName);
|
||||||
|
throw sanitizeStackTrace(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Class<T> getJavaClass(T self) {
|
public static <T> Class<T> getJavaClass(T self) {
|
||||||
return (Class<T>) self.getClass();
|
return (Class<T>) self.getClass();
|
||||||
}
|
}
|
||||||
@@ -59,7 +65,11 @@ public class Intrinsics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Throwable sanitizeStackTrace(Throwable throwable) {
|
private static final Set<String> METHOD_NAMES_TO_SKIP = new HashSet<String>(Arrays.asList(
|
||||||
|
"throwNpe", "checkReturnedValueIsNotNull"
|
||||||
|
));
|
||||||
|
|
||||||
|
private static <T extends Throwable> T sanitizeStackTrace(T throwable) {
|
||||||
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
||||||
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
|
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
|
||||||
boolean skip = true;
|
boolean skip = true;
|
||||||
@@ -68,8 +78,10 @@ public class Intrinsics {
|
|||||||
list.add(ste);
|
list.add(ste);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if("jet.runtime.Intrinsics".equals(ste.getClassName()) && "throwNpe".equals(ste.getMethodName())) {
|
if ("jet.runtime.Intrinsics".equals(ste.getClassName())) {
|
||||||
skip = false;
|
if (METHOD_NAMES_TO_SKIP.contains(ste.getMethodName())) {
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user