Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "mozilla/RefPtr.h" // RefPtr
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include <string.h> // 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<mozilla::Utf8Unit> srcBuf;
CHECK(
srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed));
RefPtr<JS::Stencil> 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<JS::Value> exception(cx);
CHECK(JS_GetPendingException(cx, &exception));
CHECK(exception.isObject());
JS::Rooted<JSObject*> 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<mozilla::Utf8Unit> srcBuf;
CHECK(
srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed));
RefPtr<JS::Stencil> 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<JS::Value> exception(cx);
CHECK(JS_GetPendingException(cx, &exception));
CHECK(exception.isObject());
JS::Rooted<JSObject*> 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<JS::Value> exception(cx);
CHECK(JS_GetPendingException(cx, &exception));
CHECK(exception.isObject());
JS::Rooted<JSObject*> 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;