diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java index e86d46bfb8e..2edc35848db 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java @@ -87,6 +87,7 @@ public class IntrinsicMethods { declareOverload(myStdLib.getLibraryScope().getFunctions("identityEquals"), 1, EQUALS); declareOverload(myStdLib.getLibraryScope().getFunctions("plus"), 1, new StringPlus()); declareOverload(myStdLib.getLibraryScope().getFunctions("Array"), 1, new NewArray()); + declareOverload(myStdLib.getLibraryScope().getFunctions("npe"), 0, new NPE()); declareIntrinsicFunction("ByteIterator", "next", 0, ITERATOR_NEXT); declareIntrinsicFunction("ShortIterator", "next", 0, ITERATOR_NEXT); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/NPE.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/NPE.java new file mode 100644 index 00000000000..c0bd922f973 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/NPE.java @@ -0,0 +1,24 @@ +package org.jetbrains.jet.codegen.intrinsics; + +import com.intellij.psi.PsiElement; +import org.jetbrains.jet.codegen.ExpressionCodegen; +import org.jetbrains.jet.codegen.JetTypeMapper; +import org.jetbrains.jet.codegen.StackValue; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +import java.util.List; + +/** + * @author alex.tkachman + */ +public class NPE implements IntrinsicMethod { + @Override + public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List arguments, StackValue receiver) { + receiver.put(JetTypeMapper.TYPE_OBJECT, v); + v.invokestatic("jet/runtime/Intrinsics", "npe", "(Ljava/lang/Object;)Ljava/lang/Object;"); + StackValue.onStack(JetTypeMapper.TYPE_OBJECT).put(expectedType, v); + return StackValue.onStack(expectedType); + } +} diff --git a/compiler/frontend/src/jet/Library.jet b/compiler/frontend/src/jet/Library.jet index 279171d8776..f085b8012b2 100644 --- a/compiler/frontend/src/jet/Library.jet +++ b/compiler/frontend/src/jet/Library.jet @@ -42,9 +42,7 @@ fun Any?.equals(other : Any?) : Boolean// = this === other // Returns "null" for null fun Any?.toString() : String// = this === other -// fun array(vararg elements : T) : Array - -// fun array(vararg elements : Int) : IntArray +fun T?.npe() : T fun String?.plus(other: Any?) : String diff --git a/compiler/tests/org/jetbrains/jet/runtime/JetNpeTest.java b/compiler/tests/org/jetbrains/jet/runtime/JetNpeTest.java new file mode 100644 index 00000000000..cf0ff45814a --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/runtime/JetNpeTest.java @@ -0,0 +1,32 @@ +package org.jetbrains.jet.runtime; + +import jet.runtime.Intrinsics; +import junit.framework.TestCase; +import org.jetbrains.jet.codegen.CodegenTestCase; + +import java.lang.reflect.Method; + +public class JetNpeTest extends CodegenTestCase { + public void testStackTrace () { + try { + Intrinsics.npe(null); + fail("No NPE thrown"); + } + catch (NullPointerException e) { + StackTraceElement stackTraceElement = e.getStackTrace()[0]; + assertEquals(stackTraceElement.getMethodName(), "testStackTrace"); + assertEquals(stackTraceElement.getClassName(), "org.jetbrains.jet.runtime.JetNpeTest"); + } + } + + public void testNotNull () throws Exception { + loadText("fun box() = if(10.npe() == 10) \"OK\" else \"fail\""); + blackBox(); + } + + public void testNull () throws Exception { + loadText("fun box() = if(null.npe() == 10) \"OK\" else \"fail\""); + Method box = generateFunction("box"); + assertThrows(box, NullPointerException.class, null); + } +} diff --git a/stdlib/src/jet/runtime/Intrinsics.java b/stdlib/src/jet/runtime/Intrinsics.java index 9243275e611..9f26a0f72b5 100644 --- a/stdlib/src/jet/runtime/Intrinsics.java +++ b/stdlib/src/jet/runtime/Intrinsics.java @@ -10,4 +10,14 @@ public class Intrinsics { public static String stringPlus(String self, Object other) { return ((self == null) ? "null" : self) + ((other == null) ? "null" : other.toString()); } + + public static Object npe(Object self) { + if(self == null) + return throwNpe(); + return self; + } + + private static Object throwNpe() { + throw new JetNullPointerException(); + } } diff --git a/stdlib/src/jet/runtime/JetNullPointerException.java b/stdlib/src/jet/runtime/JetNullPointerException.java new file mode 100644 index 00000000000..ec9c6a0c1d5 --- /dev/null +++ b/stdlib/src/jet/runtime/JetNullPointerException.java @@ -0,0 +1,32 @@ +package jet.runtime; + +import java.util.ArrayList; + +/** +* @author alex.tkachman +*/ +class JetNullPointerException extends NullPointerException { + @Override + public synchronized Throwable fillInStackTrace() { + super.fillInStackTrace(); + StackTraceElement[] stackTrace = getStackTrace(); + ArrayList list = new ArrayList(); + boolean skip = true; + for(StackTraceElement ste : stackTrace) { + if(!skip) { + list.add(ste); + } + else { + if("jet.runtime.Intrinsics".equals(ste.getClassName()) && "npe".equals(ste.getMethodName())) { + skip = false; + } + } + } + setStackTrace(list.toArray(new StackTraceElement[list.size()])); + return this; + } + + public static void main(String[] args) { + Intrinsics.npe(null); + } +}