Issue #2259 - Add mozilla::Result<V, E> and JS::Result<> for fallible return values

Based-on: m-c 1283562, 1277368/1, 1324828
This commit is contained in:
Martok 2023-06-17 16:05:46 +02:00 committed by roytam1
commit 4c2e378613
20 changed files with 905 additions and 73 deletions

View file

@ -82,6 +82,10 @@ using JS::UTF8CharsZ;
using JS::UniqueChars;
using JS::UniqueTwoByteChars;
using JS::Result;
using JS::Ok;
using JS::OOM;
using JS::AutoValueVector;
using JS::AutoIdVector;
using JS::AutoObjectVector;

View file

@ -245,6 +245,13 @@ js::ReportOutOfMemory(ExclusiveContext* cxArg)
cx->setPendingException(oomMessage, nullptr);
}
mozilla::GenericErrorResult<OOM&>
js::ReportOutOfMemoryResult(ExclusiveContext* cx)
{
ReportOutOfMemory(cx);
return cx->alreadyReportedOOM();
}
void
js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
{
@ -1009,6 +1016,34 @@ ExclusiveContext::recoverFromOutOfMemory()
task->outOfMemory = false;
}
JS::Error ExclusiveContext::reportedError;
JS::OOM ExclusiveContext::reportedOOM;
mozilla::GenericErrorResult<OOM&>
ExclusiveContext::alreadyReportedOOM()
{
#ifdef DEBUG
if (JSContext* maybecx = maybeJSContext()) {
MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
} else {
// Keep in sync with addPendingOutOfMemory.
if (ParseTask* task = helperThread()->parseTask())
MOZ_ASSERT(task->outOfMemory);
}
#endif
return mozilla::MakeGenericErrorResult(reportedOOM);
}
mozilla::GenericErrorResult<JS::Error&>
ExclusiveContext::alreadyReportedError()
{
#ifdef DEBUG
if (JSContext* maybecx = maybeJSContext())
MOZ_ASSERT(maybecx->isExceptionPending());
#endif
return mozilla::MakeGenericErrorResult(reportedError);
}
JSContext::JSContext(JSRuntime* parentRuntime)
: ExclusiveContext(this, &this->JSRuntime::mainThread, Context_JS, JS::ContextOptions()),
JSRuntime(parentRuntime),

View file

@ -12,6 +12,7 @@
#include "js/CharacterEncoding.h"
#include "js/GCVector.h"
#include "js/Result.h"
#include "js/Utility.h"
#include "js/Vector.h"
#include "vm/Caches.h"
@ -314,6 +315,30 @@ class ExclusiveContext : public ContextFriendFields,
bool addPendingCompileError(frontend::CompileError** err);
void addPendingOverRecursed();
void addPendingOutOfMemory();
private:
static JS::Error reportedError;
static JS::OOM reportedOOM;
public:
inline JS::Result<> boolToResult(bool ok);
/**
* Intentionally awkward signpost method that is stationed on the
* boundary between Result-using and non-Result-using code.
*/
template <typename V, typename E>
bool resultToBool(JS::Result<V, E> result) {
return result.isOk();
}
template <typename V, typename E>
V* resultToPtr(JS::Result<V*, E> result) {
return result.isOk() ? result.unwrap() : nullptr;
}
mozilla::GenericErrorResult<JS::OOM&> alreadyReportedOOM();
mozilla::GenericErrorResult<JS::Error&> alreadyReportedError();
};
void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
@ -490,7 +515,7 @@ struct JSContext : public js::ExclusiveContext,
}
public:
bool isExceptionPending() {
bool isExceptionPending() const {
return throwing;
}
@ -540,6 +565,17 @@ struct JSContext : public js::ExclusiveContext,
namespace js {
inline JS::Result<>
ExclusiveContext::boolToResult(bool ok)
{
if (MOZ_LIKELY(ok)) {
MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isExceptionPending());
MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isPropagatingForcedReturn());
return JS::Ok();
}
return JS::Result<>(reportedError);
}
struct MOZ_RAII AutoResolving {
public:
enum Kind {

View file

@ -553,10 +553,10 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags)
if (!shape)
return nullptr;
JSObject* obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
GetInitialHeap(GenericObject, clasp), shape, group);
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, ITERATOR_FINALIZE_KIND,
GetInitialHeap(GenericObject, clasp),
shape, group));
PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();

View file

