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;