tor-browser

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

testFrontendErrors.cpp (10765B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/RefPtr.h"  // RefPtr
      8 #include "mozilla/Utf8.h"    // mozilla::Utf8Unit
      9 
     10 #include <string.h>  // strcmp
     11 
     12 #include "frontend/FrontendContext.h"  // js::FrontendContext
     13 #include "js/AllocPolicy.h"            // js::ReportOutOfMemory
     14 #include "js/CompileOptions.h"  // JS::PrefableCompileOptions, JS::CompileOptions
     15 #include "js/Exception.h"  // JS_IsExceptionPending, JS_IsThrowingOutOfMemory, JS_GetPendingException, JS_ClearPendingException, JS_ErrorFromException
     16 #include "js/experimental/CompileScript.h"  // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::CompileGlobalScriptToStencil, JS::ConvertFrontendErrorsToRuntimeErrors, JS::HadFrontendErrors, JS::HadFrontendOverRecursed, JS::HadFrontendOutOfMemory, JS::HadFrontendAllocationOverflow, JS::GetFrontendWarningCount, JS::GetFrontendWarningAt
     17 #include "js/friend/ErrorMessages.h"        // JSMSG_*
     18 #include "js/friend/StackLimits.h"          // js::ReportOverRecursed
     19 #include "js/RootingAPI.h"                  // JS::Rooted
     20 #include "js/SourceText.h"                  // JS::SourceText
     21 #include "js/Stack.h"                       // JS::NativeStackSize
     22 #include "js/Warnings.h"                    // JS::SetWarningReporter
     23 #include "jsapi-tests/tests.h"
     24 #include "util/NativeStack.h"  // js::GetNativeStackBase
     25 #include "vm/ErrorObject.h"    // js::ErrorObject
     26 #include "vm/JSContext.h"      // JSContext
     27 #include "vm/JSObject.h"       // JSObject
     28 #include "vm/NativeObject.h"   // js::NativeObject
     29 #include "vm/Runtime.h"        // js::ReportAllocationOverflow
     30 
     31 using namespace JS;
     32 
     33 BEGIN_TEST(testFrontendErrors_error) {
     34  JS::FrontendContext* fc = JS::NewFrontendContext();
     35  CHECK(fc);
     36 
     37  static constexpr JS::NativeStackSize stackSize = 128 * sizeof(size_t) * 1024;
     38 
     39  JS::SetNativeStackQuota(fc, stackSize);
     40 
     41  JS::PrefableCompileOptions prefableOptions;
     42  JS::CompileOptions options(prefableOptions);
     43  const char* filename = "testFrontendErrors_error.js";
     44  options.setFile(filename);
     45 
     46  CHECK(!JS::HadFrontendErrors(fc));
     47 
     48  {
     49    const char source[] = "syntax error";
     50 
     51    JS::SourceText<mozilla::Utf8Unit> srcBuf;
     52    CHECK(
     53        srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed));
     54    RefPtr<JS::Stencil> stencil =
     55        JS::CompileGlobalScriptToStencil(fc, options, srcBuf);
     56    CHECK(!stencil);
     57  }
     58 
     59  CHECK(JS::HadFrontendErrors(fc));
     60  CHECK(!JS::HadFrontendOverRecursed(fc));
     61  CHECK(!JS::HadFrontendOutOfMemory(fc));
     62  CHECK(!JS::HadFrontendAllocationOverflow(fc));
     63  CHECK(JS::GetFrontendWarningCount(fc) == 0);
     64 
     65  {
     66    const JSErrorReport* report = JS::GetFrontendErrorReport(fc, options);
     67    CHECK(report);
     68 
     69    CHECK(report->errorNumber == JSMSG_UNEXPECTED_TOKEN_NO_EXPECT);
     70    // FrontendContext's error report borrows the filename.
     71    CHECK(report->filename.c_str() == filename);
     72  }
     73 
     74  CHECK(!JS_IsExceptionPending(cx));
     75 
     76  JS::SetWarningReporter(cx, warningReporter);
     77 
     78  bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options);
     79  CHECK(result);
     80 
     81  CHECK(JS_IsExceptionPending(cx));
     82  CHECK(!warningReporterCalled);
     83  CHECK(!JS_IsThrowingOutOfMemory(cx));
     84 
     85  {
     86    JS::Rooted<JS::Value> exception(cx);
     87    CHECK(JS_GetPendingException(cx, &exception));
     88 
     89    CHECK(exception.isObject());
     90    JS::Rooted<JSObject*> exceptionObj(cx, &exception.toObject());
     91 
     92    const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj);
     93    CHECK(report);
     94 
     95    CHECK(report->errorNumber == JSMSG_UNEXPECTED_TOKEN_NO_EXPECT);
     96    // Runtime's error report doesn't borrow the filename.
     97    CHECK(report->filename.c_str() != filename);
     98    CHECK(strcmp(report->filename.c_str(), filename) == 0);
     99  }
    100 
    101  JS_ClearPendingException(cx);
    102 
    103  JS::DestroyFrontendContext(fc);
    104 
    105  return true;
    106 }
    107 
    108 static bool warningReporterCalled;
    109 
    110 static void warningReporter(JSContext* cx, JSErrorReport* report) {
    111  warningReporterCalled = true;
    112 }
    113 END_TEST(testFrontendErrors_error)
    114 
    115 /* static */ bool cls_testFrontendErrors_error::warningReporterCalled = false;
    116 
    117 BEGIN_TEST(testFrontendErrors_warning) {
    118  JS::FrontendContext* fc = JS::NewFrontendContext();
    119  CHECK(fc);
    120 
    121  static constexpr JS::NativeStackSize stackSize = 128 * sizeof(size_t) * 1024;
    122 
    123  JS::SetNativeStackQuota(fc, stackSize);
    124 
    125  JS::PrefableCompileOptions prefableOptions;
    126  JS::CompileOptions options(prefableOptions);
    127  options.setFile(filename);
    128 
    129  {
    130    const char source[] = "function f() { return; f(); }";
    131 
    132    JS::SourceText<mozilla::Utf8Unit> srcBuf;
    133    CHECK(
    134        srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed));
    135    RefPtr<JS::Stencil> stencil =
    136        JS::CompileGlobalScriptToStencil(fc, options, srcBuf);
    137    CHECK(stencil);
    138  }
    139 
    140  CHECK(!JS::HadFrontendErrors(fc));
    141  CHECK(!JS::HadFrontendOverRecursed(fc));
    142  CHECK(!JS::HadFrontendOutOfMemory(fc));
    143  CHECK(!JS::HadFrontendAllocationOverflow(fc));
    144  CHECK(JS::GetFrontendWarningCount(fc) == 1);
    145 
    146  {
    147    const JSErrorReport* report = JS::GetFrontendWarningAt(fc, 0, options);
    148    CHECK(report);
    149 
    150    CHECK(report->errorNumber == JSMSG_STMT_AFTER_RETURN);
    151    // FrontendContext's error report borrows the filename.
    152    CHECK(report->filename.c_str() == filename);
    153  }
    154 
    155  CHECK(!JS_IsExceptionPending(cx));
    156 
    157  JS::SetWarningReporter(cx, warningReporter);
    158 
    159  bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options);
    160  CHECK(result);
    161 
    162  CHECK(!JS_IsExceptionPending(cx));
    163  CHECK(warningReporterCalled);
    164  CHECK(!JS_IsThrowingOutOfMemory(cx));
    165 
    166  CHECK(errorNumberMatches);
    167  CHECK(filenameMatches);
    168 
    169  JS::DestroyFrontendContext(fc);
    170 
    171  return true;
    172 }
    173 
    174 static const char* filename;
    175 static bool warningReporterCalled;
    176 static bool errorNumberMatches;
    177 static bool filenameMatches;
    178 
    179 static void warningReporter(JSContext* cx, JSErrorReport* report) {
    180  warningReporterCalled = true;
    181 
    182  errorNumberMatches = report->errorNumber == JSMSG_STMT_AFTER_RETURN;
    183  filenameMatches = report->filename.c_str() == filename;
    184 }
    185 END_TEST(testFrontendErrors_warning)
    186 
    187 /* static */ const char* cls_testFrontendErrors_warning::filename =
    188    "testFrontendErrors_warning.js";
    189 /* static */ bool cls_testFrontendErrors_warning::warningReporterCalled = false;
    190 /* static */ bool cls_testFrontendErrors_warning::errorNumberMatches = false;
    191 /* static */ bool cls_testFrontendErrors_warning::filenameMatches = false;
    192 
    193 BEGIN_TEST(testFrontendErrors_oom) {
    194  JS::FrontendContext* fc = JS::NewFrontendContext();
    195  CHECK(fc);
    196 
    197  JS::PrefableCompileOptions prefableOptions;
    198  JS::CompileOptions options(prefableOptions);
    199 
    200  CHECK(!JS::HadFrontendErrors(fc));
    201 
    202  js::ReportOutOfMemory(fc);
    203 
    204  CHECK(JS::HadFrontendErrors(fc));
    205  CHECK(!JS::HadFrontendOverRecursed(fc));
    206  CHECK(JS::HadFrontendOutOfMemory(fc));
    207  CHECK(!JS::HadFrontendAllocationOverflow(fc));
    208  CHECK(JS::GetFrontendWarningCount(fc) == 0);
    209 
    210  CHECK(!JS_IsExceptionPending(cx));
    211 
    212  JS::SetWarningReporter(cx, warningReporter);
    213 
    214  bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options);
    215  CHECK(!result);
    216 
    217  CHECK(JS_IsExceptionPending(cx));
    218  CHECK(!warningReporterCalled);
    219  CHECK(JS_IsThrowingOutOfMemory(cx));
    220 
    221  JS_ClearPendingException(cx);
    222 
    223  JS::DestroyFrontendContext(fc);
    224 
    225  return true;
    226 }
    227 
    228 static bool warningReporterCalled;
    229 
    230 static void warningReporter(JSContext* cx, JSErrorReport* report) {
    231  warningReporterCalled = true;
    232 }
    233 END_TEST(testFrontendErrors_oom)
    234 
    235 /* static */ bool cls_testFrontendErrors_oom::warningReporterCalled = false;
    236 
    237 BEGIN_TEST(testFrontendErrors_overRecursed) {
    238  JS::FrontendContext* fc = JS::NewFrontendContext();
    239  CHECK(fc);
    240 
    241  JS::PrefableCompileOptions prefableOptions;
    242  JS::CompileOptions options(prefableOptions);
    243 
    244  CHECK(!JS::HadFrontendErrors(fc));
    245 
    246  js::ReportOverRecursed(fc);
    247 
    248  CHECK(JS::HadFrontendErrors(fc));
    249  CHECK(JS::HadFrontendOverRecursed(fc));
    250  CHECK(!JS::HadFrontendOutOfMemory(fc));
    251  CHECK(!JS::HadFrontendAllocationOverflow(fc));
    252  CHECK(JS::GetFrontendWarningCount(fc) == 0);
    253 
    254  CHECK(!JS_IsExceptionPending(cx));
    255 
    256  JS::SetWarningReporter(cx, warningReporter);
    257 
    258  bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options);
    259  CHECK(result);
    260 
    261  CHECK(JS_IsExceptionPending(cx));
    262  CHECK(!warningReporterCalled);
    263  CHECK(!JS_IsThrowingOutOfMemory(cx));
    264 
    265  {
    266    JS::Rooted<JS::Value> exception(cx);
    267    CHECK(JS_GetPendingException(cx, &exception));
    268 
    269    CHECK(exception.isObject());
    270    JS::Rooted<JSObject*> exceptionObj(cx, &exception.toObject());
    271 
    272    const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj);
    273    CHECK(report);
    274 
    275    CHECK(report->errorNumber == JSMSG_OVER_RECURSED);
    276  }
    277 
    278  JS_ClearPendingException(cx);
    279 
    280  JS::DestroyFrontendContext(fc);
    281 
    282  return true;
    283 }
    284 
    285 static bool warningReporterCalled;
    286 
    287 static void warningReporter(JSContext* cx, JSErrorReport* report) {
    288  warningReporterCalled = true;
    289 }
    290 END_TEST(testFrontendErrors_overRecursed)
    291 
    292 /* static */ bool cls_testFrontendErrors_overRecursed::warningReporterCalled =
    293    false;
    294 
    295 BEGIN_TEST(testFrontendErrors_allocationOverflow) {
    296  JS::FrontendContext* fc = JS::NewFrontendContext();
    297  CHECK(fc);
    298 
    299  JS::PrefableCompileOptions prefableOptions;
    300  JS::CompileOptions options(prefableOptions);
    301 
    302  CHECK(!JS::HadFrontendErrors(fc));
    303 
    304  js::ReportAllocationOverflow(fc);
    305 
    306  CHECK(JS::HadFrontendErrors(fc));
    307  CHECK(!JS::HadFrontendOverRecursed(fc));
    308  CHECK(!JS::HadFrontendOutOfMemory(fc));
    309  CHECK(JS::HadFrontendAllocationOverflow(fc));
    310  CHECK(JS::GetFrontendWarningCount(fc) == 0);
    311 
    312  CHECK(!JS_IsExceptionPending(cx));
    313 
    314  JS::SetWarningReporter(cx, warningReporter);
    315 
    316  bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options);
    317  CHECK(result);
    318 
    319  CHECK(JS_IsExceptionPending(cx));
    320  CHECK(!warningReporterCalled);
    321  CHECK(!JS_IsThrowingOutOfMemory(cx));
    322 
    323  {
    324    JS::Rooted<JS::Value> exception(cx);
    325    CHECK(JS_GetPendingException(cx, &exception));
    326 
    327    CHECK(exception.isObject());
    328    JS::Rooted<JSObject*> exceptionObj(cx, &exception.toObject());
    329 
    330    const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj);
    331    CHECK(report);
    332 
    333    CHECK(report->errorNumber == JSMSG_ALLOC_OVERFLOW);
    334  }
    335 
    336  JS_ClearPendingException(cx);
    337 
    338  JS::DestroyFrontendContext(fc);
    339 
    340  return true;
    341 }
    342 
    343 static bool warningReporterCalled;
    344 
    345 static void warningReporter(JSContext* cx, JSErrorReport* report) {
    346  warningReporterCalled = true;
    347 }
    348 END_TEST(testFrontendErrors_allocationOverflow)
    349 
    350 /* static */ bool
    351    cls_testFrontendErrors_allocationOverflow::warningReporterCalled = false;