diff --git a/infer/tests/codetoanalyze/java/crashcontext/BUCK b/infer/tests/codetoanalyze/java/crashcontext/BUCK index aa6cdebbe..a9005be41 100644 --- a/infer/tests/codetoanalyze/java/crashcontext/BUCK +++ b/infer/tests/codetoanalyze/java/crashcontext/BUCK @@ -58,6 +58,11 @@ infer_cmds = [ "MethodNameClashExample", "MethodNameClashExample.java", "MethodNameClashExample.stacktrace.json" + ), + mk_infer_cmd( + "NativeMethodExample", + "NativeMethodExample.java", + "NativeMethodExample.stacktrace.json" ) ] diff --git a/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.java b/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.java new file mode 100644 index 000000000..b8e1384a2 --- /dev/null +++ b/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package codetoanalyze.java.crashcontext; + +import java.lang.reflect.Method; + +public class NativeMethodExample { + + public static void foo() { + String s = null; + s.toString(); + } + + public static void main(String[] args) { + try { + // Calling method.invoke is a reliable way of getting a native method + // in the stack (from the implementation of reflection) between this + // method and the target of the reflective invocation. + Method method = NativeMethodExample.class.getDeclaredMethod("foo"); + Object o = method.invoke(new Object[] {} ); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + +} diff --git a/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.stacktrace.json b/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.stacktrace.json new file mode 100644 index 000000000..05e11265d --- /dev/null +++ b/infer/tests/codetoanalyze/java/crashcontext/NativeMethodExample.stacktrace.json @@ -0,0 +1 @@ +{"exception_type": "java.lang.NullPointerException", "stack_trace": ["at codetoanalyze.java.crashcontext.NativeMethodExample.foo(NativeMethodExample.java:18)","at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)","at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)","at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)","at java.lang.reflect.Method.invoke(Method.java:497)","at codetoanalyze.java.crashcontext.NativeMethodExample.main(NativeMethodExample.java:27)",""], "exception_message": "", "normvector_stack": ["codetoanalyze.java.crashcontext.NativeMethodExample.foo","sun.reflect.NativeMethodAccessorImpl.invoke0","sun.reflect.NativeMethodAccessorImpl.invoke","at sun.reflect.DelegatingMethodAccessorImpl.invoke","java.lang.reflect.Method.invoke","codetoanalyze.java.crashcontext.NativeMethodExample.main",""]} diff --git a/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java b/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java new file mode 100644 index 000000000..867a4d99a --- /dev/null +++ b/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package endtoend.java.crashcontext; + +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import utils.CrashContextResults; + +public class NativeMethodTest { + + public static final String TAG = "NativeMethodExample"; + + public static final String MAIN_METHOD = + "codetoanalyze.java.crashcontext.NativeMethodExample.main(java.lang.String[]):void"; + + // Currently, native methods are missing their arguments lists. + public static final String REFLECTION_INVOKE_METHOD = + "sun.reflect.NativeMethodAccessorImpl.invoke0"; + + public static final String FOO_METHOD = + "codetoanalyze.java.crashcontext.NativeMethodExample.foo():void"; + + private static CrashContextResults crashcontext; + + @BeforeClass + public static void loadResults() throws IOException { + crashcontext = + CrashContextResults.loadJSONResults(TAG); + } + + @Test + public void shapeOfTheStack() { + assertThat("The stack trace should contain " + FOO_METHOD, + crashcontext.hasStackFrame(FOO_METHOD)); + assertThat("The trace should contain at least one native method ", + crashcontext.hasLocationOnStack("Native Method", -1)); + assertThat("The stack trace should contain " + REFLECTION_INVOKE_METHOD, + crashcontext.hasStackFrame(REFLECTION_INVOKE_METHOD)); + assertThat("The stack trace should contain " + MAIN_METHOD, + crashcontext.hasStackFrame(MAIN_METHOD)); + } + +} diff --git a/infer/tests/utils/CrashContextResults.java b/infer/tests/utils/CrashContextResults.java index 5aa33ff57..2b4f826ef 100644 --- a/infer/tests/utils/CrashContextResults.java +++ b/infer/tests/utils/CrashContextResults.java @@ -49,6 +49,16 @@ public class CrashContextResults { return false; } + public boolean hasLocationOnStack(String filename, int line) { + for (JsonNode frame : json.path("stack")) { + if (filename.equals(frame.path("location").path("file").asText()) && + line == frame.path("location").path("line").asInt()) { + return true; + } + } + return false; + } + private List findNodesForMethod(JsonNode node, String methodSignature, List accumulator) {