@ -257,15 +257,15 @@ js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber)
/*** PropertyDescriptor operations and DefineProperties ******************************************/
bool
static Result<>
CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
{
if (obj && !obj->isCallable()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
fieldName);
return false;
return cx->alreadyReportedError();
}
return true;
return Ok();
}
bool
@ -335,8 +335,8 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
hasGetOrSet = found;
if (found) {
if (v.isObject()) {
if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_getter_str))
return false;
if (checkAccessors)
JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_getter_str));
desc.setGetterObject(&v.toObject());
} else if (!v.isUndefined()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
@ -353,8 +353,8 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
hasGetOrSet |= found;
if (found) {
if (v.isObject()) {
if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_setter_str))
return false;
if (checkAccessors)
JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_setter_str));
desc.setSetterObject(&v.toObject());
} else if (!v.isUndefined()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
@ -381,18 +381,16 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
return true;
}
bool
Result<>
js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
{
if (desc.hasGetterObject()) {
if (!CheckCallable(cx, desc.getterObject(), js_getter_str))
return false;
}
if (desc.hasSetterObject()) {
if (!CheckCallable(cx, desc.setterObject(), js_setter_str))
return false;
}
return true;
if (desc.hasGetterObject())
MOZ_TRY(CheckCallable(cx, desc.getterObject(), js_getter_str));
if (desc.hasSetterObject())
MOZ_TRY(CheckCallable(cx, desc.setterObject(), js_setter_str));
return Ok();
}
void
@ -646,9 +644,8 @@ NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind,
return nullptr;
gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
if (newKind == SingletonObject) {
RootedObject nobj(cx, obj);

View file

@ -179,11 +179,9 @@ class JSObject : public js::gc::Cell
* Make a non-array object with the specified initial state. This method
* takes ownership of any extantSlots it is passed.
*/
static inline JSObject* create(js::ExclusiveContext* cx,
js::gc::AllocKind kind,
js::gc::InitialHeap heap,
js::HandleShape shape,
js::HandleObjectGroup group);
static inline JS::Result<JSObject*, JS::OOM&>
create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
js::HandleShape shape, js::HandleObjectGroup group);
// Set the initial slots and elements of an object. These pointers are only
// valid for native objects, but during initialization are set for all
@ -1173,7 +1171,7 @@ ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
* callable. This performs exactly the checks omitted by ToPropertyDescriptor
* when checkAccessors is false.
*/
bool
Result<>
CheckPropertyDescriptorAccessors(JSContext* cx, Handle<JS::PropertyDescriptor> desc);
void

View file

@ -319,7 +319,7 @@ SetNewObjectMetadata(ExclusiveContext* cxArg, JSObject* obj)
} // namespace js
/* static */ inline JSObject*
/* static */ inline JS::Result<JSObject*, JS::OOM&>
JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
js::HandleShape shape, js::HandleObjectGroup group)
{
@ -375,7 +375,7 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi
JSObject* obj = js::Allocate<JSObject>(cx, kind, nDynamicSlots, heap, clasp);
if (!obj)
return nullptr;
return cx->alreadyReportedOOM();
obj->group_.init(group);

View file

@ -18,6 +18,7 @@
#include "jsprototypes.h"
#include "jstypes.h"
#include "js/Result.h"
#include "js/TraceKind.h"
#include "js/TypeDecls.h"

View file

@ -89,6 +89,7 @@ EXPORTS.js += [
'../public/Proxy.h',
'../public/Realm.h',
'../public/RequiredDefines.h',
'../public/Result.h',
'../public/RootingAPI.h',
'../public/SliceBudget.h',
'../public/StructuredClone.h',

View file

@ -227,9 +227,8 @@ ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped)
return nullptr;
AutoSetNewObjectMetadata metadata(cx);
JSObject* base = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group);
if (!base)
return nullptr;
JSObject* base;
JS_TRY_VAR_OR_RETURN_NULL(cx, base, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group));
ArgumentsObject* obj = &base->as<js::ArgumentsObject>();
obj->initFixedSlot(ArgumentsObject::DATA_SLOT, PrivateValue(nullptr));
@ -283,9 +282,8 @@ ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActual
// to make sure we set the metadata for this arguments object first.
AutoSetNewObjectMetadata metadata(cx);
JSObject* base = JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group);
if (!base)
return nullptr;
JSObject* base;
JS_TRY_VAR_OR_RETURN_NULL(cx, base, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group));
obj = &base->as<ArgumentsObject>();
data =

View file

