testErrorInterceptor.cpp (4477B)
1 #include "js/ErrorInterceptor.h" 2 #include "jsapi-tests/tests.h" 3 #include "util/StringBuilder.h" 4 5 // Tests for JS_GetErrorInterceptorCallback and JS_SetErrorInterceptorCallback. 6 7 namespace { 8 MOZ_RUNINIT static JS::PersistentRootedString gLatestMessage; 9 10 // An interceptor that stores the error in `gLatestMessage`. 11 struct SimpleInterceptor : JSErrorInterceptor { 12 virtual void interceptError(JSContext* cx, JS::HandleValue val) override { 13 js::JSStringBuilder buffer(cx); 14 if (!ValueToStringBuilder(cx, val, buffer)) { 15 MOZ_CRASH("Could not convert to string buffer"); 16 } 17 gLatestMessage = buffer.finishString(); 18 if (!gLatestMessage) { 19 MOZ_CRASH("Could not convert to string"); 20 } 21 } 22 }; 23 24 bool equalStrings(JSContext* cx, JSString* a, JSString* b) { 25 int32_t result = 0; 26 if (!JS_CompareStrings(cx, a, b, &result)) { 27 MOZ_CRASH("Could not compare strings"); 28 } 29 return result == 0; 30 } 31 } // namespace 32 33 BEGIN_TEST(testErrorInterceptor) { 34 // Run the following snippets. 35 const char* SAMPLES[] = { 36 "throw new Error('I am an Error')\0", 37 "throw new TypeError('I am a TypeError')\0", 38 "throw new ReferenceError('I am a ReferenceError')\0", 39 "throw new SyntaxError('I am a SyntaxError')\0", 40 "throw 5\0", 41 "foo[0]\0", 42 "b[\0", 43 }; 44 // With the simpleInterceptor, we should end up with the following error: 45 const char* TO_STRING[] = { 46 "Error: I am an Error\0", 47 "TypeError: I am a TypeError\0", 48 "ReferenceError: I am a ReferenceError\0", 49 "SyntaxError: I am a SyntaxError\0", 50 "5\0", 51 "ReferenceError: foo is not defined\0", 52 "SyntaxError: expected expression, got end of script\0", 53 }; 54 static_assert(std::size(SAMPLES) == std::size(TO_STRING)); 55 56 // Save original callback. 57 JSErrorInterceptor* original = JS_GetErrorInterceptorCallback(cx->runtime()); 58 gLatestMessage.init(cx); 59 60 // Test without callback. 61 JS_SetErrorInterceptorCallback(cx->runtime(), nullptr); 62 CHECK(gLatestMessage == nullptr); 63 64 for (auto sample : SAMPLES) { 65 if (execDontReport(sample, __FILE__, __LINE__)) { 66 MOZ_CRASH("This sample should have failed"); 67 } 68 CHECK(JS_IsExceptionPending(cx)); 69 CHECK(gLatestMessage == nullptr); 70 JS_ClearPendingException(cx); 71 } 72 73 // Test with callback. 74 SimpleInterceptor simpleInterceptor; 75 JS_SetErrorInterceptorCallback(cx->runtime(), &simpleInterceptor); 76 77 // Test that we return the right callback. 78 CHECK_EQUAL(JS_GetErrorInterceptorCallback(cx->runtime()), 79 &simpleInterceptor); 80 81 // This shouldn't cause any error. 82 EXEC("function bar() {}"); 83 CHECK(gLatestMessage == nullptr); 84 85 // Test error throwing with a callback that succeeds. 86 for (size_t i = 0; i < std::size(SAMPLES); ++i) { 87 // This should cause the appropriate error. 88 if (execDontReport(SAMPLES[i], __FILE__, __LINE__)) { 89 MOZ_CRASH("This sample should have failed"); 90 } 91 CHECK(JS_IsExceptionPending(cx)); 92 93 // Check result of callback. 94 CHECK(gLatestMessage != nullptr); 95 CHECK(js::StringEqualsAscii(&gLatestMessage->asLinear(), TO_STRING[i])); 96 97 // Check the final error. 98 JS::RootedValue exn(cx); 99 CHECK(JS_GetPendingException(cx, &exn)); 100 JS_ClearPendingException(cx); 101 102 js::JSStringBuilder buffer(cx); 103 CHECK(ValueToStringBuilder(cx, exn, buffer)); 104 JS::Rooted<JSLinearString*> linear(cx, buffer.finishString()); 105 CHECK(equalStrings(cx, linear, gLatestMessage)); 106 107 // Cleanup. 108 gLatestMessage = nullptr; 109 } 110 111 // Test again without callback. 112 JS_SetErrorInterceptorCallback(cx->runtime(), nullptr); 113 for (size_t i = 0; i < std::size(SAMPLES); ++i) { 114 if (execDontReport(SAMPLES[i], __FILE__, __LINE__)) { 115 MOZ_CRASH("This sample should have failed"); 116 } 117 CHECK(JS_IsExceptionPending(cx)); 118 119 // Check that the callback wasn't called. 120 CHECK(gLatestMessage == nullptr); 121 122 // Check the final error. 123 JS::RootedValue exn(cx); 124 CHECK(JS_GetPendingException(cx, &exn)); 125 JS_ClearPendingException(cx); 126 127 js::JSStringBuilder buffer(cx); 128 CHECK(ValueToStringBuilder(cx, exn, buffer)); 129 JS::Rooted<JSLinearString*> linear(cx, buffer.finishString()); 130 CHECK(js::StringEqualsAscii(linear, TO_STRING[i])); 131 132 // Cleanup. 133 gLatestMessage = nullptr; 134 } 135 136 // Cleanup 137 JS_SetErrorInterceptorCallback(cx->runtime(), original); 138 gLatestMessage = nullptr; 139 JS_ClearPendingException(cx); 140 141 return true; 142 } 143 END_TEST(testErrorInterceptor)