Bug 1499813 - Part 2: JSObject::unwrapAs<T>() and canUnwrapAs<T>() methods. r=tcampbell

Depends on D9834

Differential Revision: https://phabricator.services.mozilla.com/D9835

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jason Orendorff 2018-10-30 21:58:21 +00:00
Родитель 6ff7b405ea
Коммит cc2589c333
10 изменённых файлов: 69 добавлений и 56 удалений

Просмотреть файл

@ -957,13 +957,6 @@ const JSPropertySpec DataViewObject::properties[] = {
JS_PS_END
};
JS_FRIEND_API(bool)
JS_IsDataViewObject(JSObject* obj)
{
obj = CheckedUnwrap(obj);
return obj ? obj->is<DataViewObject>() : false;
}
JS_FRIEND_API(uint32_t)
JS_GetDataViewByteOffset(JSObject* obj)
{

Просмотреть файл

@ -170,18 +170,7 @@ template<class T>
bool
IsMaybeWrapped(const HandleValue v)
{
if (!v.isObject()) {
return false;
}
JSObject* obj = &v.toObject();
if (obj->is<T>()) {
return true;
}
obj = CheckedUnwrap(obj);
if (!obj) {
return false;
}
return obj->is<T>();
return v.isObject() && v.toObject().canUnwrapAs<T>();
}
static inline uint32_t
@ -2742,7 +2731,7 @@ static MOZ_MUST_USE bool
ReadableStreamDefaultController_close_impl(JSContext* cx, const CallArgs& args)
{
Rooted<ReadableStreamDefaultController*> controller(cx);
controller = &UncheckedUnwrap(&args.thisv().toObject())->as<ReadableStreamDefaultController>();
controller = &args.thisv().toObject().unwrapAs<ReadableStreamDefaultController>();
// Steps 2-3.
if (!VerifyControllerStateForClosing(cx, controller)) {
@ -2773,7 +2762,7 @@ static MOZ_MUST_USE bool
ReadableStreamDefaultController_enqueue_impl(JSContext* cx, const CallArgs& args)
{
Rooted<ReadableStreamDefaultController*> controller(cx);
controller = &UncheckedUnwrap(&args.thisv().toObject())->as<ReadableStreamDefaultController>();
controller = &args.thisv().toObject().unwrapAs<ReadableStreamDefaultController>();
// Step 2: If this.[[closeRequested]] is true, throw a TypeError exception.
if (ControllerFlags(controller) & ControllerFlag_CloseRequested) {
@ -2819,7 +2808,7 @@ static MOZ_MUST_USE bool
ReadableStreamDefaultController_error_impl(JSContext* cx, const CallArgs& args)
{
Rooted<ReadableStreamDefaultController*> controller(cx);
controller = &UncheckedUnwrap(&args.thisv().toObject())->as<ReadableStreamDefaultController>();
controller = &args.thisv().toObject().unwrapAs<ReadableStreamDefaultController>();
// Step 2: Let stream be this.[[controlledReadableStream]].
// Step 3: If stream.[[state]] is not "readable", throw a TypeError exception.

Просмотреть файл

@ -4719,28 +4719,19 @@ JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
JS_PUBLIC_API(bool)
JS::IsReadableStream(JSObject* obj)
{
if (IsWrapper(obj)) {
obj = CheckedUnwrap(obj);
}
return obj && obj->is<ReadableStream>();
return obj->canUnwrapAs<ReadableStream>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamReader(JSObject* obj)
{
if (IsWrapper(obj)) {
obj = CheckedUnwrap(obj);
}
return obj && obj->is<ReadableStreamDefaultReader>();
return obj->canUnwrapAs<ReadableStreamDefaultReader>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamDefaultReader(JSObject* obj)
{
if (IsWrapper(obj)) {
obj = CheckedUnwrap(obj);
}
return obj && obj->is<ReadableStreamDefaultReader>();
return obj->canUnwrapAs<ReadableStreamDefaultReader>();
}
template<class T>

Просмотреть файл

@ -2068,12 +2068,6 @@ JS_DetachArrayBuffer(JSContext* cx, JS::HandleObject obj);
extern JS_FRIEND_API(bool)
JS_IsDetachedArrayBufferObject(JSObject* obj);
/**
* Check whether obj supports JS_GetDataView* APIs.
*/
JS_FRIEND_API(bool)
JS_IsDataViewObject(JSObject* obj);
/**
* Create a new DataView using the given buffer for storage. The given buffer
* must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper

Просмотреть файл

@ -13,6 +13,7 @@
#include "js/Conversions.h"
#include "js/GCVector.h"
#include "js/HeapAPI.h"
#include "js/Wrapper.h"
#include "vm/Printer.h"
#include "vm/Shape.h"
#include "vm/StringType.h"
@ -535,6 +536,27 @@ class JSObject : public js::gc::Cell
return *static_cast<const T*>(this);
}
/*
* True if either this or CheckedUnwrap(this) is an object of class T.
* (Only two objects are checked, regardless of how many wrappers there
* are.)
*
* /!\ Note: This can be true at one point, but false later for the same
* object, thanks to js::NukeCrossCompartmentWrapper and friends.
*/
template <class T>
bool canUnwrapAs();
/*
* Unwrap and downcast to class T.
*
* Precondition: `this->canUnwrapAs<T>()`. Note that it's not enough to
* have checked this at some point in the past; if there's any doubt as to
* whether js::Nuke* could have been called in the meantime, check again.
*/
template <class T>
T& unwrapAs();
#if defined(DEBUG) || defined(JS_JITSPEW)
void dump(js::GenericPrinter& fp) const;
void dump() const;
@ -590,6 +612,39 @@ js::HandleBase<JSObject*, Wrapper>::as() const
return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
}
template <class T>
bool
JSObject::canUnwrapAs()
{
static_assert(!std::is_convertible<T*, js::Wrapper*>::value,
"T can't be a Wrapper type; this function discards wrappers");
if (is<T>()) {
return true;
}
JSObject* obj = js::CheckedUnwrap(this);
return obj && obj->is<T>();
}
template <class T>
T&
JSObject::unwrapAs()
{
static_assert(!std::is_convertible<T*, js::Wrapper*>::value,
"T can't be a Wrapper type; this function discards wrappers");
if (is<T>()) {
return as<T>();
}
// Since the caller just called canUnwrapAs<T>(), which does a
// CheckedUnwrap, this does not need to repeat the security check.
JSObject* unwrapped = js::UncheckedUnwrap(this);
MOZ_ASSERT(js::CheckedUnwrap(this) == unwrapped,
"check that the security check we skipped really is redundant");
return unwrapped->as<T>();
}
/*
* The only sensible way to compare JSObject with == is by identity. We use
* const& instead of * as a syntactic way to assert non-null. This leads to an

Просмотреть файл

@ -97,14 +97,6 @@ class SavedFrame : public NativeObject {
RootedIterator end() { return RootedIterator(); }
};
static bool isSavedFrameOrWrapper(JSObject& obj) {
auto unwrapped = CheckedUnwrap(&obj);
if (!unwrapped) {
return false;
}
return unwrapped->is<SavedFrame>();
}
struct Lookup;
struct HashPolicy;

Просмотреть файл

@ -23,7 +23,7 @@ inline void
js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack)
{
if (stack) {
MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameOrWrapper(*stack));
MOZ_RELEASE_ASSERT(stack->canUnwrapAs<SavedFrame>());
}
}

Просмотреть файл

@ -1132,7 +1132,7 @@ JS_PUBLIC_API(bool)
IsMaybeWrappedSavedFrame(JSObject* obj)
{
MOZ_ASSERT(obj);
return js::SavedFrame::isSavedFrameOrWrapper(*obj);
return obj->canUnwrapAs<js::SavedFrame>();
}
JS_PUBLIC_API(bool)

Просмотреть файл

@ -1827,16 +1827,16 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
break;
case ESClass::Other: {
if (JS_IsTypedArrayObject(obj)) {
if (obj->canUnwrapAs<TypedArrayObject>()) {
return writeTypedArray(obj);
}
if (JS_IsDataViewObject(obj)) {
if (obj->canUnwrapAs<DataViewObject>()) {
return writeDataView(obj);
}
if (wasm::IsSharedWasmMemoryObject(obj)) {
return writeSharedWasmMemory(obj);
}
if (SavedFrame::isSavedFrameOrWrapper(*obj)) {
if (obj->canUnwrapAs<SavedFrame>()) {
return traverseSavedFrame(obj);
}
break;
@ -2072,7 +2072,7 @@ JSStructuredCloneWriter::write(HandleValue v)
if (!startWrite(key) || !startWrite(val)) {
return false;
}
} else if (cls == ESClass::Set || SavedFrame::isSavedFrameOrWrapper(*obj)) {
} else if (cls == ESClass::Set || obj->canUnwrapAs<SavedFrame>()) {
key = otherEntries.popCopy();
checkStack();

Просмотреть файл

@ -2269,8 +2269,7 @@ js::DefineTypedArrayElement(JSContext* cx, HandleObject obj, uint64_t index,
JS_FRIEND_API(bool)
JS_IsTypedArrayObject(JSObject* obj)
{
obj = CheckedUnwrap(obj);
return obj ? obj->is<TypedArrayObject>() : false;
return obj->canUnwrapAs<TypedArrayObject>();
}
JS_FRIEND_API(uint32_t)