3 #include "js/ErrorInterceptor.h"
4 #include "jsapi-tests/tests.h"
5 #include "util/StringBuffer.h"
7 // Tests for JS_GetErrorInterceptorCallback and JS_SetErrorInterceptorCallback.
10 static JS::PersistentRootedString gLatestMessage
;
12 // An interceptor that stores the error in `gLatestMessage`.
13 struct SimpleInterceptor
: JSErrorInterceptor
{
14 virtual void interceptError(JSContext
* cx
, JS::HandleValue val
) override
{
15 js::JSStringBuilder
buffer(cx
);
16 if (!ValueToStringBuffer(cx
, val
, buffer
)) {
17 MOZ_CRASH("Could not convert to string buffer");
19 gLatestMessage
= buffer
.finishString();
20 if (!gLatestMessage
) {
21 MOZ_CRASH("Could not convert to string");
26 bool equalStrings(JSContext
* cx
, JSString
* a
, JSString
* b
) {
28 if (!JS_CompareStrings(cx
, a
, b
, &result
)) {
29 MOZ_CRASH("Could not compare strings");
35 BEGIN_TEST(testErrorInterceptor
) {
36 // Run the following snippets.
37 const char* SAMPLES
[] = {
38 "throw new Error('I am an Error')\0",
39 "throw new TypeError('I am a TypeError')\0",
40 "throw new ReferenceError('I am a ReferenceError')\0",
41 "throw new SyntaxError('I am a SyntaxError')\0",
46 // With the simpleInterceptor, we should end up with the following error:
47 const char* TO_STRING
[] = {
48 "Error: I am an Error\0",
49 "TypeError: I am a TypeError\0",
50 "ReferenceError: I am a ReferenceError\0",
51 "SyntaxError: I am a SyntaxError\0",
53 "ReferenceError: foo is not defined\0",
54 "SyntaxError: expected expression, got end of script\0",
56 static_assert(std::size(SAMPLES
) == std::size(TO_STRING
));
58 // Save original callback.
59 JSErrorInterceptor
* original
= JS_GetErrorInterceptorCallback(cx
->runtime());
60 gLatestMessage
.init(cx
);
62 // Test without callback.
63 JS_SetErrorInterceptorCallback(cx
->runtime(), nullptr);
64 CHECK(gLatestMessage
== nullptr);
66 for (auto sample
: SAMPLES
) {
67 if (execDontReport(sample
, __FILE__
, __LINE__
)) {
68 MOZ_CRASH("This sample should have failed");
70 CHECK(JS_IsExceptionPending(cx
));
71 CHECK(gLatestMessage
== nullptr);
72 JS_ClearPendingException(cx
);
75 // Test with callback.
76 SimpleInterceptor simpleInterceptor
;
77 JS_SetErrorInterceptorCallback(cx
->runtime(), &simpleInterceptor
);
79 // Test that we return the right callback.
80 CHECK_EQUAL(JS_GetErrorInterceptorCallback(cx
->runtime()),
83 // This shouldn't cause any error.
84 EXEC("function bar() {}");
85 CHECK(gLatestMessage
== nullptr);
87 // Test error throwing with a callback that succeeds.
88 for (size_t i
= 0; i
< std::size(SAMPLES
); ++i
) {
89 // This should cause the appropriate error.
90 if (execDontReport(SAMPLES
[i
], __FILE__
, __LINE__
)) {
91 MOZ_CRASH("This sample should have failed");
93 CHECK(JS_IsExceptionPending(cx
));
95 // Check result of callback.
96 CHECK(gLatestMessage
!= nullptr);
97 CHECK(js::StringEqualsAscii(&gLatestMessage
->asLinear(), TO_STRING
[i
]));
99 // Check the final error.
100 JS::RootedValue
exn(cx
);
101 CHECK(JS_GetPendingException(cx
, &exn
));
102 JS_ClearPendingException(cx
);
104 js::JSStringBuilder
buffer(cx
);
105 CHECK(ValueToStringBuffer(cx
, exn
, buffer
));
106 JS::Rooted
<JSLinearString
*> linear(cx
, buffer
.finishString());
107 CHECK(equalStrings(cx
, linear
, gLatestMessage
));
110 gLatestMessage
= nullptr;
113 // Test again without callback.
114 JS_SetErrorInterceptorCallback(cx
->runtime(), nullptr);
115 for (size_t i
= 0; i
< std::size(SAMPLES
); ++i
) {
116 if (execDontReport(SAMPLES
[i
], __FILE__
, __LINE__
)) {
117 MOZ_CRASH("This sample should have failed");
119 CHECK(JS_IsExceptionPending(cx
));
121 // Check that the callback wasn't called.
122 CHECK(gLatestMessage
== nullptr);
124 // Check the final error.
125 JS::RootedValue
exn(cx
);
126 CHECK(JS_GetPendingException(cx
, &exn
));
127 JS_ClearPendingException(cx
);
129 js::JSStringBuilder
buffer(cx
);
130 CHECK(ValueToStringBuffer(cx
, exn
, buffer
));
131 JS::Rooted
<JSLinearString
*> linear(cx
, buffer
.finishString());
132 CHECK(js::StringEqualsAscii(linear
, TO_STRING
[i
]));
135 gLatestMessage
= nullptr;
139 JS_SetErrorInterceptorCallback(cx
->runtime(), original
);
140 gLatestMessage
= nullptr;
141 JS_ClearPendingException(cx
);
145 END_TEST(testErrorInterceptor
)