Bug 1202028 - Implement range based for loops for SavedFrame stacks. r=terrence

This commit is contained in:
Nick Fitzgerald 2015-09-10 16:24:00 +02:00
Родитель b44db3bce8
Коммит 640c3f9609
3 изменённых файлов: 147 добавлений и 9 удалений

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

@ -8,8 +8,8 @@
#include "jsfriendapi.h"
#include "jsstr.h"
#include "builtin/TestingFunctions.h"
#include "jsapi-tests/tests.h"
#include "vm/ArrayObject.h"
#include "vm/SavedStacks.h"
@ -63,3 +63,51 @@ BEGIN_TEST(testSavedStacks_ApiDefaultValues)
return true;
}
END_TEST(testSavedStacks_ApiDefaultValues)
BEGIN_TEST(testSavedStacks_RangeBasedForLoops)
{
CHECK(js::DefineTestingFunctions(cx, global, false));
JS::RootedValue val(cx);
CHECK(evaluate("(function one() { \n" // 1
" return (function two() { \n" // 2
" return (function three() { \n" // 3
" return saveStack(); \n" // 4
" }()); \n" // 5
" }()); \n" // 6
"}()); \n", // 7
"filename.js",
1,
&val));
CHECK(val.isObject());
JS::RootedObject obj(cx, &val.toObject());
CHECK(obj->is<js::SavedFrame>());
JS::Rooted<js::SavedFrame*> savedFrame(cx, &obj->as<js::SavedFrame>());
js::SavedFrame* f = savedFrame.get();
for (auto& frame : *savedFrame.get()) {
CHECK(&frame == f);
f = f->getParent();
}
CHECK(f == nullptr);
const js::SavedFrame* cf = savedFrame.get();
for (const auto& frame : *savedFrame.get()) {
CHECK(&frame == cf);
cf = cf->getParent();
}
CHECK(cf == nullptr);
JS::Rooted<js::SavedFrame*> rf(cx, savedFrame);
for (JS::Handle<js::SavedFrame*> frame : js::SavedFrame::RootedRange(cx, rf)) {
JS_GC(cx->runtime());
CHECK(frame == rf);
rf = rf->getParent();
}
CHECK(rf == nullptr);
return true;
}
END_TEST(testSavedStacks_RangeBasedForLoops)

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

@ -39,11 +39,82 @@ class SavedFrame : public NativeObject {
uint32_t getColumn();
JSAtom* getFunctionDisplayName();
JSAtom* getAsyncCause();
SavedFrame* getParent();
SavedFrame* getParent() const;
JSPrincipals* getPrincipals();
bool isSelfHosted();
// Iterators for use with C++11 range based for loops, eg:
//
// SavedFrame* stack = getSomeSavedFrameStack();
// for (const SavedFrame* frame : *stack) {
// ...
// }
//
// If you need to keep each frame rooted during iteration, you can use
// `SavedFrame::RootedRange`. Each frame yielded by
// `SavedFrame::RootedRange` is only a valid handle to a rooted `SavedFrame`
// within the loop's block for a single loop iteration. When the next
// iteration begins, the value is invalidated.
//
// RootedSavedFrame stack(cx, getSomeSavedFrameStack());
// for (HandleSavedFrame frame : SavedFrame::RootedRange(cx, stack)) {
// ...
// }
class Iterator {
SavedFrame* frame_;
public:
explicit Iterator(SavedFrame* frame) : frame_(frame) { }
SavedFrame& operator*() const { MOZ_ASSERT(frame_); return *frame_; }
bool operator!=(const Iterator& rhs) const { return rhs.frame_ != frame_; }
inline void operator++();
};
Iterator begin() { return Iterator(this); }
Iterator end() { return Iterator(nullptr); }
class ConstIterator {
const SavedFrame* frame_;
public:
explicit ConstIterator(const SavedFrame* frame) : frame_(frame) { }
const SavedFrame& operator*() const { MOZ_ASSERT(frame_); return *frame_; }
bool operator!=(const ConstIterator& rhs) const { return rhs.frame_ != frame_; }
inline void operator++();
};
ConstIterator begin() const { return ConstIterator(this); }
ConstIterator end() const { return ConstIterator(nullptr); }
class RootedRange;
class MOZ_STACK_CLASS RootedIterator {
friend class RootedRange;
RootedRange* range_;
// For use by RootedRange::end() only.
explicit RootedIterator() : range_(nullptr) { }
public:
explicit RootedIterator(RootedRange& range) : range_(&range) { }
HandleSavedFrame operator*() { MOZ_ASSERT(range_); return range_->frame_; }
bool operator!=(const RootedIterator& rhs) const {
// We should only ever compare to the null range, aka we are just
// testing if this range is done.
MOZ_ASSERT(rhs.range_ == nullptr);
return range_->frame_ != nullptr;
}
inline void operator++();
};
class MOZ_STACK_CLASS RootedRange {
friend class RootedIterator;
RootedSavedFrame frame_;
public:
RootedRange(JSContext* cx, HandleSavedFrame frame) : frame_(cx, frame) { }
RootedIterator begin() { return RootedIterator(*this); }
RootedIterator end() { return RootedIterator(); }
};
static bool isSavedFrameAndNotProto(JSObject& obj) {
return obj.is<SavedFrame>() &&
!obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
@ -139,6 +210,25 @@ struct ReconstructedSavedFramePrincipals : public JSPrincipals
}
};
inline void
SavedFrame::Iterator::operator++()
{
frame_ = frame_->getParent();
}
inline void
SavedFrame::ConstIterator::operator++()
{
frame_ = frame_->getParent();
}
inline void
SavedFrame::RootedIterator::operator++()
{
MOZ_ASSERT(range_);
range_->frame_ = range_->frame_->getParent();
}
} // namespace js
namespace JS {

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

@ -390,7 +390,7 @@ SavedFrame::getAsyncCause()
}
SavedFrame*
SavedFrame::getParent()
SavedFrame::getParent() const
{
const Value& v = getReservedSlot(JSSLOT_PARENT);
return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;