@ -9949,8 +9949,7 @@ DebuggerObject::defineProperty(JSContext* cx, HandleDebuggerObject object, Handl
Rooted<PropertyDescriptor> desc(cx, desc_);
if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc))
return false;
if (!CheckPropertyDescriptorAccessors(cx, desc))
return false;
JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));
Maybe<AutoCompartment> ac;
ac.emplace(cx, referent);
@ -9978,8 +9977,7 @@ DebuggerObject::defineProperties(JSContext* cx, HandleDebuggerObject object,
for (size_t i = 0; i < descs.length(); i++) {
if (!dbg->unwrapPropertyDescriptor(cx, referent, descs[i]))
return false;
if (!CheckPropertyDescriptorAccessors(cx, descs[i]))
return false;
JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, descs[i]));
}
Maybe<AutoCompartment> ac;

View file

@ -141,9 +141,8 @@ CallObject::create(JSContext* cx, HandleShape shape, HandleObjectGroup group)
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, group);
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::DefaultHeap, shape, group));
return &obj->as<CallObject>();
}
@ -158,9 +157,9 @@ CallObject::createSingleton(JSContext* cx, HandleShape shape)
RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, &class_, TaggedProto(nullptr)));
if (!group)
return nullptr;
RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, group));
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::TenuredHeap, shape, group));
MOZ_ASSERT(obj->isSingleton(),
"group created inline above must be a singleton");
@ -189,9 +188,8 @@ CallObject::createTemplateObject(JSContext* cx, HandleScript script, HandleObjec
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
CallObject* callObj = &obj->as<CallObject>();
callObj->initEnclosingEnvironment(enclosing);
@ -321,14 +319,13 @@ VarEnvironmentObject::create(JSContext* cx, HandleShape shape, HandleObject encl
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
kind = gc::GetBackgroundAllocKind(kind);
NativeObject* obj = MaybeNativeObject(JSObject::create(cx, kind, heap, shape, group));
if (!obj)
return nullptr;
MOZ_ASSERT(!obj->inDictionaryMode());
MOZ_ASSERT(obj->isDelegate());
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
VarEnvironmentObject* env = &obj->as<VarEnvironmentObject>();
MOZ_ASSERT(!env->inDictionaryMode());
MOZ_ASSERT(env->isDelegate());
env->initEnclosingEnvironment(enclosing);
return env;
@ -437,9 +434,8 @@ ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
if (!obj)
return nullptr;
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, TenuredHeap, shape, group));
RootedModuleEnvironmentObject env(cx, &obj->as<ModuleEnvironmentObject>());
@ -842,15 +838,14 @@ LexicalEnvironmentObject::createTemplateObject(JSContext* cx, HandleShape shape,
gc::AllocKind allocKind = gc::GetGCObjectKind(shape->numFixedSlots());
MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &LexicalEnvironmentObject::class_));
allocKind = GetBackgroundAllocKind(allocKind);
RootedNativeObject obj(cx,
MaybeNativeObject(JSObject::create(cx, allocKind, heap, shape, group)));
if (!obj)
return nullptr;
MOZ_ASSERT(!obj->inDictionaryMode());
MOZ_ASSERT(obj->isDelegate());
JSObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, allocKind, heap, shape, group));
LexicalEnvironmentObject* env = &obj->as<LexicalEnvironmentObject>();
MOZ_ASSERT(!env->inDictionaryMode());
MOZ_ASSERT(env->isDelegate());
if (enclosing)
env->initEnclosingEnvironment(enclosing);

View file

@ -258,9 +258,8 @@ NativeObject::createWithTemplate(JSContext* cx, gc::InitialHeap heap,
MOZ_ASSERT(CanBeFinalizedInBackground(kind, shape->getObjectClass()));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* baseObj = create(cx, kind, heap, shape, group);
if (!baseObj)
return nullptr;
JSObject* baseObj;
JS_TRY_VAR_OR_RETURN_NULL(cx, baseObj, create(cx, kind, heap, shape, group));
return &baseObj->as<NativeObject>();
}
@ -272,9 +271,9 @@ NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap hea
RootedObjectGroup group(cx, templateObject->group());
MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite());
JSObject* baseObj = create(cx, kind, heap, shape, group);
if (!baseObj)
return nullptr;
JSObject* baseObj;
JS_TRY_VAR_OR_RETURN_NULL(cx, baseObj, create(cx, kind, heap, shape, group));
NativeObject* obj = &baseObj->as<NativeObject>();
size_t span = shape->slotSpan();

View file

@ -87,6 +87,10 @@ namespace js {
extern MOZ_COLD void
ReportOutOfMemory(ExclusiveContext* cx);
/* Different signature because the return type has MOZ_MUST_USE_TYPE. */
extern MOZ_COLD mozilla::GenericErrorResult<OOM&>
ReportOutOfMemoryResult(ExclusiveContext* cx);
extern MOZ_COLD void
ReportAllocationOverflow(ExclusiveContext* maybecx);