tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)