/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/RefPtr.h" // RefPtr #include "mozilla/Utf8.h" // mozilla::Utf8Unit #include // strcmp #include "frontend/FrontendContext.h" // js::FrontendContext #include "js/AllocPolicy.h" // js::ReportOutOfMemory #include "js/CompileOptions.h" // JS::PrefableCompileOptions, JS::CompileOptions #include "js/Exception.h" // JS_IsExceptionPending, JS_IsThrowingOutOfMemory, JS_GetPendingException, JS_ClearPendingException, JS_ErrorFromException #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 #include "js/friend/ErrorMessages.h" // JSMSG_* #include "js/friend/StackLimits.h" // js::ReportOverRecursed #include "js/RootingAPI.h" // JS::Rooted #include "js/SourceText.h" // JS::SourceText #include "js/Stack.h" // JS::NativeStackSize #include "js/Warnings.h" // JS::SetWarningReporter #include "jsapi-tests/tests.h" #include "util/NativeStack.h" // js::GetNativeStackBase #include "vm/ErrorObject.h" // js::ErrorObject #include "vm/JSContext.h" // JSContext #include "vm/JSObject.h" // JSObject #include "vm/NativeObject.h" // js::NativeObject #include "vm/Runtime.h" // js::ReportAllocationOverflow using namespace JS; BEGIN_TEST(testFrontendErrors_error) { JS::FrontendContext* fc = JS::NewFrontendContext(); CHECK(fc); static constexpr JS::NativeStackSize stackSize = 128 * sizeof(size_t) * 1024; JS::SetNativeStackQuota(fc, stackSize); JS::PrefableCompileOptions prefableOptions; JS::CompileOptions options(prefableOptions); const char* filename = "testFrontendErrors_error.js"; options.setFile(filename); CHECK(!JS::HadFrontendErrors(fc)); { const char source[] = "syntax error"; JS::SourceText srcBuf; CHECK( srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed)); RefPtr stencil = JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(!stencil); } CHECK(JS::HadFrontendErrors(fc)); CHECK(!JS::HadFrontendOverRecursed(fc)); CHECK(!JS::HadFrontendOutOfMemory(fc)); CHECK(!JS::HadFrontendAllocationOverflow(fc)); CHECK(JS::GetFrontendWarningCount(fc) == 0); { const JSErrorReport* report = JS::GetFrontendErrorReport(fc, options); CHECK(report); CHECK(report->errorNumber == JSMSG_UNEXPECTED_TOKEN_NO_EXPECT); // FrontendContext's error report borrows the filename. CHECK(report->filename.c_str() == filename); } CHECK(!JS_IsExceptionPending(cx)); JS::SetWarningReporter(cx, warningReporter); bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options); CHECK(result); CHECK(JS_IsExceptionPending(cx)); CHECK(!warningReporterCalled); CHECK(!JS_IsThrowingOutOfMemory(cx)); { JS::Rooted exception(cx); CHECK(JS_GetPendingException(cx, &exception)); CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); CHECK(report); CHECK(report->errorNumber == JSMSG_UNEXPECTED_TOKEN_NO_EXPECT); // Runtime's error report doesn't borrow the filename. CHECK(report->filename.c_str() != filename); CHECK(strcmp(report->filename.c_str(), filename) == 0); } JS_ClearPendingException(cx); JS::DestroyFrontendContext(fc); return true; } static bool warningReporterCalled; static void warningReporter(JSContext* cx, JSErrorReport* report) { warningReporterCalled = true; } END_TEST(testFrontendErrors_error) /* static */ bool cls_testFrontendErrors_error::warningReporterCalled = false; BEGIN_TEST(testFrontendErrors_warning) { JS::FrontendContext* fc = JS::NewFrontendContext(); CHECK(fc); static constexpr JS::NativeStackSize stackSize = 128 * sizeof(size_t) * 1024; JS::SetNativeStackQuota(fc, stackSize); JS::PrefableCompileOptions prefableOptions; JS::CompileOptions options(prefableOptions); options.setFile(filename); { const char source[] = "function f() { return; f(); }"; JS::SourceText srcBuf; CHECK( srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed)); RefPtr stencil = JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(stencil); } CHECK(!JS::HadFrontendErrors(fc)); CHECK(!JS::HadFrontendOverRecursed(fc)); CHECK(!JS::HadFrontendOutOfMemory(fc)); CHECK(!JS::HadFrontendAllocationOverflow(fc)); CHECK(JS::GetFrontendWarningCount(fc) == 1); { const JSErrorReport* report = JS::GetFrontendWarningAt(fc, 0, options); CHECK(report); CHECK(report->errorNumber == JSMSG_STMT_AFTER_RETURN); // FrontendContext's error report borrows the filename. CHECK(report->filename.c_str() == filename); } CHECK(!JS_IsExceptionPending(cx)); JS::SetWarningReporter(cx, warningReporter); bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options); CHECK(result); CHECK(!JS_IsExceptionPending(cx)); CHECK(warningReporterCalled); CHECK(!JS_IsThrowingOutOfMemory(cx)); CHECK(errorNumberMatches); CHECK(filenameMatches); JS::DestroyFrontendContext(fc); return true; } static const char* filename; static bool warningReporterCalled; static bool errorNumberMatches; static bool filenameMatches; static void warningReporter(JSContext* cx, JSErrorReport* report) { warningReporterCalled = true; errorNumberMatches = report->errorNumber == JSMSG_STMT_AFTER_RETURN; filenameMatches = report->filename.c_str() == filename; } END_TEST(testFrontendErrors_warning) /* static */ const char* cls_testFrontendErrors_warning::filename = "testFrontendErrors_warning.js"; /* static */ bool cls_testFrontendErrors_warning::warningReporterCalled = false; /* static */ bool cls_testFrontendErrors_warning::errorNumberMatches = false; /* static */ bool cls_testFrontendErrors_warning::filenameMatches = false; BEGIN_TEST(testFrontendErrors_oom) { JS::FrontendContext* fc = JS::NewFrontendContext(); CHECK(fc); JS::PrefableCompileOptions prefableOptions; JS::CompileOptions options(prefableOptions); CHECK(!JS::HadFrontendErrors(fc)); js::ReportOutOfMemory(fc); CHECK(JS::HadFrontendErrors(fc)); CHECK(!JS::HadFrontendOverRecursed(fc)); CHECK(JS::HadFrontendOutOfMemory(fc)); CHECK(!JS::HadFrontendAllocationOverflow(fc)); CHECK(JS::GetFrontendWarningCount(fc) == 0); CHECK(!JS_IsExceptionPending(cx)); JS::SetWarningReporter(cx, warningReporter); bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options); CHECK(!result); CHECK(JS_IsExceptionPending(cx)); CHECK(!warningReporterCalled); CHECK(JS_IsThrowingOutOfMemory(cx)); JS_ClearPendingException(cx); JS::DestroyFrontendContext(fc); return true; } static bool warningReporterCalled; static void warningReporter(JSContext* cx, JSErrorReport* report) { warningReporterCalled = true; } END_TEST(testFrontendErrors_oom) /* static */ bool cls_testFrontendErrors_oom::warningReporterCalled = false; BEGIN_TEST(testFrontendErrors_overRecursed) { JS::FrontendContext* fc = JS::NewFrontendContext(); CHECK(fc); JS::PrefableCompileOptions prefableOptions; JS::CompileOptions options(prefableOptions); CHECK(!JS::HadFrontendErrors(fc)); js::ReportOverRecursed(fc); CHECK(JS::HadFrontendErrors(fc)); CHECK(JS::HadFrontendOverRecursed(fc)); CHECK(!JS::HadFrontendOutOfMemory(fc)); CHECK(!JS::HadFrontendAllocationOverflow(fc)); CHECK(JS::GetFrontendWarningCount(fc) == 0); CHECK(!JS_IsExceptionPending(cx)); JS::SetWarningReporter(cx, warningReporter); bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options); CHECK(result); CHECK(JS_IsExceptionPending(cx)); CHECK(!warningReporterCalled); CHECK(!JS_IsThrowingOutOfMemory(cx)); { JS::Rooted exception(cx); CHECK(JS_GetPendingException(cx, &exception)); CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); CHECK(report); CHECK(report->errorNumber == JSMSG_OVER_RECURSED); } JS_ClearPendingException(cx); JS::DestroyFrontendContext(fc); return true; } static bool warningReporterCalled; static void warningReporter(JSContext* cx, JSErrorReport* report) { warningReporterCalled = true; } END_TEST(testFrontendErrors_overRecursed) /* static */ bool cls_testFrontendErrors_overRecursed::warningReporterCalled = false; BEGIN_TEST(testFrontendErrors_allocationOverflow) { JS::FrontendContext* fc = JS::NewFrontendContext(); CHECK(fc); JS::PrefableCompileOptions prefableOptions; JS::CompileOptions options(prefableOptions); CHECK(!JS::HadFrontendErrors(fc)); js::ReportAllocationOverflow(fc); CHECK(JS::HadFrontendErrors(fc)); CHECK(!JS::HadFrontendOverRecursed(fc)); CHECK(!JS::HadFrontendOutOfMemory(fc)); CHECK(JS::HadFrontendAllocationOverflow(fc)); CHECK(JS::GetFrontendWarningCount(fc) == 0); CHECK(!JS_IsExceptionPending(cx)); JS::SetWarningReporter(cx, warningReporter); bool result = JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options); CHECK(result); CHECK(JS_IsExceptionPending(cx)); CHECK(!warningReporterCalled); CHECK(!JS_IsThrowingOutOfMemory(cx)); { JS::Rooted exception(cx); CHECK(JS_GetPendingException(cx, &exception)); CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); CHECK(report); CHECK(report->errorNumber == JSMSG_ALLOC_OVERFLOW); } JS_ClearPendingException(cx); JS::DestroyFrontendContext(fc); return true; } static bool warningReporterCalled; static void warningReporter(JSContext* cx, JSErrorReport* report) { warningReporterCalled = true; } END_TEST(testFrontendErrors_allocationOverflow) /* static */ bool cls_testFrontendErrors_allocationOverflow::warningReporterCalled = false;