|
@@ -0,0 +1,14693 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Jan de Mooij <jdemooij@mozilla.com>
|
|
|
+# Date 1536225329 -7200
|
|
|
+# Thu Sep 06 11:15:29 2018 +0200
|
|
|
+# Node ID 3810b18e5e79dc6a5204a2df01bfaafc40f0573e
|
|
|
+# Parent b5b33a78679f204f9284dc126ca96c2c619acf71
|
|
|
+Bug 1488698 - Always use braces for if/for/while statements in js/src/vm, part 7. r=jorendorff
|
|
|
+
|
|
|
+diff --git a/js/src/vm/JSAtom-inl.h b/js/src/vm/JSAtom-inl.h
|
|
|
+--- a/js/src/vm/JSAtom-inl.h
|
|
|
++++ b/js/src/vm/JSAtom-inl.h
|
|
|
+@@ -20,55 +20,59 @@
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ inline jsid
|
|
|
+ AtomToId(JSAtom* atom)
|
|
|
+ {
|
|
|
+ JS_STATIC_ASSERT(JSID_INT_MIN == 0);
|
|
|
+
|
|
|
+ uint32_t index;
|
|
|
+- if (atom->isIndex(&index) && index <= JSID_INT_MAX)
|
|
|
++ if (atom->isIndex(&index) && index <= JSID_INT_MAX) {
|
|
|
+ return INT_TO_JSID(int32_t(index));
|
|
|
++ }
|
|
|
+
|
|
|
+ return JSID_FROM_BITS(size_t(atom) | JSID_TYPE_STRING);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Use the NameToId method instead!
|
|
|
+ inline jsid
|
|
|
+ AtomToId(PropertyName* name) = delete;
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ ValueToIntId(const Value& v, jsid* id)
|
|
|
+ {
|
|
|
+ int32_t i;
|
|
|
+- if (v.isInt32())
|
|
|
++ if (v.isInt32()) {
|
|
|
+ i = v.toInt32();
|
|
|
+- else if (!v.isDouble() || !mozilla::NumberEqualsInt32(v.toDouble(), &i))
|
|
|
++ } else if (!v.isDouble() || !mozilla::NumberEqualsInt32(v.toDouble(), &i)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (!INT_FITS_IN_JSID(i))
|
|
|
++ if (!INT_FITS_IN_JSID(i)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *id = INT_TO_JSID(i);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ ValueToIdPure(const Value& v, jsid* id)
|
|
|
+ {
|
|
|
+ if (v.isString()) {
|
|
|
+ if (v.toString()->isAtom()) {
|
|
|
+ *id = AtomToId(&v.toString()->asAtom());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (ValueToIntId(v, id))
|
|
|
++ if (ValueToIntId(v, id)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (v.isSymbol()) {
|
|
|
+ *id = SYMBOL_TO_JSID(v.toSymbol());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+@@ -79,28 +83,30 @@ ValueToId(JSContext* cx, typename MaybeR
|
|
|
+ typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
|
|
|
+ {
|
|
|
+ if (v.isString()) {
|
|
|
+ if (v.toString()->isAtom()) {
|
|
|
+ idp.set(AtomToId(&v.toString()->asAtom()));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+- if (ValueToIntId(v, idp.address()))
|
|
|
++ if (ValueToIntId(v, idp.address())) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (v.isSymbol()) {
|
|
|
+ idp.set(SYMBOL_TO_JSID(v.toSymbol()));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom* atom = ToAtom<allowGC>(cx, v);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ idp.set(AtomToId(atom));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Write out character representing |index| to the memory just before |end|.
|
|
|
+ * Thus |*end| is not touched, but |end[-1]| and earlier are modified as
|
|
|
+@@ -142,26 +148,29 @@ IndexToId(JSContext* cx, uint32_t index,
|
|
|
+ }
|
|
|
+
|
|
|
+ return IndexToIdSlow(cx, index, idp);
|
|
|
+ }
|
|
|
+
|
|
|
+ static MOZ_ALWAYS_INLINE JSFlatString*
|
|
|
+ IdToString(JSContext* cx, jsid id)
|
|
|
+ {
|
|
|
+- if (JSID_IS_STRING(id))
|
|
|
++ if (JSID_IS_STRING(id)) {
|
|
|
+ return JSID_TO_ATOM(id);
|
|
|
++ }
|
|
|
+
|
|
|
+- if (MOZ_LIKELY(JSID_IS_INT(id)))
|
|
|
++ if (MOZ_LIKELY(JSID_IS_INT(id))) {
|
|
|
+ return Int32ToString<CanGC>(cx, JSID_TO_INT(id));
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue idv(cx, IdToValue(id));
|
|
|
+ JSString* str = ToStringSlow<CanGC>(cx, idv);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return str->ensureFlat(cx);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Handle<PropertyName*>
|
|
|
+ TypeName(JSType type, const JSAtomState& names)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(type < JSTYPE_LIMIT);
|
|
|
+diff --git a/js/src/vm/JSAtom.cpp b/js/src/vm/JSAtom.cpp
|
|
|
+--- a/js/src/vm/JSAtom.cpp
|
|
|
++++ b/js/src/vm/JSAtom.cpp
|
|
|
+@@ -83,40 +83,45 @@ js::AtomHasher::hash(const Lookup& l)
|
|
|
+ {
|
|
|
+ return l.hash;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ js::AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup)
|
|
|
+ {
|
|
|
+ JSAtom* key = entry.asPtrUnbarriered();
|
|
|
+- if (lookup.atom)
|
|
|
++ if (lookup.atom) {
|
|
|
+ return lookup.atom == key;
|
|
|
+- if (key->length() != lookup.length || key->hash() != lookup.hash)
|
|
|
++ }
|
|
|
++ if (key->length() != lookup.length || key->hash() != lookup.hash) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (key->hasLatin1Chars()) {
|
|
|
+ const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
|
|
|
+- if (lookup.isLatin1)
|
|
|
++ if (lookup.isLatin1) {
|
|
|
+ return mozilla::ArrayEqual(keyChars, lookup.latin1Chars, lookup.length);
|
|
|
++ }
|
|
|
+ return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ const char16_t* keyChars = key->twoByteChars(lookup.nogc);
|
|
|
+- if (lookup.isLatin1)
|
|
|
++ if (lookup.isLatin1) {
|
|
|
+ return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
|
|
|
++ }
|
|
|
+ return mozilla::ArrayEqual(keyChars, lookup.twoByteChars, lookup.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSAtom*
|
|
|
+ js::AtomStateEntry::asPtr(JSContext* cx) const
|
|
|
+ {
|
|
|
+ JSAtom* atom = asPtrUnbarriered();
|
|
|
+- if (!cx->helperThread())
|
|
|
++ if (!cx->helperThread()) {
|
|
|
+ JSString::readBarrier(atom);
|
|
|
++ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ UniqueChars
|
|
|
+ js::AtomToPrintableString(JSContext* cx, JSAtom* atom)
|
|
|
+ {
|
|
|
+ return QuoteString(cx, atom);
|
|
|
+ }
|
|
|
+@@ -156,29 +161,32 @@ JSRuntime::initializeAtoms(JSContext* cx
|
|
|
+ permanentAtoms_ = parentRuntime->permanentAtoms_;
|
|
|
+
|
|
|
+ staticStrings = parentRuntime->staticStrings;
|
|
|
+ commonNames = parentRuntime->commonNames;
|
|
|
+ emptyString = parentRuntime->emptyString;
|
|
|
+ wellKnownSymbols = parentRuntime->wellKnownSymbols;
|
|
|
+
|
|
|
+ atoms_ = js_new<AtomsTable>();
|
|
|
+- if (!atoms_)
|
|
|
++ if (!atoms_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return atoms_->init();
|
|
|
+ }
|
|
|
+
|
|
|
+ permanentAtomsDuringInit_ = js_new<AtomSet>(JS_PERMANENT_ATOM_SIZE);
|
|
|
+- if (!permanentAtomsDuringInit_)
|
|
|
++ if (!permanentAtomsDuringInit_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ staticStrings = js_new<StaticStrings>();
|
|
|
+- if (!staticStrings || !staticStrings->init(cx))
|
|
|
++ if (!staticStrings || !staticStrings->init(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ static const CommonNameInfo cachedNames[] = {
|
|
|
+ #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
|
|
|
+ FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
|
|
|
+ #undef COMMON_NAME_INFO
|
|
|
+ #define COMMON_NAME_INFO(name, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
|
|
|
+ JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
|
|
|
+ #undef COMMON_NAME_INFO
|
|
|
+@@ -186,34 +194,37 @@ JSRuntime::initializeAtoms(JSContext* cx
|
|
|
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
|
|
|
+ #undef COMMON_NAME_INFO
|
|
|
+ #define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 },
|
|
|
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
|
|
|
+ #undef COMMON_NAME_INFO
|
|
|
+ };
|
|
|
+
|
|
|
+ commonNames = js_new<JSAtomState>();
|
|
|
+- if (!commonNames)
|
|
|
++ if (!commonNames) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames.ref());
|
|
|
+ for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
|
|
|
+ JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ names->init(atom->asPropertyName());
|
|
|
+ }
|
|
|
+ MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
|
|
|
+
|
|
|
+ emptyString = commonNames->empty;
|
|
|
+
|
|
|
+ // Create the well-known symbols.
|
|
|
+ wellKnownSymbols = js_new<WellKnownSymbols>();
|
|
|
+- if (!wellKnownSymbols)
|
|
|
++ if (!wellKnownSymbols) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ ImmutablePropertyNamePtr* descriptions = commonNames->wellKnownSymbolDescriptions();
|
|
|
+ ImmutableSymbolPtr* symbols = reinterpret_cast<ImmutableSymbolPtr*>(wellKnownSymbols.ref());
|
|
|
+ for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
|
|
+ JS::Symbol* symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
|
|
|
+ if (!symbol) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+@@ -254,69 +265,74 @@ class AtomsTable::AutoLock
|
|
|
+ MOZ_ALWAYS_INLINE explicit AutoLock(JSRuntime* rt, Mutex& aLock) {
|
|
|
+ if (rt->hasHelperThreadZones()) {
|
|
|
+ lock = &aLock;
|
|
|
+ lock->lock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE ~AutoLock() {
|
|
|
+- if (lock)
|
|
|
++ if (lock) {
|
|
|
+ lock->unlock();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ AtomsTable::Partition::Partition(uint32_t index)
|
|
|
+ : lock(MutexId { mutexid::AtomsTable.name, mutexid::AtomsTable.order + index }),
|
|
|
+ atoms(InitialTableSize),
|
|
|
+ atomsAddedWhileSweeping(nullptr)
|
|
|
+ {}
|
|
|
+
|
|
|
+ AtomsTable::Partition::~Partition()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!atomsAddedWhileSweeping);
|
|
|
+ }
|
|
|
+
|
|
|
+ AtomsTable::~AtomsTable()
|
|
|
+ {
|
|
|
+- for (size_t i = 0; i < PartitionCount; i++)
|
|
|
++ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ js_delete(partitions[i]);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ AtomsTable::init()
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ partitions[i] = js_new<Partition>(i);
|
|
|
+- if (!partitions[i])
|
|
|
++ if (!partitions[i]) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ AtomsTable::lockAll()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!allPartitionsLocked);
|
|
|
+
|
|
|
+- for (size_t i = 0; i < PartitionCount; i++)
|
|
|
++ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ partitions[i]->lock.lock();
|
|
|
++ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ allPartitionsLocked = true;
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ AtomsTable::unlockAll()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(allPartitionsLocked);
|
|
|
+
|
|
|
+- for (size_t i = 0; i < PartitionCount; i++)
|
|
|
++ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ partitions[PartitionCount - i - 1]->lock.unlock();
|
|
|
++ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ allPartitionsLocked = false;
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE size_t
|
|
|
+ AtomsTable::getPartitionIndex(const AtomHasher::Lookup& lookup)
|
|
|
+@@ -341,27 +357,29 @@ AtomsTable::tracePinnedAtomsInSet(JSTrac
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ AtomsTable::tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access)
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ Partition& part = *partitions[i];
|
|
|
+ tracePinnedAtomsInSet(trc, part.atoms);
|
|
|
+- if (part.atomsAddedWhileSweeping)
|
|
|
++ if (part.atomsAddedWhileSweeping) {
|
|
|
+ tracePinnedAtomsInSet(trc, *part.atomsAddedWhileSweeping);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::TraceAtoms(JSTracer* trc, const AutoAccessAtomsZone& access)
|
|
|
+ {
|
|
|
+ JSRuntime* rt = trc->runtime();
|
|
|
+- if (rt->permanentAtomsPopulated())
|
|
|
++ if (rt->permanentAtomsPopulated()) {
|
|
|
+ rt->atoms().tracePinnedAtoms(trc, access);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ TracePermanentAtoms(JSTracer* trc, AtomSet::Range atoms)
|
|
|
+ {
|
|
|
+ for (; !atoms.empty(); atoms.popFront()) {
|
|
|
+ const AtomStateEntry& entry = atoms.front();
|
|
|
+ JSAtom* atom = entry.asPtrUnbarriered();
|
|
|
+@@ -369,54 +387,61 @@ TracePermanentAtoms(JSTracer* trc, AtomS
|
|
|
+ TraceProcessGlobalRoot(trc, atom, "permanent atom");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSRuntime::tracePermanentAtoms(JSTracer* trc)
|
|
|
+ {
|
|
|
+ // Permanent atoms only need to be traced in the runtime which owns them.
|
|
|
+- if (parentRuntime)
|
|
|
++ if (parentRuntime) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Static strings are not included in the permanent atoms table.
|
|
|
+- if (staticStrings)
|
|
|
++ if (staticStrings) {
|
|
|
+ staticStrings->trace(trc);
|
|
|
++ }
|
|
|
+
|
|
|
+- if (permanentAtomsDuringInit_)
|
|
|
++ if (permanentAtomsDuringInit_) {
|
|
|
+ TracePermanentAtoms(trc, permanentAtomsDuringInit_->all());
|
|
|
++ }
|
|
|
+
|
|
|
+- if (permanentAtoms_)
|
|
|
++ if (permanentAtoms_) {
|
|
|
+ TracePermanentAtoms(trc, permanentAtoms_->all());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::TraceWellKnownSymbols(JSTracer* trc)
|
|
|
+ {
|
|
|
+ JSRuntime* rt = trc->runtime();
|
|
|
+
|
|
|
+- if (rt->parentRuntime)
|
|
|
++ if (rt->parentRuntime) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
|
|
|
+- for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
|
|
|
++ for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
|
|
+ TraceProcessGlobalRoot(trc, wks->get(i).get(), "well_known_symbol");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ AtomsTable::sweepAll(JSRuntime* rt)
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ AutoLock lock(rt, partitions[i]->lock);
|
|
|
+ AtomSet& atoms = partitions[i]->atoms;
|
|
|
+ for (AtomSet::Enum e(atoms); !e.empty(); e.popFront()) {
|
|
|
+ JSAtom* atom = e.front().asPtrUnbarriered();
|
|
|
+- if (IsAboutToBeFinalizedUnbarriered(&atom))
|
|
|
++ if (IsAboutToBeFinalizedUnbarriered(&atom)) {
|
|
|
+ e.removeFront();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ AtomsTable::SweepIterator::SweepIterator(AtomsTable& atoms)
|
|
|
+ : atoms(atoms),
|
|
|
+ partitionIndex(0)
|
|
|
+ {
|
|
|
+@@ -441,18 +466,19 @@ AtomsTable::SweepIterator::finishSweepin
|
|
|
+ inline void
|
|
|
+ AtomsTable::SweepIterator::settle()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!empty());
|
|
|
+
|
|
|
+ while (atomsIter->empty()) {
|
|
|
+ finishSweepingPartition();
|
|
|
+ partitionIndex++;
|
|
|
+- if (empty())
|
|
|
++ if (empty()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+ startSweepingPartition();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ AtomsTable::SweepIterator::empty() const
|
|
|
+ {
|
|
|
+ return partitionIndex == PartitionCount;
|
|
|
+@@ -517,40 +543,44 @@ AtomsTable::mergeAtomsAddedWhileSweeping
|
|
|
+ // the main table.
|
|
|
+
|
|
|
+ AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
|
+
|
|
|
+ auto newAtoms = part.atomsAddedWhileSweeping;
|
|
|
+ part.atomsAddedWhileSweeping = nullptr;
|
|
|
+
|
|
|
+ for (auto r = newAtoms->all(); !r.empty(); r.popFront()) {
|
|
|
+- if (!part.atoms.putNew(AtomHasher::Lookup(r.front().asPtrUnbarriered()), r.front()))
|
|
|
++ if (!part.atoms.putNew(AtomHasher::Lookup(r.front().asPtrUnbarriered()), r.front())) {
|
|
|
+ oomUnsafe.crash("Adding atom from secondary table after sweep");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ js_delete(newAtoms);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ AtomsTable::sweepIncrementally(SweepIterator& atomsToSweep, SliceBudget& budget)
|
|
|
+ {
|
|
|
+ // Sweep the table incrementally until we run out of work or budget.
|
|
|
+ while (!atomsToSweep.empty()) {
|
|
|
+ budget.step();
|
|
|
+- if (budget.isOverBudget())
|
|
|
++ if (budget.isOverBudget()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom* atom = atomsToSweep.front();
|
|
|
+- if (IsAboutToBeFinalizedUnbarriered(&atom))
|
|
|
++ if (IsAboutToBeFinalizedUnbarriered(&atom)) {
|
|
|
+ atomsToSweep.removeFront();
|
|
|
++ }
|
|
|
+ atomsToSweep.popFront();
|
|
|
+ }
|
|
|
+
|
|
|
+- for (size_t i = 0; i < PartitionCount; i++)
|
|
|
++ for (size_t i = 0; i < PartitionCount; i++) {
|
|
|
+ MOZ_ASSERT(!partitions[i]->atomsAddedWhileSweeping);
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t
|
|
|
+ AtomsTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
|
+ {
|
|
|
+ size_t size = sizeof(AtomsTable);
|
|
|
+@@ -592,18 +622,19 @@ AllocateNewAtom(JSContext* cx, const Cha
|
|
|
+
|
|
|
+ /* |tbchars| must not point into an inline or short string. */
|
|
|
+ template <typename CharT>
|
|
|
+ MOZ_ALWAYS_INLINE
|
|
|
+ static JSAtom*
|
|
|
+ AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
|
|
|
+ const Maybe<uint32_t>& indexValue)
|
|
|
+ {
|
|
|
+- if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
|
|
|
++ if (JSAtom* s = cx->staticStrings().lookup(tbchars, length)) {
|
|
|
+ return s;
|
|
|
++ }
|
|
|
+
|
|
|
+ AtomHasher::Lookup lookup(tbchars, length);
|
|
|
+
|
|
|
+ // Try the per-Zone cache first. If we find the atom there we can avoid the
|
|
|
+ // atoms lock, the markAtom call, and the multiple HashSet lookups below.
|
|
|
+ // We don't use the per-Zone cache if we want a pinned atom: handling that
|
|
|
+ // is more complicated and pinning atoms is relatively uncommon.
|
|
|
+ Zone* zone = cx->zone();
|
|
|
+@@ -618,44 +649,47 @@ AtomizeAndCopyChars(JSContext* cx, const
|
|
|
+ MOZ_ASSERT(AtomIsMarked(zone, atom));
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // This function can be called during initialization, while the permanent
|
|
|
+ // atoms table is being created. In this case all atoms created are added to
|
|
|
+ // the permanent atoms table.
|
|
|
+- if (!cx->permanentAtomsPopulated())
|
|
|
++ if (!cx->permanentAtomsPopulated()) {
|
|
|
+ return PermanentlyAtomizeAndCopyChars(cx, zonePtr, tbchars, length, indexValue, lookup);
|
|
|
++ }
|
|
|
+
|
|
|
+ AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
|
|
|
+ if (pp) {
|
|
|
+ JSAtom* atom = pp->asPtr(cx);
|
|
|
+ if (zonePtr &&
|
|
|
+ MOZ_UNLIKELY(!zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))))
|
|
|
+ {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Validate the length before taking an atoms partition lock, as throwing an
|
|
|
+ // exception here may reenter this code.
|
|
|
+- if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
|
|
|
++ if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx,
|
|
|
+ tbchars, length,
|
|
|
+ pin,
|
|
|
+ indexValue,
|
|
|
+ lookup);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->atomMarking().inlinedMarkAtom(cx, atom);
|
|
|
+
|
|
|
+ if (zonePtr &&
|
|
|
+ MOZ_UNLIKELY(!zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))))
|
|
|
+ {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return nullptr;
|
|
|
+@@ -687,34 +721,36 @@ AtomsTable::atomizeAndCopyChars(JSContex
|
|
|
+ // be added to a secondary table. Check this first.
|
|
|
+ p = atomsAddedWhileSweeping->lookupForAdd(lookup);
|
|
|
+
|
|
|
+ // If that fails check the main table but check if any atom found there
|
|
|
+ // is dead.
|
|
|
+ if (!p) {
|
|
|
+ if (AtomSet::AddPtr p2 = atoms.lookupForAdd(lookup)) {
|
|
|
+ JSAtom* atom = p2->asPtrUnbarriered();
|
|
|
+- if (!IsAboutToBeFinalizedUnbarriered(&atom))
|
|
|
++ if (!IsAboutToBeFinalizedUnbarriered(&atom)) {
|
|
|
+ p = p2;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (p) {
|
|
|
+ JSAtom* atom = p->asPtr(cx);
|
|
|
+ if (pin && !atom->isPinned()) {
|
|
|
+ atom->setPinned();
|
|
|
+ p->setPinned(true);
|
|
|
+ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom* atom = AllocateNewAtom(cx, tbchars, length, pin, indexValue, lookup);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // We have held the lock since looking up p, and the operations we've done
|
|
|
+ // since then can't GC; therefore the atoms table has not been modified and
|
|
|
+ // p is still valid.
|
|
|
+ AtomSet* addSet = part.atomsAddedWhileSweeping ? part.atomsAddedWhileSweeping : &atoms;
|
|
|
+ if (MOZ_UNLIKELY(!addSet->add(p, AtomStateEntry(atom, bool(pin))))) {
|
|
|
+ ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
|
|
|
+ return nullptr;
|
|
|
+@@ -740,22 +776,24 @@ PermanentlyAtomizeAndCopyChars(JSContext
|
|
|
+ const AtomHasher::Lookup& lookup)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!cx->permanentAtomsPopulated());
|
|
|
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
|
|
+
|
|
|
+ JSRuntime* rt = cx->runtime();
|
|
|
+ AtomSet& atoms = *rt->permanentAtomsDuringInit();
|
|
|
+ AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
|
|
|
+- if (p)
|
|
|
++ if (p) {
|
|
|
+ return p->asPtr(cx);
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom* atom = AllocateNewAtom(cx, tbchars, length, DoNotPinAtom, indexValue, lookup);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ atom->morphIntoPermanentAtom();
|
|
|
+
|
|
|
+ // We are single threaded at this point, and the operations we've done since
|
|
|
+ // then can't GC; therefore the atoms table has not been modified and p is
|
|
|
+ // still valid.
|
|
|
+ if (!atoms.add(p, AtomStateEntry(atom, true))) {
|
|
|
+ ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
|
|
|
+@@ -786,45 +824,50 @@ AllocateNewAtom(JSContext* cx, const Cha
|
|
|
+ // please also fix or comment the similar case in Symbol::new_.
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom* atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
|
|
|
+ MOZ_ASSERT(atom->hash() == lookup.hash);
|
|
|
+
|
|
|
+- if (pin)
|
|
|
++ if (pin) {
|
|
|
+ atom->setPinned();
|
|
|
++ }
|
|
|
+
|
|
|
+- if (indexValue)
|
|
|
++ if (indexValue) {
|
|
|
+ atom->maybeInitializeIndex(*indexValue, true);
|
|
|
++ }
|
|
|
+
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom*
|
|
|
+ js::AtomizeString(JSContext* cx, JSString* str,
|
|
|
+ js::PinningBehavior pin /* = js::DoNotPinAtom */)
|
|
|
+ {
|
|
|
+ if (str->isAtom()) {
|
|
|
+ JSAtom& atom = str->asAtom();
|
|
|
+ /* N.B. static atoms are effectively always interned. */
|
|
|
+- if (pin == PinAtom && !atom.isPinned())
|
|
|
++ if (pin == PinAtom && !atom.isPinned()) {
|
|
|
+ cx->runtime()->atoms().pinExistingAtom(cx, &atom);
|
|
|
++ }
|
|
|
+
|
|
|
+ return &atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSLinearString* linear = str->ensureLinear(cx);
|
|
|
+- if (!linear)
|
|
|
++ if (!linear) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ Maybe<uint32_t> indexValue;
|
|
|
+- if (str->hasIndexValue())
|
|
|
++ if (str->hasIndexValue()) {
|
|
|
+ indexValue.emplace(str->getIndexValue());
|
|
|
++ }
|
|
|
+
|
|
|
+ JS::AutoCheckCannotGC nogc;
|
|
|
+ return linear->hasLatin1Chars()
|
|
|
+ ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
|
|
|
+ : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+@@ -833,18 +876,19 @@ AtomsTable::pinExistingAtom(JSContext* c
|
|
|
+ MOZ_ASSERT(atom);
|
|
|
+ MOZ_ASSERT(!atom->isPinned());
|
|
|
+
|
|
|
+ AtomHasher::Lookup lookup(atom);
|
|
|
+
|
|
|
+ AtomsTable::Partition& part = *partitions[getPartitionIndex(lookup)];
|
|
|
+ AtomsTable::AutoLock lock(cx->runtime(), part.lock);
|
|
|
+ AtomSet::Ptr p = part.atoms.lookup(lookup);
|
|
|
+- if (!p && part.atomsAddedWhileSweeping)
|
|
|
++ if (!p && part.atomsAddedWhileSweeping) {
|
|
|
+ p = part.atomsAddedWhileSweeping->lookup(lookup);
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(p); // Unpinned atoms must exist in atoms table.
|
|
|
+ MOZ_ASSERT(p->asPtrUnbarriered() == atom);
|
|
|
+
|
|
|
+ atom->setPinned();
|
|
|
+ p->setPinned(true);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -878,108 +922,120 @@ js::AtomizeUTF8Chars(JSContext* cx, cons
|
|
|
+ // This could be optimized to hand the char16_t's directly to the JSAtom
|
|
|
+ // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be
|
|
|
+ // refactored to take an JSContext so that this function could also.
|
|
|
+
|
|
|
+ UTF8Chars utf8(utf8Chars, utf8ByteLength);
|
|
|
+
|
|
|
+ size_t length;
|
|
|
+ UniqueTwoByteChars chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
|
|
|
+- if (!chars)
|
|
|
++ if (!chars) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return AtomizeChars(cx, chars.get(), length);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::IndexToIdSlow(JSContext* cx, uint32_t index, MutableHandleId idp)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(index > JSID_INT_MAX);
|
|
|
+
|
|
|
+ char16_t buf[UINT32_CHAR_BUFFER_LENGTH];
|
|
|
+ RangedPtr<char16_t> end(ArrayEnd(buf), buf, ArrayEnd(buf));
|
|
|
+ RangedPtr<char16_t> start = BackfillIndexInCharBuffer(index, end);
|
|
|
+
|
|
|
+ JSAtom* atom = AtomizeChars(cx, start.get(), end - start);
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ idp.set(JSID_FROM_BITS((size_t)atom | JSID_TYPE_STRING));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <AllowGC allowGC>
|
|
|
+ static JSAtom*
|
|
|
+ ToAtomSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!arg.isString());
|
|
|
+
|
|
|
+ Value v = arg;
|
|
|
+ if (!v.isPrimitive()) {
|
|
|
+ MOZ_ASSERT(!cx->helperThread());
|
|
|
+- if (!allowGC)
|
|
|
++ if (!allowGC) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ RootedValue v2(cx, v);
|
|
|
+- if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
|
|
|
++ if (!ToPrimitive(cx, JSTYPE_STRING, &v2)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ v = v2;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (v.isString()) {
|
|
|
+ JSAtom* atom = AtomizeString(cx, v.toString());
|
|
|
+- if (!allowGC && !atom)
|
|
|
++ if (!allowGC && !atom) {
|
|
|
+ cx->recoverFromOutOfMemory();
|
|
|
++ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+ if (v.isInt32()) {
|
|
|
+ JSAtom* atom = Int32ToAtom(cx, v.toInt32());
|
|
|
+- if (!allowGC && !atom)
|
|
|
++ if (!allowGC && !atom) {
|
|
|
+ cx->recoverFromOutOfMemory();
|
|
|
++ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+ if (v.isDouble()) {
|
|
|
+ JSAtom* atom = NumberToAtom(cx, v.toDouble());
|
|
|
+- if (!allowGC && !atom)
|
|
|
++ if (!allowGC && !atom) {
|
|
|
+ cx->recoverFromOutOfMemory();
|
|
|
++ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+- if (v.isBoolean())
|
|
|
++ if (v.isBoolean()) {
|
|
|
+ return v.toBoolean() ? cx->names().true_ : cx->names().false_;
|
|
|
+- if (v.isNull())
|
|
|
++ }
|
|
|
++ if (v.isNull()) {
|
|
|
+ return cx->names().null;
|
|
|
++ }
|
|
|
+ if (v.isSymbol()) {
|
|
|
+ MOZ_ASSERT(!cx->helperThread());
|
|
|
+ if (allowGC) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
|
|
+ JSMSG_SYMBOL_TO_STRING);
|
|
|
+ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ #ifdef ENABLE_BIGINT
|
|
|
+ if (v.isBigInt()) {
|
|
|
+ JSAtom* atom = BigIntToAtom(cx, v.toBigInt());
|
|
|
+- if (!allowGC && !atom)
|
|
|
++ if (!allowGC && !atom) {
|
|
|
+ cx->recoverFromOutOfMemory();
|
|
|
++ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ MOZ_ASSERT(v.isUndefined());
|
|
|
+ return cx->names().undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <AllowGC allowGC>
|
|
|
+ JSAtom*
|
|
|
+ js::ToAtom(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v)
|
|
|
+ {
|
|
|
+- if (!v.isString())
|
|
|
++ if (!v.isString()) {
|
|
|
+ return ToAtomSlow<allowGC>(cx, v);
|
|
|
++ }
|
|
|
+
|
|
|
+ JSString* str = v.toString();
|
|
|
+- if (str->isAtom())
|
|
|
++ if (str->isAtom()) {
|
|
|
+ return &str->asAtom();
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom* atom = AtomizeString(cx, str);
|
|
|
+ if (!atom && !allowGC) {
|
|
|
+ MOZ_ASSERT_IF(!cx->helperThread(), cx->isThrowingOutOfMemory());
|
|
|
+ cx->recoverFromOutOfMemory();
|
|
|
+ }
|
|
|
+ return atom;
|
|
|
+ }
|
|
|
+@@ -1008,23 +1064,25 @@ js::XDRAtom(XDRState<mode>* xdr, Mutable
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ length = lengthAndEncoding >> 1;
|
|
|
+ latin1 = lengthAndEncoding & 0x1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // We need to align the string in the XDR buffer such that we can avoid
|
|
|
+ // non-align loads of 16bits characters.
|
|
|
+- if (!latin1)
|
|
|
++ if (!latin1) {
|
|
|
+ MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
|
|
|
++ }
|
|
|
+
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ JS::AutoCheckCannotGC nogc;
|
|
|
+- if (latin1)
|
|
|
++ if (latin1) {
|
|
|
+ return xdr->codeChars(atomp->latin1Chars(nogc), length);
|
|
|
++ }
|
|
|
+ return xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(mode == XDR_DECODE);
|
|
|
+ /* Avoid JSString allocation for already existing atoms. See bug 321985. */
|
|
|
+ JSContext* cx = xdr->cx();
|
|
|
+ JSAtom* atom;
|
|
|
+ if (latin1) {
|
|
|
+@@ -1061,29 +1119,31 @@ js::XDRAtom(XDRState<mode>* xdr, Mutable
|
|
|
+ chars = stackChars;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * This is very uncommon. Don't use the tempLifoAlloc arena for this as
|
|
|
+ * most allocations here will be bigger than tempLifoAlloc's default
|
|
|
+ * chunk size.
|
|
|
+ */
|
|
|
+ heapChars.reset(cx->pod_malloc<char16_t>(length));
|
|
|
+- if (!heapChars)
|
|
|
++ if (!heapChars) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ chars = heapChars.get();
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeChars(chars, length));
|
|
|
+ atom = AtomizeChars(cx, chars, length);
|
|
|
+ #endif /* !MOZ_LITTLE_ENDIAN */
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ atomp.set(atom);
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ template XDRResult
|
|
|
+ js::XDRAtom(XDRState<XDR_ENCODE>* xdr, MutableHandleAtom atomp);
|
|
|
+
|
|
|
+ template XDRResult
|
|
|
+@@ -1094,18 +1154,20 @@ js::ClassName(JSProtoKey key, JSContext*
|
|
|
+ {
|
|
|
+ return ClassName(key, cx->names());
|
|
|
+ }
|
|
|
+
|
|
|
+ js::AutoLockAllAtoms::AutoLockAllAtoms(JSRuntime* rt)
|
|
|
+ : runtime(rt)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
|
|
|
+- if (runtime->hasHelperThreadZones())
|
|
|
++ if (runtime->hasHelperThreadZones()) {
|
|
|
+ runtime->atoms().lockAll();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ js::AutoLockAllAtoms::~AutoLockAllAtoms()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
|
|
|
+- if (runtime->hasHelperThreadZones())
|
|
|
++ if (runtime->hasHelperThreadZones()) {
|
|
|
+ runtime->atoms().unlockAll();
|
|
|
++ }
|
|
|
+ }
|
|
|
+diff --git a/js/src/vm/JSContext-inl.h b/js/src/vm/JSContext-inl.h
|
|
|
+--- a/js/src/vm/JSContext-inl.h
|
|
|
++++ b/js/src/vm/JSContext-inl.h
|
|
|
+@@ -53,28 +53,31 @@ class ContextChecks
|
|
|
+ c1, c2, argIndex);
|
|
|
+ }
|
|
|
+ static void fail(JS::Zone* z1, JS::Zone* z2, int argIndex) {
|
|
|
+ MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d\n",
|
|
|
+ z1, z2, argIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JS::Realm* r, int argIndex) {
|
|
|
+- if (r && r != realm())
|
|
|
++ if (r && r != realm()) {
|
|
|
+ fail(realm(), r, argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JS::Compartment* c, int argIndex) {
|
|
|
+- if (c && c != compartment())
|
|
|
++ if (c && c != compartment()) {
|
|
|
+ fail(compartment(), c, argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JS::Zone* z, int argIndex) {
|
|
|
+- if (zone() && z != zone())
|
|
|
++ if (zone() && z != zone()) {
|
|
|
+ fail(zone(), z, argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JSObject* obj, int argIndex) {
|
|
|
+ if (obj) {
|
|
|
+ MOZ_ASSERT(JS::ObjectIsNotGray(obj));
|
|
|
+ MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
|
|
|
+ check(obj->compartment(), argIndex);
|
|
|
+ }
|
|
|
+@@ -95,82 +98,91 @@ class ContextChecks
|
|
|
+ zone(), argIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JSString* str, int argIndex) {
|
|
|
+ MOZ_ASSERT(JS::CellIsNotGray(str));
|
|
|
+- if (str->isAtom())
|
|
|
++ if (str->isAtom()) {
|
|
|
+ checkAtom(&str->asAtom(), argIndex);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ check(str->zone(), argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JS::Symbol* symbol, int argIndex) {
|
|
|
+ checkAtom(symbol, argIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(const js::Value& v, int argIndex) {
|
|
|
+- if (v.isObject())
|
|
|
++ if (v.isObject()) {
|
|
|
+ check(&v.toObject(), argIndex);
|
|
|
+- else if (v.isString())
|
|
|
++ } else if (v.isString()) {
|
|
|
+ check(v.toString(), argIndex);
|
|
|
+- else if (v.isSymbol())
|
|
|
++ } else if (v.isSymbol()) {
|
|
|
+ check(v.toSymbol(), argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check the contents of any container class that supports the C++
|
|
|
+ // iteration protocol, eg GCVector<jsid>.
|
|
|
+ template <typename Container>
|
|
|
+ typename mozilla::EnableIf<
|
|
|
+ mozilla::IsSame<
|
|
|
+ decltype(((Container*)nullptr)->begin()),
|
|
|
+ decltype(((Container*)nullptr)->end())
|
|
|
+ >::value
|
|
|
+ >::Type
|
|
|
+ check(const Container& container, int argIndex) {
|
|
|
+- for (auto i : container)
|
|
|
++ for (auto i : container) {
|
|
|
+ check(i, argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(const JS::HandleValueArray& arr, int argIndex) {
|
|
|
+- for (size_t i = 0; i < arr.length(); i++)
|
|
|
++ for (size_t i = 0; i < arr.length(); i++) {
|
|
|
+ check(arr[i], argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(const CallArgs& args, int argIndex) {
|
|
|
+- for (Value* p = args.base(); p != args.end(); ++p)
|
|
|
++ for (Value* p = args.base(); p != args.end(); ++p) {
|
|
|
+ check(*p, argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(jsid id, int argIndex) {
|
|
|
+- if (JSID_IS_ATOM(id))
|
|
|
++ if (JSID_IS_ATOM(id)) {
|
|
|
+ checkAtom(JSID_TO_ATOM(id), argIndex);
|
|
|
+- else if (JSID_IS_SYMBOL(id))
|
|
|
++ } else if (JSID_IS_SYMBOL(id)) {
|
|
|
+ checkAtom(JSID_TO_SYMBOL(id), argIndex);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_ASSERT(!JSID_IS_GCTHING(id));
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(JSScript* script, int argIndex) {
|
|
|
+ MOZ_ASSERT(JS::CellIsNotGray(script));
|
|
|
+- if (script)
|
|
|
++ if (script) {
|
|
|
+ check(script->realm(), argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(AbstractFramePtr frame, int argIndex);
|
|
|
+
|
|
|
+ void check(Handle<PropertyDescriptor> desc, int argIndex) {
|
|
|
+ check(desc.object(), argIndex);
|
|
|
+- if (desc.hasGetterObject())
|
|
|
++ if (desc.hasGetterObject()) {
|
|
|
+ check(desc.getterObject(), argIndex);
|
|
|
+- if (desc.hasSetterObject())
|
|
|
++ }
|
|
|
++ if (desc.hasSetterObject()) {
|
|
|
+ check(desc.setterObject(), argIndex);
|
|
|
++ }
|
|
|
+ check(desc.value(), argIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ void check(TypeSet::Type type, int argIndex) {
|
|
|
+ check(type.maybeCompartment(), argIndex);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+@@ -182,34 +194,37 @@ JSContext::checkImpl(int argIndex, const
|
|
|
+ js::ContextChecks(this).check(head, argIndex);
|
|
|
+ checkImpl(argIndex + 1, tail...);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class... Args> inline void
|
|
|
+ JSContext::check(const Args&... args)
|
|
|
+ {
|
|
|
+ #ifdef JS_CRASH_DIAGNOSTICS
|
|
|
+- if (contextChecksEnabled())
|
|
|
++ if (contextChecksEnabled()) {
|
|
|
+ checkImpl(0, args...);
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class... Args> inline void
|
|
|
+ JSContext::releaseCheck(const Args&... args)
|
|
|
+ {
|
|
|
+- if (contextChecksEnabled())
|
|
|
++ if (contextChecksEnabled()) {
|
|
|
+ checkImpl(0, args...);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class... Args> MOZ_ALWAYS_INLINE void
|
|
|
+ JSContext::debugOnlyCheck(const Args&... args)
|
|
|
+ {
|
|
|
+ #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
|
|
|
+- if (contextChecksEnabled())
|
|
|
++ if (contextChecksEnabled()) {
|
|
|
+ checkImpl(0, args...);
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ CallNativeImpl(JSContext* cx, NativeImpl impl, const CallArgs& args)
|
|
|
+@@ -225,69 +240,76 @@ CallNativeImpl(JSContext* cx, NativeImpl
|
|
|
+ }
|
|
|
+ return ok;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id,
|
|
|
+ MutableHandleValue vp)
|
|
|
+ {
|
|
|
+- if (!CheckRecursionLimit(cx))
|
|
|
++ if (!CheckRecursionLimit(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->check(obj, id, vp);
|
|
|
+ bool ok = op(cx, obj, id, vp);
|
|
|
+- if (ok)
|
|
|
++ if (ok) {
|
|
|
+ cx->check(vp);
|
|
|
++ }
|
|
|
+ return ok;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ CallJSSetterOp(JSContext* cx, SetterOp op, HandleObject obj, HandleId id, HandleValue v,
|
|
|
+ ObjectOpResult& result)
|
|
|
+ {
|
|
|
+- if (!CheckRecursionLimit(cx))
|
|
|
++ if (!CheckRecursionLimit(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->check(obj, id, v);
|
|
|
+ return op(cx, obj, id, v, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id,
|
|
|
+ HandleValue v)
|
|
|
+ {
|
|
|
+- if (!CheckRecursionLimit(cx))
|
|
|
++ if (!CheckRecursionLimit(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->check(obj, id, v);
|
|
|
+ return op(cx, obj, id, v);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
|
|
|
+ ObjectOpResult& result)
|
|
|
+ {
|
|
|
+- if (!CheckRecursionLimit(cx))
|
|
|
++ if (!CheckRecursionLimit(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->check(receiver, id);
|
|
|
+- if (op)
|
|
|
++ if (op) {
|
|
|
+ return op(cx, receiver, id, result);
|
|
|
++ }
|
|
|
+ return result.succeed();
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ CheckForInterrupt(JSContext* cx)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!cx->isExceptionPending());
|
|
|
+ // Add an inline fast-path since we have to check for interrupts in some hot
|
|
|
+ // C++ loops of library builtins.
|
|
|
+- if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt()))
|
|
|
++ if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt())) {
|
|
|
+ return cx->handleInterrupt();
|
|
|
++ }
|
|
|
+
|
|
|
+ JS_INTERRUPT_POSSIBLY_FAIL();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ } /* namespace js */
|
|
|
+
|
|
|
+@@ -312,22 +334,24 @@ JSContext::minorGC(JS::gcreason::Reason
|
|
|
+ inline void
|
|
|
+ JSContext::setPendingException(JS::HandleValue v)
|
|
|
+ {
|
|
|
+ #if defined(NIGHTLY_BUILD)
|
|
|
+ do {
|
|
|
+ // Do not intercept exceptions if we are already
|
|
|
+ // in the exception interceptor. That would lead
|
|
|
+ // to infinite recursion.
|
|
|
+- if (this->runtime()->errorInterception.isExecuting)
|
|
|
++ if (this->runtime()->errorInterception.isExecuting) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Check whether we have an interceptor at all.
|
|
|
+- if (!this->runtime()->errorInterception.interceptor)
|
|
|
++ if (!this->runtime()->errorInterception.interceptor) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Make sure that we do not call the interceptor from within
|
|
|
+ // the interceptor.
|
|
|
+ this->runtime()->errorInterception.isExecuting = true;
|
|
|
+
|
|
|
+ // The interceptor must be infallible.
|
|
|
+ const mozilla::DebugOnly<bool> wasExceptionPending = this->isExceptionPending();
|
|
|
+ this->runtime()->errorInterception.interceptor->interceptError(this, v);
|
|
|
+@@ -365,18 +389,19 @@ JSContext::enterAtomsZone()
|
|
|
+ {
|
|
|
+ realm_ = nullptr;
|
|
|
+ setZone(runtime_->unsafeAtomsZone(), AtomsZone);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSContext::setZone(js::Zone *zone, JSContext::IsAtomsZone isAtomsZone)
|
|
|
+ {
|
|
|
+- if (zone_)
|
|
|
++ if (zone_) {
|
|
|
+ zone_->addTenuredAllocsSinceMinorGC(allocsThisZoneSinceMinorGC_);
|
|
|
++ }
|
|
|
+
|
|
|
+ allocsThisZoneSinceMinorGC_ = 0;
|
|
|
+
|
|
|
+ zone_ = zone;
|
|
|
+ if (zone == nullptr) {
|
|
|
+ freeLists_ = nullptr;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+@@ -425,18 +450,19 @@ JSContext::leaveRealm(JS::Realm* oldReal
|
|
|
+ // Only call leave() after we've setRealm()-ed away from the current realm.
|
|
|
+ JS::Realm* startingRealm = realm_;
|
|
|
+
|
|
|
+ // The current realm should be marked as entered-from-C++ at this point.
|
|
|
+ MOZ_ASSERT_IF(startingRealm, startingRealm->hasBeenEnteredIgnoringJit());
|
|
|
+
|
|
|
+ setRealm(oldRealm);
|
|
|
+
|
|
|
+- if (startingRealm)
|
|
|
++ if (startingRealm) {
|
|
|
+ startingRealm->leave();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSContext::leaveAtomsZone(JS::Realm* oldRealm)
|
|
|
+ {
|
|
|
+ setRealm(oldRealm);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -461,49 +487,55 @@ JSContext::setRealmForJitExceptionHandle
|
|
|
+ // so we don't call realm->leave() here.
|
|
|
+ MOZ_ASSERT(realm->compartment() == compartment());
|
|
|
+ realm_ = realm;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSScript*
|
|
|
+ JSContext::currentScript(jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const
|
|
|
+ {
|
|
|
+- if (ppc)
|
|
|
++ if (ppc) {
|
|
|
+ *ppc = nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ js::Activation* act = activation();
|
|
|
+- if (!act)
|
|
|
++ if (!act) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(act->cx() == this);
|
|
|
+
|
|
|
+ // Cross-compartment implies cross-realm.
|
|
|
+- if (allowCrossRealm == AllowCrossRealm::DontAllow && act->compartment() != compartment())
|
|
|
++ if (allowCrossRealm == AllowCrossRealm::DontAllow && act->compartment() != compartment()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSScript* script = nullptr;
|
|
|
+ jsbytecode* pc = nullptr;
|
|
|
+ if (act->isJit()) {
|
|
|
+- if (act->hasWasmExitFP())
|
|
|
++ if (act->hasWasmExitFP()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc);
|
|
|
+ } else {
|
|
|
+ js::InterpreterFrame* fp = act->asInterpreter()->current();
|
|
|
+ MOZ_ASSERT(!fp->runningInJit());
|
|
|
+ script = fp->script();
|
|
|
+ pc = act->asInterpreter()->regs().pc;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(script->containsPC(pc));
|
|
|
+
|
|
|
+- if (allowCrossRealm == AllowCrossRealm::DontAllow && script->realm() != realm())
|
|
|
++ if (allowCrossRealm == AllowCrossRealm::DontAllow && script->realm() != realm()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (ppc)
|
|
|
++ if (ppc) {
|
|
|
+ *ppc = pc;
|
|
|
++ }
|
|
|
+ return script;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::RuntimeCaches&
|
|
|
+ JSContext::caches()
|
|
|
+ {
|
|
|
+ return runtime()->caches();
|
|
|
+ }
|
|
|
+diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp
|
|
|
+--- a/js/src/vm/JSContext.cpp
|
|
|
++++ b/js/src/vm/JSContext.cpp
|
|
|
+@@ -72,22 +72,24 @@ using mozilla::PodArrayZero;
|
|
|
+ bool
|
|
|
+ js::AutoCycleDetector::init()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(cyclic);
|
|
|
+
|
|
|
+ AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
|
|
|
+
|
|
|
+ for (JSObject* obj2 : vector) {
|
|
|
+- if (MOZ_UNLIKELY(obj == obj2))
|
|
|
++ if (MOZ_UNLIKELY(obj == obj2)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!vector.append(obj))
|
|
|
++ if (!vector.append(obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cyclic = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ js::AutoCycleDetector::~AutoCycleDetector()
|
|
|
+ {
|
|
|
+ if (MOZ_LIKELY(!cyclic)) {
|
|
|
+@@ -102,34 +104,39 @@ js::AutoCycleDetector::~AutoCycleDetecto
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSContext::init(ContextKind kind)
|
|
|
+ {
|
|
|
+ // Skip most of the initialization if this thread will not be running JS.
|
|
|
+ if (kind == ContextKind::MainThread) {
|
|
|
+- if (!regexpStack.ref().init())
|
|
|
++ if (!regexpStack.ref().init()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (!fx.initInstance())
|
|
|
++ if (!fx.initInstance()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ #ifdef JS_SIMULATOR
|
|
|
+ simulator_ = jit::Simulator::Create(this);
|
|
|
+- if (!simulator_)
|
|
|
++ if (!simulator_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+- if (!wasm::EnsureSignalHandlers(this))
|
|
|
++ if (!wasm::EnsureSignalHandlers(this)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ atomsZoneFreeLists_ = js_new<gc::FreeLists>();
|
|
|
+- if (!atomsZoneFreeLists_)
|
|
|
++ if (!atomsZoneFreeLists_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the ContextKind last, so that ProtectedData checks will allow us to
|
|
|
+ // initialize this context before it becomes the runtime's active context.
|
|
|
+ kind_ = kind;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -142,18 +149,19 @@ js::NewContext(uint32_t maxBytes, uint32
|
|
|
+ MOZ_RELEASE_ASSERT(!TlsContext.get());
|
|
|
+
|
|
|
+ #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
|
|
+ js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
|
|
|
+ : js::THREAD_TYPE_WORKER);
|
|
|
+ #endif
|
|
|
+
|
|
|
+ JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
|
|
|
+- if (!runtime)
|
|
|
++ if (!runtime) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
|
|
|
+ if (!cx) {
|
|
|
+ js_delete(runtime);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
|
|
|
+@@ -171,18 +179,19 @@ js::NewContext(uint32_t maxBytes, uint32
|
|
|
+ }
|
|
|
+
|
|
|
+ return cx;
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ FreeJobQueueHandling(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (!cx->jobQueue)
|
|
|
++ if (!cx->jobQueue) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->jobQueue->reset();
|
|
|
+ FreeOp* fop = cx->defaultFreeOp();
|
|
|
+ fop->delete_(cx->jobQueue.ref());
|
|
|
+ cx->getIncumbentGlobalCallback = nullptr;
|
|
|
+ cx->enqueuePromiseJobCallback = nullptr;
|
|
|
+ cx->enqueuePromiseJobCallbackData = nullptr;
|
|
|
+ }
|
|
|
+@@ -208,30 +217,32 @@ js::DestroyContext(JSContext* cx)
|
|
|
+ cx->runtime()->destroyRuntime();
|
|
|
+ js_delete(cx->runtime());
|
|
|
+ js_delete_poison(cx);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JS::RootingContext::checkNoGCRooters() {
|
|
|
+ #ifdef DEBUG
|
|
|
+- for (auto const& stackRootPtr : stackRoots_)
|
|
|
++ for (auto const& stackRootPtr : stackRoots_) {
|
|
|
+ MOZ_ASSERT(stackRootPtr == nullptr);
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ AutoResolving::alreadyStartedSlow() const
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(link);
|
|
|
+ AutoResolving* cursor = link;
|
|
|
+ do {
|
|
|
+ MOZ_ASSERT(this != cursor);
|
|
|
+- if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
|
|
|
++ if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ } while (!!(cursor = cursor->link));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback,
|
|
|
+ void* userRef)
|
|
|
+ {
|
|
|
+@@ -259,26 +270,28 @@ ReportError(JSContext* cx, JSErrorReport
|
|
|
+ /*
|
|
|
+ * The given JSErrorReport object have been zeroed and must not outlive
|
|
|
+ * cx->fp() (otherwise owned fields may become invalid).
|
|
|
+ */
|
|
|
+ static void
|
|
|
+ PopulateReportBlame(JSContext* cx, JSErrorReport* report)
|
|
|
+ {
|
|
|
+ JS::Realm* realm = cx->realm();
|
|
|
+- if (!realm)
|
|
|
++ if (!realm) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Walk stack until we find a frame that is associated with a non-builtin
|
|
|
+ * rather than a builtin frame and which we're allowed to know about.
|
|
|
+ */
|
|
|
+ NonBuiltinFrameIter iter(cx, realm->principals());
|
|
|
+- if (iter.done())
|
|
|
++ if (iter.done()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ report->filename = iter.filename();
|
|
|
+ uint32_t column;
|
|
|
+ report->lineno = iter.computeLine(&column);
|
|
|
+ report->column = FixupColumnForDisplay(column);
|
|
|
+ report->isMuted = iter.mutedErrors();
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -299,25 +312,27 @@ js::ReportOutOfMemory(JSContext* cx)
|
|
|
+ * OOMs are non-deterministic, especially across different execution modes
|
|
|
+ * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
|
|
|
+ * so that the fuzzers can detect this.
|
|
|
+ */
|
|
|
+ fprintf(stderr, "ReportOutOfMemory called\n");
|
|
|
+ #endif
|
|
|
+ mozilla::recordreplay::InvalidateRecording("OutOfMemory exception thrown");
|
|
|
+
|
|
|
+- if (cx->helperThread())
|
|
|
++ if (cx->helperThread()) {
|
|
|
+ return cx->addPendingOutOfMemory();
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->runtime()->hadOutOfMemory = true;
|
|
|
+ gc::AutoSuppressGC suppressGC(cx);
|
|
|
+
|
|
|
+ /* Report the oom. */
|
|
|
+- if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
|
|
|
++ if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
|
|
|
+ oomCallback(cx, cx->runtime()->oomCallbackData);
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue oomMessage(cx, StringValue(cx->names().outOfMemory));
|
|
|
+ cx->setPendingException(oomMessage);
|
|
|
+ }
|
|
|
+
|
|
|
+ mozilla::GenericErrorResult<OOM&>
|
|
|
+ js::ReportOutOfMemoryResult(JSContext* cx)
|
|
|
+ {
|
|
|
+@@ -354,56 +369,61 @@ JS_FRIEND_API(void)
|
|
|
+ js::ReportOverRecursed(JSContext* maybecx)
|
|
|
+ {
|
|
|
+ ReportOverRecursed(maybecx, JSMSG_OVER_RECURSED);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ReportAllocationOverflow(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (!cx)
|
|
|
++ if (!cx) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (cx->helperThread())
|
|
|
++ if (cx->helperThread()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ gc::AutoSuppressGC suppressGC(cx);
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Given flags and the state of cx, decide whether we should report an
|
|
|
+ * error, a warning, or just continue execution normally. Return
|
|
|
+ * true if we should continue normally, without reporting anything;
|
|
|
+ * otherwise, adjust *flags as appropriate and return false.
|
|
|
+ */
|
|
|
+ static bool
|
|
|
+ checkReportFlags(JSContext* cx, unsigned* flags)
|
|
|
+ {
|
|
|
+ if (JSREPORT_IS_STRICT(*flags)) {
|
|
|
+ /* Warning/error only when JSOPTION_STRICT is set. */
|
|
|
+- if (!cx->realm()->behaviors().extraWarnings(cx))
|
|
|
++ if (!cx->realm()->behaviors().extraWarnings(cx)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Warnings become errors when JSOPTION_WERROR is set. */
|
|
|
+- if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
|
|
|
++ if (JSREPORT_IS_WARNING(*flags) && cx->options().werror()) {
|
|
|
+ *flags &= ~JSREPORT_WARNING;
|
|
|
++ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
|
|
|
+ ErrorArgumentsType argumentsType, va_list ap)
|
|
|
+ {
|
|
|
+ JSErrorReport report;
|
|
|
+
|
|
|
+- if (checkReportFlags(cx, &flags))
|
|
|
++ if (checkReportFlags(cx, &flags)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ UniqueChars message(JS_vsmprintf(format, ap));
|
|
|
+ if (!message) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message.get()));
|
|
|
+@@ -411,43 +431,46 @@ js::ReportErrorVA(JSContext* cx, unsigne
|
|
|
+ report.flags = flags;
|
|
|
+ report.errorNumber = JSMSG_USER_DEFINED_ERROR;
|
|
|
+ if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
|
|
|
+ report.initOwnedMessage(message.release());
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
|
|
|
+ Latin1Chars latin1(message.get(), strlen(message.get()));
|
|
|
+ UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
|
|
|
+- if (!utf8)
|
|
|
++ if (!utf8) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
|
|
|
+ }
|
|
|
+ PopulateReportBlame(cx, &report);
|
|
|
+
|
|
|
+ bool warning = JSREPORT_IS_WARNING(report.flags);
|
|
|
+
|
|
|
+ ReportError(cx, &report, nullptr, nullptr);
|
|
|
+ return warning;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
|
|
|
+ void
|
|
|
+ js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
|
|
|
+ {
|
|
|
+ RootedValue usage(cx);
|
|
|
+- if (!JS_GetProperty(cx, callee, "usage", &usage))
|
|
|
++ if (!JS_GetProperty(cx, callee, "usage", &usage)) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!usage.isString()) {
|
|
|
+ JS_ReportErrorASCII(cx, "%s", msg);
|
|
|
+ } else {
|
|
|
+ RootedString usageStr(cx, usage.toString());
|
|
|
+ UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+ JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ enum class PrintErrorKind {
|
|
|
+ Error,
|
|
|
+ Warning,
|
|
|
+ StrictWarning,
|
|
|
+@@ -456,34 +479,39 @@ enum class PrintErrorKind {
|
|
|
+
|
|
|
+ static void
|
|
|
+ PrintErrorLine(FILE* file, const char* prefix, JSErrorReport* report)
|
|
|
+ {
|
|
|
+ if (const char16_t* linebuf = report->linebuf()) {
|
|
|
+ size_t n = report->linebufLength();
|
|
|
+
|
|
|
+ fputs(":\n", file);
|
|
|
+- if (prefix)
|
|
|
++ if (prefix) {
|
|
|
+ fputs(prefix, file);
|
|
|
++ }
|
|
|
+
|
|
|
+- for (size_t i = 0; i < n; i++)
|
|
|
++ for (size_t i = 0; i < n; i++) {
|
|
|
+ fputc(static_cast<char>(linebuf[i]), file);
|
|
|
++ }
|
|
|
+
|
|
|
+ // linebuf usually ends with a newline. If not, add one here.
|
|
|
+- if (n == 0 || linebuf[n-1] != '\n')
|
|
|
++ if (n == 0 || linebuf[n-1] != '\n') {
|
|
|
+ fputc('\n', file);
|
|
|
++ }
|
|
|
+
|
|
|
+- if (prefix)
|
|
|
++ if (prefix) {
|
|
|
+ fputs(prefix, file);
|
|
|
++ }
|
|
|
+
|
|
|
+ n = report->tokenOffset();
|
|
|
+ for (size_t i = 0, j = 0; i < n; i++) {
|
|
|
+ if (linebuf[i] == '\t') {
|
|
|
+- for (size_t k = (j + 8) & ~7; j < k; j++)
|
|
|
++ for (size_t k = (j + 8) & ~7; j < k; j++) {
|
|
|
+ fputc('.', file);
|
|
|
++ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ fputc('.', file);
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ fputc('^', file);
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -494,18 +522,19 @@ PrintErrorLine(FILE* file, const char* p
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename T>
|
|
|
+ static bool
|
|
|
+ PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
|
|
|
+ T* report, PrintErrorKind kind)
|
|
|
+ {
|
|
|
+ UniqueChars prefix;
|
|
|
+- if (report->filename)
|
|
|
++ if (report->filename) {
|
|
|
+ prefix = JS_smprintf("%s:", report->filename);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (report->lineno) {
|
|
|
+ prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
|
|
|
+ report->column);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (kind != PrintErrorKind::Error) {
|
|
|
+ const char* kindPrefix = nullptr;
|
|
|
+@@ -527,56 +556,61 @@ PrintSingleError(JSContext* cx, FILE* fi
|
|
|
+ }
|
|
|
+
|
|
|
+ const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
|
|
|
+
|
|
|
+ /* embedded newlines -- argh! */
|
|
|
+ const char* ctmp;
|
|
|
+ while ((ctmp = strchr(message, '\n')) != 0) {
|
|
|
+ ctmp++;
|
|
|
+- if (prefix)
|
|
|
++ if (prefix) {
|
|
|
+ fputs(prefix.get(), file);
|
|
|
++ }
|
|
|
+ mozilla::Unused << fwrite(message, 1, ctmp - message, file);
|
|
|
+ message = ctmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If there were no filename or lineno, the prefix might be empty */
|
|
|
+- if (prefix)
|
|
|
++ if (prefix) {
|
|
|
+ fputs(prefix.get(), file);
|
|
|
++ }
|
|
|
+ fputs(message, file);
|
|
|
+
|
|
|
+ PrintErrorLine(file, prefix.get(), report);
|
|
|
+ fputc('\n', file);
|
|
|
+
|
|
|
+ fflush(file);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
|
|
|
+ JSErrorReport* report, bool reportWarnings)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(report);
|
|
|
+
|
|
|
+ /* Conditionally ignore reported warnings. */
|
|
|
+- if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
|
|
|
++ if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ PrintErrorKind kind = PrintErrorKind::Error;
|
|
|
+ if (JSREPORT_IS_WARNING(report->flags)) {
|
|
|
+- if (JSREPORT_IS_STRICT(report->flags))
|
|
|
++ if (JSREPORT_IS_STRICT(report->flags)) {
|
|
|
+ kind = PrintErrorKind::StrictWarning;
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ kind = PrintErrorKind::Warning;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ PrintSingleError(cx, file, toStringResult, report, kind);
|
|
|
+
|
|
|
+ if (report->notes) {
|
|
|
+- for (auto&& note : *report->notes)
|
|
|
++ for (auto&& note : *report->notes) {
|
|
|
+ PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ class MOZ_RAII AutoMessageArgs
|
|
|
+ {
|
|
|
+ size_t totalLength_;
|
|
|
+@@ -594,18 +628,19 @@ class MOZ_RAII AutoMessageArgs
|
|
|
+ }
|
|
|
+
|
|
|
+ ~AutoMessageArgs()
|
|
|
+ {
|
|
|
+ /* free the arguments only if we allocated them */
|
|
|
+ if (allocatedElements_) {
|
|
|
+ uint16_t i = 0;
|
|
|
+ while (i < count_) {
|
|
|
+- if (args_[i])
|
|
|
++ if (args_[i]) {
|
|
|
+ js_free((void*)args_[i]);
|
|
|
++ }
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const char* args(size_t i) const {
|
|
|
+ MOZ_ASSERT(i < count_);
|
|
|
+ return args_[i];
|
|
|
+@@ -642,31 +677,33 @@ class MOZ_RAII AutoMessageArgs
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ArgumentsAreLatin1: {
|
|
|
+ MOZ_ASSERT(!argsArg);
|
|
|
+ const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
|
|
|
+ size_t len = strlen(reinterpret_cast<const char*>(latin1));
|
|
|
+ mozilla::Range<const Latin1Char> range(latin1, len);
|
|
|
+ char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
|
|
|
+- if (!utf8)
|
|
|
++ if (!utf8) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ args_[i] = utf8;
|
|
|
+ lengths_[i] = strlen(utf8);
|
|
|
+ allocatedElements_ = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ArgumentsAreUnicode: {
|
|
|
+ const char16_t* uc = argsArg ? argsArg[i] : va_arg(ap, char16_t*);
|
|
|
+ size_t len = js_strlen(uc);
|
|
|
+ mozilla::Range<const char16_t> range(uc, len);
|
|
|
+ char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
|
|
|
+- if (!utf8)
|
|
|
++ if (!utf8) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ args_[i] = utf8;
|
|
|
+ lengths_[i] = strlen(utf8);
|
|
|
+ allocatedElements_ = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ totalLength_ += lengths_[i];
|
|
|
+@@ -703,18 +740,19 @@ bool
|
|
|
+ ExpandErrorArgumentsHelper(JSContext* cx, JSErrorCallback callback,
|
|
|
+ void* userRef, const unsigned errorNumber,
|
|
|
+ const char16_t** messageArgs,
|
|
|
+ ErrorArgumentsType argumentsType,
|
|
|
+ T* reportp, va_list ap)
|
|
|
+ {
|
|
|
+ const JSErrorFormatString* efs;
|
|
|
+
|
|
|
+- if (!callback)
|
|
|
++ if (!callback) {
|
|
|
+ callback = GetErrorMessage;
|
|
|
++ }
|
|
|
+
|
|
|
+ {
|
|
|
+ gc::AutoSuppressGC suppressGC(cx);
|
|
|
+ efs = callback(userRef, errorNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (efs) {
|
|
|
+ SetExnType(reportp, efs->exnType);
|
|
|
+@@ -733,30 +771,32 @@ ExpandErrorArgumentsHelper(JSContext* cx
|
|
|
+ char* out;
|
|
|
+ #ifdef DEBUG
|
|
|
+ int expandedArgs = 0;
|
|
|
+ #endif
|
|
|
+ size_t expandedLength;
|
|
|
+ size_t len = strlen(efs->format);
|
|
|
+
|
|
|
+ AutoMessageArgs args;
|
|
|
+- if (!args.init(cx, messageArgs, argCount, argumentsType, ap))
|
|
|
++ if (!args.init(cx, messageArgs, argCount, argumentsType, ap)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ expandedLength = len
|
|
|
+ - (3 * args.count()) /* exclude the {n} */
|
|
|
+ + args.totalLength();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Note - the above calculation assumes that each argument
|
|
|
+ * is used once and only once in the expansion !!!
|
|
|
+ */
|
|
|
+ char* utf8 = out = cx->pod_malloc<char>(expandedLength + 1);
|
|
|
+- if (!out)
|
|
|
++ if (!out) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ fmt = efs->format;
|
|
|
+ while (*fmt) {
|
|
|
+ if (*fmt == '{') {
|
|
|
+ if (isdigit(fmt[1])) {
|
|
|
+ int d = JS7_UNDEC(fmt[1]);
|
|
|
+ MOZ_RELEASE_ASSERT(d < args.count());
|
|
|
+ strncpy(out, args.args(d), args.lengths(d));
|
|
|
+@@ -777,28 +817,30 @@ ExpandErrorArgumentsHelper(JSContext* cx
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* Non-null messageArgs should have at least one non-null arg. */
|
|
|
+ MOZ_ASSERT(!messageArgs);
|
|
|
+ /*
|
|
|
+ * Zero arguments: the format string (if it exists) is the
|
|
|
+ * entire message.
|
|
|
+ */
|
|
|
+- if (efs->format)
|
|
|
++ if (efs->format) {
|
|
|
+ reportp->initBorrowedMessage(efs->format);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!reportp->message()) {
|
|
|
+ /* where's the right place for this ??? */
|
|
|
+ const char* defaultErrorMessage
|
|
|
+ = "No error message available for error number %d";
|
|
|
+ size_t nbytes = strlen(defaultErrorMessage) + 16;
|
|
|
+ char* message = cx->pod_malloc<char>(nbytes);
|
|
|
+- if (!message)
|
|
|
++ if (!message) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ snprintf(message, nbytes, defaultErrorMessage, errorNumber);
|
|
|
+ reportp->initOwnedMessage(message);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
|
|
|
+@@ -825,18 +867,19 @@ js::ExpandErrorArgumentsVA(JSContext* cx
|
|
|
+ bool
|
|
|
+ js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
|
|
|
+ void* userRef, const unsigned errorNumber,
|
|
|
+ ErrorArgumentsType argumentsType, va_list ap)
|
|
|
+ {
|
|
|
+ JSErrorReport report;
|
|
|
+ bool warning;
|
|
|
+
|
|
|
+- if (checkReportFlags(cx, &flags))
|
|
|
++ if (checkReportFlags(cx, &flags)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ warning = JSREPORT_IS_WARNING(flags);
|
|
|
+
|
|
|
+ report.flags = flags;
|
|
|
+ report.errorNumber = errorNumber;
|
|
|
+ PopulateReportBlame(cx, &report);
|
|
|
+
|
|
|
+ if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
|
|
|
+ nullptr, argumentsType, &report, ap)) {
|
|
|
+@@ -863,18 +906,19 @@ ExpandErrorArguments(JSContext* cx, JSEr
|
|
|
+ return expanded;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
|
|
|
+ void* userRef, const unsigned errorNumber,
|
|
|
+ const char16_t** args)
|
|
|
+ {
|
|
|
+- if (checkReportFlags(cx, &flags))
|
|
|
++ if (checkReportFlags(cx, &flags)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ bool warning = JSREPORT_IS_WARNING(flags);
|
|
|
+
|
|
|
+ JSErrorReport report;
|
|
|
+ report.flags = flags;
|
|
|
+ report.errorNumber = errorNumber;
|
|
|
+ PopulateReportBlame(cx, &report);
|
|
|
+
|
|
|
+ if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
|
|
|
+@@ -886,18 +930,19 @@ js::ReportErrorNumberUCArray(JSContext*
|
|
|
+ ReportError(cx, &report, callback, userRef);
|
|
|
+
|
|
|
+ return warning;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ReportIsNotDefined(JSContext* cx, HandleId id)
|
|
|
+ {
|
|
|
+- if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
|
|
|
++ if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.get());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
|
|
|
+ {
|
|
|
+ RootedId id(cx, NameToId(name));
|
|
|
+ ReportIsNotDefined(cx, id);
|
|
|
+ }
|
|
|
+@@ -909,18 +954,19 @@ js::ReportIsNullOrUndefinedForPropertyAc
|
|
|
+
|
|
|
+ if (!reportScanStack) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
|
|
|
+ v.isNull() ? "null" : "undefined", "object");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, bytes.get());
|
|
|
+ } else if (v.isUndefined()) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
|
|
|
+ js_undefined_str);
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(v.isNull());
|
|
|
+@@ -931,29 +977,31 @@ js::ReportIsNullOrUndefinedForPropertyAc
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
|
|
|
+ bool reportScanStack)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(v.isNullOrUndefined());
|
|
|
+
|
|
|
+ UniqueChars keyBytes = IdToPrintableUTF8(cx, key, IdToPrintableBehavior::IdIsPropertyKey);
|
|
|
+- if (!keyBytes)
|
|
|
++ if (!keyBytes) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!reportScanStack) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
|
|
|
+ keyBytes.get(),
|
|
|
+ v.isUndefined() ? js_undefined_str : js_null_str);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
|
|
|
+ keyBytes.get(), bytes.get());
|
|
|
+ } else if (v.isUndefined()) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
|
|
|
+ bytes.get(), js_undefined_str, keyBytes.get());
|
|
|
+ } else {
|
|
|
+@@ -968,106 +1016,120 @@ js::ReportMissingArg(JSContext* cx, Hand
|
|
|
+ {
|
|
|
+ char argbuf[11];
|
|
|
+ UniqueChars bytes;
|
|
|
+
|
|
|
+ SprintfLiteral(argbuf, "%u", arg);
|
|
|
+ if (IsFunctionObject(v)) {
|
|
|
+ RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
|
|
|
+ bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_MISSING_FUN_ARG,
|
|
|
+ argbuf, bytes ? bytes.get() : "");
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
|
|
|
+ int spindex, HandleValue v, HandleString fallback,
|
|
|
+ const char* arg1, const char* arg2)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
|
|
|
+ MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
|
|
|
+ UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, errorNumber,
|
|
|
+ bytes.get(), arg1, arg2);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
|
|
|
+ {
|
|
|
+ RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
|
|
|
+- if (!notesArray)
|
|
|
++ if (!notesArray) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (!report->notes)
|
|
|
++ if (!report->notes) {
|
|
|
+ return notesArray;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (auto&& note : *report->notes) {
|
|
|
+ RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
|
|
+- if (!noteObj)
|
|
|
++ if (!noteObj) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedString messageStr(cx, note->newMessageString(cx));
|
|
|
+- if (!messageStr)
|
|
|
++ if (!messageStr) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ RootedValue messageVal(cx, StringValue(messageStr));
|
|
|
+- if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal))
|
|
|
++ if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue filenameVal(cx);
|
|
|
+ if (note->filename) {
|
|
|
+ RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
|
|
|
+- if (!filenameStr)
|
|
|
++ if (!filenameStr) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ filenameVal = StringValue(filenameStr);
|
|
|
+ }
|
|
|
+- if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal))
|
|
|
++ if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue linenoVal(cx, Int32Value(note->lineno));
|
|
|
+- if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal))
|
|
|
++ if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ RootedValue columnVal(cx, Int32Value(note->column));
|
|
|
+- if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal))
|
|
|
++ if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj)))
|
|
|
++ if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return notesArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
|
|
|
+ #define MSG_DEF(name, count, exception, format) \
|
|
|
+ { #name, format, count, exception } ,
|
|
|
+ #include "js.msg"
|
|
|
+ #undef MSG_DEF
|
|
|
+ };
|
|
|
+
|
|
|
+ JS_FRIEND_API(const JSErrorFormatString*)
|
|
|
+ js::GetErrorMessage(void* userRef, const unsigned errorNumber)
|
|
|
+ {
|
|
|
+- if (errorNumber > 0 && errorNumber < JSErr_Limit)
|
|
|
++ if (errorNumber > 0 && errorNumber < JSErr_Limit) {
|
|
|
+ return &js_ErrorFormatString[errorNumber];
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSContext::recoverFromOutOfMemory()
|
|
|
+ {
|
|
|
+ if (helperThread()) {
|
|
|
+ // Keep in sync with addPendingOutOfMemory.
|
|
|
+- if (ParseTask* task = helperThread()->parseTask())
|
|
|
++ if (ParseTask* task = helperThread()->parseTask()) {
|
|
|
+ task->outOfMemory = false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ if (isExceptionPending()) {
|
|
|
+ MOZ_ASSERT(isThrowingOutOfMemory());
|
|
|
+ clearPendingException();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1089,18 +1151,19 @@ JS_FRIEND_API(bool)
|
|
|
+ js::UseInternalJobQueues(JSContext* cx)
|
|
|
+ {
|
|
|
+ // Internal job queue handling must be set up very early. Self-hosting
|
|
|
+ // initialization is as good a marker for that as any.
|
|
|
+ MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
|
|
|
+ "js::UseInternalJobQueues must be called early during runtime startup.");
|
|
|
+ MOZ_ASSERT(!cx->jobQueue);
|
|
|
+ auto* queue = js_new<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
|
|
|
+- if (!queue)
|
|
|
++ if (!queue) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->jobQueue = queue;
|
|
|
+
|
|
|
+ cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
|
|
|
+ MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
|
|
|
+
|
|
|
+ JS::SetEnqueuePromiseJobCallback(cx, InternalEnqueuePromiseJobCallback);
|
|
|
+
|
|
|
+@@ -1127,18 +1190,19 @@ js::StopDrainingJobQueue(JSContext* cx)
|
|
|
+ cx->stopDrainingJobQueue = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_FRIEND_API(void)
|
|
|
+ js::RunJobs(JSContext* cx)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(cx->jobQueue);
|
|
|
+
|
|
|
+- if (cx->drainingJobQueue || cx->stopDrainingJobQueue)
|
|
|
++ if (cx->drainingJobQueue || cx->stopDrainingJobQueue) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
|
|
|
+
|
|
|
+ // It doesn't make sense for job queue draining to be reentrant. At the
|
|
|
+ // same time we don't want to assert against it, because that'd make
|
|
|
+ // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
|
|
|
+ // so we simply ignore nested calls of drainJobQueue.
|
|
|
+@@ -1149,41 +1213,45 @@ js::RunJobs(JSContext* cx)
|
|
|
+ RootedValue rval(cx);
|
|
|
+
|
|
|
+ // Execute jobs in a loop until we've reached the end of the queue.
|
|
|
+ // Since executing a job can trigger enqueuing of additional jobs,
|
|
|
+ // it's crucial to re-check the queue length during each iteration.
|
|
|
+ for (size_t i = 0; i < cx->jobQueue->length(); i++) {
|
|
|
+ // A previous job might have set this flag. E.g., the js shell
|
|
|
+ // sets it if the `quit` builtin function is called.
|
|
|
+- if (cx->stopDrainingJobQueue)
|
|
|
++ if (cx->stopDrainingJobQueue) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ job = cx->jobQueue->get()[i];
|
|
|
+
|
|
|
+ // It's possible that queue draining was interrupted prematurely,
|
|
|
+ // leaving the queue partly processed. In that case, slots for
|
|
|
+ // already-executed entries will contain nullptrs, which we should
|
|
|
+ // just skip.
|
|
|
+- if (!job)
|
|
|
++ if (!job) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->jobQueue->get()[i] = nullptr;
|
|
|
+
|
|
|
+ // If the next job is the last job in the job queue, allow
|
|
|
+ // skipping the standard job queuing behavior.
|
|
|
+- if (i == cx->jobQueue->length() - 1)
|
|
|
++ if (i == cx->jobQueue->length() - 1) {
|
|
|
+ JS::JobQueueIsEmpty(cx);
|
|
|
++ }
|
|
|
+
|
|
|
+ AutoRealm ar(cx, &job->as<JSFunction>());
|
|
|
+ {
|
|
|
+ if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
|
|
|
+ // Nothing we can do about uncatchable exceptions.
|
|
|
+- if (!cx->isExceptionPending())
|
|
|
++ if (!cx->isExceptionPending()) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+ RootedValue exn(cx);
|
|
|
+ if (cx->getPendingException(&exn)) {
|
|
|
+ /*
|
|
|
+ * Clear the exception, because
|
|
|
+ * PrepareScriptEnvironmentAndInvoke will assert that we don't
|
|
|
+ * have one.
|
|
|
+ */
|
|
|
+ cx->clearPendingException();
|
|
|
+@@ -1199,45 +1267,48 @@ js::RunJobs(JSContext* cx)
|
|
|
+ if (cx->stopDrainingJobQueue) {
|
|
|
+ cx->stopDrainingJobQueue = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ cx->jobQueue->clear();
|
|
|
+
|
|
|
+ // It's possible a job added a new off-thread promise task.
|
|
|
+- if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending())
|
|
|
++ if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JS::Error JSContext::reportedError;
|
|
|
+ JS::OOM JSContext::reportedOOM;
|
|
|
+
|
|
|
+ mozilla::GenericErrorResult<OOM&>
|
|
|
+ JSContext::alreadyReportedOOM()
|
|
|
+ {
|
|
|
+ #ifdef DEBUG
|
|
|
+ if (helperThread()) {
|
|
|
+ // Keep in sync with addPendingOutOfMemory.
|
|
|
+- if (ParseTask* task = helperThread()->parseTask())
|
|
|
++ if (ParseTask* task = helperThread()->parseTask()) {
|
|
|
+ MOZ_ASSERT(task->outOfMemory);
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(isThrowingOutOfMemory());
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ return mozilla::Err(reportedOOM);
|
|
|
+ }
|
|
|
+
|
|
|
+ mozilla::GenericErrorResult<JS::Error&>
|
|
|
+ JSContext::alreadyReportedError()
|
|
|
+ {
|
|
|
+ #ifdef DEBUG
|
|
|
+- if (!helperThread())
|
|
|
++ if (!helperThread()) {
|
|
|
+ MOZ_ASSERT(isExceptionPending());
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+ return mozilla::Err(reportedError);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|
|
+ : runtime_(runtime),
|
|
|
+ kind_(ContextKind::HelperThread),
|
|
|
+ helperThread_(nullptr),
|
|
|
+@@ -1315,42 +1386,45 @@ JSContext::JSContext(JSRuntime* runtime,
|
|
|
+ promiseRejectionTrackerCallbackData(nullptr)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
|
|
|
+ JS::RootingContext::get(this));
|
|
|
+
|
|
|
+ MOZ_ASSERT(!TlsContext.get());
|
|
|
+ TlsContext.set(this);
|
|
|
+
|
|
|
+- for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
|
|
|
++ for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++) {
|
|
|
+ nativeStackQuota[i] = 0;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JSContext::~JSContext()
|
|
|
+ {
|
|
|
+ // Clear the ContextKind first, so that ProtectedData checks will allow us to
|
|
|
+ // destroy this context even if the runtime is already gone.
|
|
|
+ kind_ = ContextKind::HelperThread;
|
|
|
+
|
|
|
+ /* Free the stuff hanging off of cx. */
|
|
|
+ MOZ_ASSERT(!resolvingList);
|
|
|
+
|
|
|
+- if (dtoaState)
|
|
|
++ if (dtoaState) {
|
|
|
+ DestroyDtoaState(dtoaState);
|
|
|
++ }
|
|
|
+
|
|
|
+ fx.destroyInstance();
|
|
|
+ freeOsrTempData();
|
|
|
+
|
|
|
+ #ifdef JS_SIMULATOR
|
|
|
+ js::jit::Simulator::Destroy(simulator_);
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #ifdef JS_TRACE_LOGGING
|
|
|
+- if (traceLogger)
|
|
|
++ if (traceLogger) {
|
|
|
+ DestroyTraceLogger(traceLogger);
|
|
|
++ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ js_delete(atomsZoneFreeLists_.ref());
|
|
|
+
|
|
|
+ MOZ_ASSERT(TlsContext.get() == this);
|
|
|
+ TlsContext.set(nullptr);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1366,22 +1440,24 @@ JSContext::setRuntime(JSRuntime* rt)
|
|
|
+ runtime_ = rt;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSContext::getPendingException(MutableHandleValue rval)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(throwing);
|
|
|
+ rval.set(unwrappedException());
|
|
|
+- if (zone()->isAtomsZone())
|
|
|
++ if (zone()->isAtomsZone()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ bool wasOverRecursed = overRecursed_;
|
|
|
+ clearPendingException();
|
|
|
+- if (!compartment()->wrap(this, rval))
|
|
|
++ if (!compartment()->wrap(this, rval)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ this->check(rval);
|
|
|
+ setPendingException(rval);
|
|
|
+ overRecursed_ = wasOverRecursed;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSContext::isThrowingOutOfMemory()
|
|
|
+@@ -1501,18 +1577,19 @@ JSContext::inAtomsZone() const
|
|
|
+ #endif
|
|
|
+
|
|
|
+ void
|
|
|
+ JSContext::trace(JSTracer* trc)
|
|
|
+ {
|
|
|
+ cycleDetectorVector().trace(trc);
|
|
|
+ geckoProfiler().trace(trc);
|
|
|
+
|
|
|
+- if (trc->isMarkingTracer() && realm_)
|
|
|
++ if (trc->isMarkingTracer() && realm_) {
|
|
|
+ realm_->mark();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void*
|
|
|
+ JSContext::stackLimitAddressForJitCode(JS::StackKind kind)
|
|
|
+ {
|
|
|
+ #ifdef JS_SIMULATOR
|
|
|
+ return addressOfSimulatorStackLimit();
|
|
|
+ #else
|
|
|
+@@ -1560,18 +1637,19 @@ JSContext::updateMallocCounter(size_t nb
|
|
|
+
|
|
|
+ zone()->updateMallocCounter(nbytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef JS_CRASH_DIAGNOSTICS
|
|
|
+ void
|
|
|
+ ContextChecks::check(AbstractFramePtr frame, int argIndex)
|
|
|
+ {
|
|
|
+- if (frame)
|
|
|
++ if (frame) {
|
|
|
+ check(frame.realm(), argIndex);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ void
|
|
|
+ AutoEnterOOMUnsafeRegion::crash(const char* reason)
|
|
|
+ {
|
|
|
+ char msgbuf[1024];
|
|
|
+ js::NoteIntentionalCrash();
|
|
|
+@@ -1583,18 +1661,19 @@ AutoEnterOOMUnsafeRegion::crash(const ch
|
|
|
+ AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
|
|
|
+ AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
|
|
|
+
|
|
|
+ void
|
|
|
+ AutoEnterOOMUnsafeRegion::crash(size_t size, const char* reason)
|
|
|
+ {
|
|
|
+ {
|
|
|
+ JS::AutoSuppressGCAnalysis suppress;
|
|
|
+- if (annotateOOMSizeCallback)
|
|
|
++ if (annotateOOMSizeCallback) {
|
|
|
+ annotateOOMSizeCallback(size);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ crash(reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
|
|
|
+ : cx_(TlsContext.get()),
|
|
|
+ nested_(cx_->hasAutoUnsafeCallWithABI),
|
|
|
+diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h
|
|
|
+--- a/js/src/vm/JSContext.h
|
|
|
++++ b/js/src/vm/JSContext.h
|
|
|
+@@ -178,26 +178,28 @@ struct JSContext : public JS::RootingCon
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This variation of calloc will call the large-allocation-failure callback
|
|
|
+ * on OOM and retry the allocation.
|
|
|
+ */
|
|
|
+ template <typename T>
|
|
|
+ T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
|
|
|
+ T* p = maybe_pod_calloc<T>(numElems, arena);
|
|
|
+- if (MOZ_LIKELY(!!p))
|
|
|
++ if (MOZ_LIKELY(!!p)) {
|
|
|
+ return p;
|
|
|
++ }
|
|
|
+ size_t bytes;
|
|
|
+ if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
|
|
|
+ reportAllocationOverflow();
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ p = static_cast<T*>(runtime()->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
|
|
|
+- if (!p)
|
|
|
++ if (!p) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ updateMallocCounter(bytes);
|
|
|
+ return p;
|
|
|
+ }
|
|
|
+
|
|
|
+ void updateMallocCounter(size_t nbytes);
|
|
|
+
|
|
|
+ void reportAllocationOverflow() {
|
|
|
+ js::ReportAllocationOverflow(this);
|
|
|
+@@ -642,18 +644,19 @@ struct JSContext : public JS::RootingCon
|
|
|
+ js::ThreadData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache;
|
|
|
+
|
|
|
+ private:
|
|
|
+ /* Exception state -- the exception member is a GC root by definition. */
|
|
|
+ js::ThreadData<bool> throwing; /* is there a pending exception? */
|
|
|
+ js::ThreadData<JS::PersistentRooted<JS::Value>> unwrappedException_; /* most-recently-thrown exception */
|
|
|
+
|
|
|
+ JS::Value& unwrappedException() {
|
|
|
+- if (!unwrappedException_.ref().initialized())
|
|
|
++ if (!unwrappedException_.ref().initialized()) {
|
|
|
+ unwrappedException_.ref().init(this);
|
|
|
++ }
|
|
|
+ return unwrappedException_.ref().get();
|
|
|
+ }
|
|
|
+
|
|
|
+ // True if the exception currently being thrown is by result of
|
|
|
+ // ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
|
|
|
+ js::ThreadData<bool> overRecursed_;
|
|
|
+
|
|
|
+ // True if propagating a forced return from an interrupt handler during
|
|
|
+@@ -717,18 +720,19 @@ struct JSContext : public JS::RootingCon
|
|
|
+ *
|
|
|
+ * New activations will reset this to nullptr on construction after getting
|
|
|
+ * the current value, and will restore the previous value on destruction.
|
|
|
+ */
|
|
|
+ js::ThreadData<JS::PersistentRooted<js::SavedFrame*>> asyncStackForNewActivations_;
|
|
|
+ public:
|
|
|
+
|
|
|
+ js::SavedFrame*& asyncStackForNewActivations() {
|
|
|
+- if (!asyncStackForNewActivations_.ref().initialized())
|
|
|
++ if (!asyncStackForNewActivations_.ref().initialized()) {
|
|
|
+ asyncStackForNewActivations_.ref().init(this);
|
|
|
++ }
|
|
|
+ return asyncStackForNewActivations_.ref().get();
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Value of asyncCause to be attached to asyncStackForNewActivations.
|
|
|
+ */
|
|
|
+ js::ThreadData<const char*> asyncCauseForNewActivations;
|
|
|
+
|
|
|
+diff --git a/js/src/vm/JSFunction-inl.h b/js/src/vm/JSFunction-inl.h
|
|
|
+--- a/js/src/vm/JSFunction-inl.h
|
|
|
++++ b/js/src/vm/JSFunction-inl.h
|
|
|
+@@ -26,27 +26,30 @@ GetFunctionNameBytes(JSContext* cx, JSFu
|
|
|
+ return bytes->get();
|
|
|
+ }
|
|
|
+ return js_anonymous_str;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ CanReuseFunctionForClone(JSContext* cx, HandleFunction fun)
|
|
|
+ {
|
|
|
+- if (!fun->isSingleton())
|
|
|
++ if (!fun->isSingleton()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (fun->isInterpretedLazy()) {
|
|
|
+ LazyScript* lazy = fun->lazyScript();
|
|
|
+- if (lazy->hasBeenCloned())
|
|
|
++ if (lazy->hasBeenCloned()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ lazy->setHasBeenCloned();
|
|
|
+ } else {
|
|
|
+ JSScript* script = fun->nonLazyScript();
|
|
|
+- if (script->hasBeenCloned())
|
|
|
++ if (script->hasBeenCloned()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ script->setHasBeenCloned();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSFunction*
|
|
|
+ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObject parent,
|
|
|
+ HandleObject proto = nullptr,
|
|
|
+@@ -61,37 +64,40 @@ CloneFunctionObjectIfNotSingleton(JSCont
|
|
|
+ *
|
|
|
+ * For functions inner to run once lambda, it may be possible that
|
|
|
+ * the lambda runs multiple times and we repeatedly clone it. In these
|
|
|
+ * cases, fall through to CloneFunctionObject, which will deep clone
|
|
|
+ * the function's script.
|
|
|
+ */
|
|
|
+ if (CanReuseFunctionForClone(cx, fun)) {
|
|
|
+ ObjectOpResult succeeded;
|
|
|
+- if (proto && !SetPrototype(cx, fun, proto, succeeded))
|
|
|
++ if (proto && !SetPrototype(cx, fun, proto, succeeded)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(!proto || succeeded);
|
|
|
+ fun->setEnvironment(parent);
|
|
|
+ return fun;
|
|
|
+ }
|
|
|
+
|
|
|
+ // These intermediate variables are needed to avoid link errors on some
|
|
|
+ // platforms. Sigh.
|
|
|
+ gc::AllocKind finalizeKind = gc::AllocKind::FUNCTION;
|
|
|
+ gc::AllocKind extendedFinalizeKind = gc::AllocKind::FUNCTION_EXTENDED;
|
|
|
+ gc::AllocKind kind = fun->isExtended()
|
|
|
+ ? extendedFinalizeKind
|
|
|
+ : finalizeKind;
|
|
|
+
|
|
|
+- if (CanReuseScriptForClone(cx->realm(), fun, parent))
|
|
|
++ if (CanReuseScriptForClone(cx->realm(), fun, parent)) {
|
|
|
+ return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ RootedScope enclosingScope(cx, script->enclosingScope());
|
|
|
+ return CloneFunctionAndScript(cx, fun, parent, enclosingScope, kind, proto);
|
|
|
+ }
|
|
|
+
|
|
|
+ } /* namespace js */
|
|
|
+
|
|
|
+ /* static */ inline JS::Result<JSFunction*, JS::OOM&>
|
|
|
+ JSFunction::create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
|
|
|
+@@ -105,18 +111,19 @@ JSFunction::create(JSContext* cx, js::gc
|
|
|
+ const js::Class* clasp = group->clasp();
|
|
|
+ MOZ_ASSERT(clasp->isJSFunction());
|
|
|
+
|
|
|
+ static constexpr size_t NumDynamicSlots = 0;
|
|
|
+ MOZ_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp) ==
|
|
|
+ NumDynamicSlots);
|
|
|
+
|
|
|
+ JSObject* obj = js::Allocate<JSObject>(cx, kind, NumDynamicSlots, heap, clasp);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return cx->alreadyReportedOOM();
|
|
|
++ }
|
|
|
+
|
|
|
+ NativeObject* nobj = static_cast<NativeObject*>(obj);
|
|
|
+ nobj->initGroup(group);
|
|
|
+ nobj->initShape(shape);
|
|
|
+
|
|
|
+ nobj->initSlots(nullptr);
|
|
|
+ nobj->setEmptyElements();
|
|
|
+
|
|
|
+@@ -130,18 +137,19 @@ JSFunction::create(JSContext* cx, js::gc
|
|
|
+ // value to which we could sensibly initialize this.
|
|
|
+ MOZ_MAKE_MEM_UNDEFINED(&fun->u, sizeof(u));
|
|
|
+
|
|
|
+ // Safe: we're initializing for the very first time.
|
|
|
+ fun->atom_.unsafeSet(nullptr);
|
|
|
+
|
|
|
+ if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
|
|
|
+ fun->setFlags(JSFunction::EXTENDED);
|
|
|
+- for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots)
|
|
|
++ for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots) {
|
|
|
+ extendedSlot.unsafeSet(JS::DoubleValue(+0.0));
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ fun->setFlags(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!clasp->shouldDelayMetadataBuilder(),
|
|
|
+ "Function has no extra data hanging off it, that wouldn't be "
|
|
|
+ "allocated at this point, that would require delaying the "
|
|
|
+ "building of metadata for it");
|
|
|
+diff --git a/js/src/vm/JSFunction.cpp b/js/src/vm/JSFunction.cpp
|
|
|
+--- a/js/src/vm/JSFunction.cpp
|
|
|
++++ b/js/src/vm/JSFunction.cpp
|
|
|
+@@ -75,30 +75,33 @@ fun_enumerate(JSContext* cx, HandleObjec
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(obj->is<JSFunction>());
|
|
|
+
|
|
|
+ RootedId id(cx);
|
|
|
+ bool found;
|
|
|
+
|
|
|
+ if (!obj->isBoundFunction() && !obj->as<JSFunction>().isArrow()) {
|
|
|
+ id = NameToId(cx->names().prototype);
|
|
|
+- if (!HasOwnProperty(cx, obj, id, &found))
|
|
|
++ if (!HasOwnProperty(cx, obj, id, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!obj->as<JSFunction>().hasResolvedLength()) {
|
|
|
+ id = NameToId(cx->names().length);
|
|
|
+- if (!HasOwnProperty(cx, obj, id, &found))
|
|
|
++ if (!HasOwnProperty(cx, obj, id, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!obj->as<JSFunction>().hasResolvedName()) {
|
|
|
+ id = NameToId(cx->names().name);
|
|
|
+- if (!HasOwnProperty(cx, obj, id, &found))
|
|
|
++ if (!HasOwnProperty(cx, obj, id, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ IsFunction(HandleValue v)
|
|
|
+ {
|
|
|
+@@ -106,48 +109,53 @@ IsFunction(HandleValue v)
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!fun->isBuiltin());
|
|
|
+
|
|
|
+ for (; !iter.done(); ++iter) {
|
|
|
+- if (!iter.isFunctionFrame())
|
|
|
++ if (!iter.isFunctionFrame()) {
|
|
|
+ continue;
|
|
|
+- if (iter.matchCallee(cx, fun))
|
|
|
++ }
|
|
|
++ if (iter.matchCallee(cx, fun)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ThrowTypeErrorBehavior(JSContext* cx)
|
|
|
+ {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_THROW_TYPE_ERROR);
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ IsSloppyNormalFunction(JSFunction* fun)
|
|
|
+ {
|
|
|
+ // FunctionDeclaration or FunctionExpression in sloppy mode.
|
|
|
+ if (fun->kind() == JSFunction::NormalFunction) {
|
|
|
+- if (fun->isBuiltin() || fun->isBoundFunction())
|
|
|
++ if (fun->isBuiltin() || fun->isBoundFunction()) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (fun->isGenerator() || fun->isAsync())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (fun->isGenerator() || fun->isAsync()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(fun->isInterpreted());
|
|
|
+ return !fun->strict();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Or asm.js function in sloppy mode.
|
|
|
+- if (fun->kind() == JSFunction::AsmJS)
|
|
|
++ if (fun->kind() == JSFunction::AsmJS) {
|
|
|
+ return !IsAsmJSStrictModeModuleOrFunction(fun);
|
|
|
++ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Beware: this function can be invoked on *any* function! That includes
|
|
|
+ // natives, strict mode functions, bound functions, arrow functions,
|
|
|
+ // self-hosted functions and constructors, asm.js functions, functions with
|
|
|
+ // destructuring arguments and/or a rest argument, and probably a few more I
|
|
|
+@@ -176,29 +184,31 @@ ArgumentsRestrictions(JSContext* cx, Han
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ArgumentsGetterImpl(JSContext* cx, const CallArgs& args)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(IsFunction(args.thisv()));
|
|
|
+
|
|
|
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
|
|
|
+- if (!ArgumentsRestrictions(cx, fun))
|
|
|
++ if (!ArgumentsRestrictions(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Return null if this function wasn't found on the stack.
|
|
|
+ NonBuiltinScriptFrameIter iter(cx);
|
|
|
+ if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
|
|
|
+ args.rval().setNull();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ Rooted<ArgumentsObject*> argsobj(cx, ArgumentsObject::createUnexpected(cx, iter));
|
|
|
+- if (!argsobj)
|
|
|
++ if (!argsobj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ #ifndef JS_CODEGEN_NONE
|
|
|
+ // Disabling compiling of this script in IonMonkey. IonMonkey doesn't
|
|
|
+ // guarantee |f.arguments| can be fully recovered, so we try to mitigate
|
|
|
+ // observing this behavior by detecting its use early.
|
|
|
+ JSScript* script = iter.script();
|
|
|
+ jit::ForbidCompilation(cx, script);
|
|
|
+ #endif
|
|
|
+@@ -215,18 +225,19 @@ ArgumentsGetter(JSContext* cx, unsigned
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ArgumentsSetterImpl(JSContext* cx, const CallArgs& args)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(IsFunction(args.thisv()));
|
|
|
+
|
|
|
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
|
|
|
+- if (!ArgumentsRestrictions(cx, fun))
|
|
|
++ if (!ArgumentsRestrictions(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // If the function passes the gauntlet, return |undefined|.
|
|
|
+ args.rval().setUndefined();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ ArgumentsSetter(JSContext* cx, unsigned argc, Value* vp)
|
|
|
+@@ -268,56 +279,61 @@ CallerGetterImpl(JSContext* cx, const Ca
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(IsFunction(args.thisv()));
|
|
|
+
|
|
|
+ // Beware! This function can be invoked on *any* function! It can't
|
|
|
+ // assume it'll never be invoked on natives, strict mode functions, bound
|
|
|
+ // functions, or anything else that ordinarily has immutable .caller
|
|
|
+ // defined with [[ThrowTypeError]].
|
|
|
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
|
|
|
+- if (!CallerRestrictions(cx, fun))
|
|
|
++ if (!CallerRestrictions(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Also return null if this function wasn't found on the stack.
|
|
|
+ NonBuiltinScriptFrameIter iter(cx);
|
|
|
+ if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
|
|
|
+ args.rval().setNull();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ ++iter;
|
|
|
+- while (!iter.done() && iter.isEvalFrame())
|
|
|
++ while (!iter.done() && iter.isEvalFrame()) {
|
|
|
+ ++iter;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (iter.done() || !iter.isFunctionFrame()) {
|
|
|
+ args.rval().setNull();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObject caller(cx, iter.callee(cx));
|
|
|
+- if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync())
|
|
|
++ if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync()) {
|
|
|
+ caller = GetWrappedAsyncFunction(&caller->as<JSFunction>());
|
|
|
+- if (!cx->compartment()->wrap(cx, &caller))
|
|
|
++ }
|
|
|
++ if (!cx->compartment()->wrap(cx, &caller)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Censor the caller if we don't have full access to it. If we do, but the
|
|
|
+ // caller is a function with strict mode code, throw a TypeError per ES5.
|
|
|
+ // If we pass these checks, we can return the computed caller.
|
|
|
+ {
|
|
|
+ JSObject* callerObj = CheckedUnwrap(caller);
|
|
|
+ if (!callerObj) {
|
|
|
+ args.rval().setNull();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFunction* callerFun = &callerObj->as<JSFunction>();
|
|
|
+- if (IsWrappedAsyncFunction(callerFun))
|
|
|
++ if (IsWrappedAsyncFunction(callerFun)) {
|
|
|
+ callerFun = GetUnwrappedAsyncFunction(callerFun);
|
|
|
+- else if (IsWrappedAsyncGenerator(callerFun))
|
|
|
++ } else if (IsWrappedAsyncGenerator(callerFun)) {
|
|
|
+ callerFun = GetUnwrappedAsyncGenerator(callerFun);
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
|
|
|
+
|
|
|
+ if (callerFun->strict()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CALLER_IS_STRICT);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -337,53 +353,58 @@ CallerSetterImpl(JSContext* cx, const Ca
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(IsFunction(args.thisv()));
|
|
|
+
|
|
|
+ // Beware! This function can be invoked on *any* function! It can't
|
|
|
+ // assume it'll never be invoked on natives, strict mode functions, bound
|
|
|
+ // functions, or anything else that ordinarily has immutable .caller
|
|
|
+ // defined with [[ThrowTypeError]].
|
|
|
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
|
|
|
+- if (!CallerRestrictions(cx, fun))
|
|
|
++ if (!CallerRestrictions(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Return |undefined| unless an error must be thrown.
|
|
|
+ args.rval().setUndefined();
|
|
|
+
|
|
|
+ // We can almost just return |undefined| here -- but if the caller function
|
|
|
+ // was strict mode code, we still have to throw a TypeError. This requires
|
|
|
+ // computing the caller, checking that no security boundaries are crossed,
|
|
|
+ // and throwing a TypeError if the resulting caller is strict.
|
|
|
+
|
|
|
+ NonBuiltinScriptFrameIter iter(cx);
|
|
|
+- if (!AdvanceToActiveCallLinear(cx, iter, fun))
|
|
|
++ if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ ++iter;
|
|
|
+- while (!iter.done() && iter.isEvalFrame())
|
|
|
++ while (!iter.done() && iter.isEvalFrame()) {
|
|
|
+ ++iter;
|
|
|
+-
|
|
|
+- if (iter.done() || !iter.isFunctionFrame())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (iter.done() || !iter.isFunctionFrame()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedObject caller(cx, iter.callee(cx));
|
|
|
+ // |caller| is only used for security access-checking and for its
|
|
|
+ // strictness. An unwrapped async function has its wrapped async
|
|
|
+ // function's security access and strictness, so don't bother calling
|
|
|
+ // |GetUnwrappedAsyncFunction|.
|
|
|
+ if (!cx->compartment()->wrap(cx, &caller)) {
|
|
|
+ cx->clearPendingException();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we don't have full access to the caller, or the caller is not strict,
|
|
|
+ // return undefined. Otherwise throw a TypeError.
|
|
|
+ JSObject* callerObj = CheckedUnwrap(caller);
|
|
|
+- if (!callerObj)
|
|
|
++ if (!callerObj) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSFunction* callerFun = &callerObj->as<JSFunction>();
|
|
|
+ MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
|
|
|
+
|
|
|
+ if (callerFun->strict()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CALLER_IS_STRICT);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+@@ -420,38 +441,42 @@ ResolveInterpretedFunctionPrototype(JSCo
|
|
|
+
|
|
|
+ // Make the prototype object an instance of Object with the same parent as
|
|
|
+ // the function object itself, unless the function is an ES6 generator. In
|
|
|
+ // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
|
|
|
+ // the GeneratorObjectPrototype singleton.
|
|
|
+ bool isGenerator = fun->isGenerator();
|
|
|
+ Rooted<GlobalObject*> global(cx, &fun->global());
|
|
|
+ RootedObject objProto(cx);
|
|
|
+- if (isAsyncGenerator)
|
|
|
++ if (isAsyncGenerator) {
|
|
|
+ objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
|
|
|
+- else if (isGenerator)
|
|
|
++ } else if (isGenerator) {
|
|
|
+ objProto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
|
|
|
+- if (!objProto)
|
|
|
++ }
|
|
|
++ if (!objProto) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
|
|
|
+ SingletonObject));
|
|
|
+- if (!proto)
|
|
|
++ if (!proto) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Per ES5 13.2 the prototype's .constructor property is configurable,
|
|
|
+ // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft,
|
|
|
+ // section 15.19.3, the .prototype of a generator function does not link
|
|
|
+ // back with a .constructor.
|
|
|
+ if (!isGenerator && !isAsyncGenerator) {
|
|
|
+ RootedValue objVal(cx, ObjectValue(*fun));
|
|
|
+- if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0))
|
|
|
++ if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Per ES5 15.3.5.2 a user-defined function's .prototype property is
|
|
|
+ // initially non-configurable, non-enumerable, and writable.
|
|
|
+ RootedValue protoVal(cx, ObjectValue(*proto));
|
|
|
+ return DefineDataProperty(cx, fun, id, protoVal, JSPROP_PERMANENT | JSPROP_RESOLVING);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -471,46 +496,51 @@ JSFunction::needsPrototypeProperty()
|
|
|
+ * Generators are not constructors, but they have a .prototype property anyway,
|
|
|
+ * according to errata to ES6. See bug 1191486.
|
|
|
+ *
|
|
|
+ * Thus all of the following don't get a .prototype property:
|
|
|
+ * - Methods (that are not class-constructors or generators)
|
|
|
+ * - Arrow functions
|
|
|
+ * - Function.prototype
|
|
|
+ */
|
|
|
+- if (isBuiltin())
|
|
|
++ if (isBuiltin()) {
|
|
|
+ return IsWrappedAsyncGenerator(this);
|
|
|
++ }
|
|
|
+
|
|
|
+ return isConstructor() || isGenerator() || isAsync();
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ fun_mayResolve(const JSAtomState& names, jsid id, JSObject*)
|
|
|
+ {
|
|
|
+- if (!JSID_IS_ATOM(id))
|
|
|
++ if (!JSID_IS_ATOM(id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom* atom = JSID_TO_ATOM(id);
|
|
|
+ return atom == names.prototype || atom == names.length || atom == names.name;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|
|
+ {
|
|
|
+- if (!JSID_IS_ATOM(id))
|
|
|
++ if (!JSID_IS_ATOM(id)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedFunction fun(cx, &obj->as<JSFunction>());
|
|
|
+
|
|
|
+ if (JSID_IS_ATOM(id, cx->names().prototype)) {
|
|
|
+- if (!fun->needsPrototypeProperty())
|
|
|
++ if (!fun->needsPrototypeProperty()) {
|
|
|
+ return true;
|
|
|
+-
|
|
|
+- if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *resolvedp = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool isLength = JSID_IS_ATOM(id, cx->names().length);
|
|
|
+ if (isLength || JSID_IS_ATOM(id, cx->names().name)) {
|
|
|
+ MOZ_ASSERT(!IsInternalFunctionObject(*obj));
|
|
|
+@@ -527,43 +557,50 @@ fun_resolve(JSContext* cx, HandleObject
|
|
|
+ // Afterwards, asking for f.length or f.name again will cause this
|
|
|
+ // resolve hook to run again. Defining the property again the second
|
|
|
+ // time through would be a bug.
|
|
|
+ // assertEq(f.length, 0); // gets Function.prototype.length!
|
|
|
+ // assertEq(f.name, ""); // gets Function.prototype.name!
|
|
|
+ // We use the RESOLVED_LENGTH and RESOLVED_NAME flags as a hack to prevent this
|
|
|
+ // bug.
|
|
|
+ if (isLength) {
|
|
|
+- if (fun->hasResolvedLength())
|
|
|
++ if (fun->hasResolvedLength()) {
|
|
|
+ return true;
|
|
|
+-
|
|
|
+- if (!JSFunction::getUnresolvedLength(cx, fun, &v))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!JSFunction::getUnresolvedLength(cx, fun, &v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (fun->hasResolvedName())
|
|
|
++ if (fun->hasResolvedName()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedString name(cx);
|
|
|
+- if (!JSFunction::getUnresolvedName(cx, fun, &name))
|
|
|
++ if (!JSFunction::getUnresolvedName(cx, fun, &name)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Don't define an own .name property for unnamed functions.
|
|
|
+- if (!name)
|
|
|
++ if (!name) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ v.setString(name);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!NativeDefineDataProperty(cx, fun, id, v, JSPROP_READONLY | JSPROP_RESOLVING))
|
|
|
++ if (!NativeDefineDataProperty(cx, fun, id, v, JSPROP_READONLY | JSPROP_RESOLVING)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (isLength)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (isLength) {
|
|
|
+ fun->setResolvedLength();
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun->setResolvedName();
|
|
|
++ }
|
|
|
+
|
|
|
+ *resolvedp = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -586,36 +623,40 @@ js::XDRInterpretedFunction(XDRState<mode
|
|
|
+
|
|
|
+ JSContext* cx = xdr->cx();
|
|
|
+ RootedFunction fun(cx);
|
|
|
+ RootedScript script(cx);
|
|
|
+ Rooted<LazyScript*> lazy(cx);
|
|
|
+
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ fun = objp;
|
|
|
+- if (!fun->isInterpreted())
|
|
|
++ if (!fun->isInterpreted()) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
|
|
|
+-
|
|
|
+- if (fun->explicitName() || fun->hasInferredName() || fun->hasGuessedAtom())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (fun->explicitName() || fun->hasInferredName() || fun->hasGuessedAtom()) {
|
|
|
+ firstword |= HasAtom;
|
|
|
+-
|
|
|
+- if (fun->isGenerator() || fun->isAsync())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (fun->isGenerator() || fun->isAsync()) {
|
|
|
+ firstword |= HasGeneratorProto;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (fun->isInterpretedLazy()) {
|
|
|
+ // Encode a lazy script.
|
|
|
+ firstword |= IsLazy;
|
|
|
+ lazy = fun->lazyScript();
|
|
|
+ } else {
|
|
|
+ // Encode the script.
|
|
|
+ script = fun->nonLazyScript();
|
|
|
+ }
|
|
|
+
|
|
|
+- if (fun->isSingleton())
|
|
|
++ if (fun->isSingleton()) {
|
|
|
+ firstword |= HasSingletonType;
|
|
|
++ }
|
|
|
+
|
|
|
+ atom = fun->displayAtom();
|
|
|
+ flagsword = (fun->nargs() << 16) |
|
|
|
+ (fun->flags() & ~JSFunction::NO_XDR_FLAGS);
|
|
|
+
|
|
|
+ // The environment of any function which is not reused will always be
|
|
|
+ // null, it is later defined when a function is cloned or reused to
|
|
|
+ // mirror the scope chain.
|
|
|
+@@ -626,58 +667,64 @@ js::XDRInterpretedFunction(XDRState<mode
|
|
|
+
|
|
|
+ // Everything added below can substituted by the non-lazy-script version of
|
|
|
+ // this function later.
|
|
|
+ MOZ_TRY(xdr->codeAlign(sizeof(js::XDRAlignment)));
|
|
|
+ js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeUint32(&firstword));
|
|
|
+
|
|
|
+- if (firstword & HasAtom)
|
|
|
++ if (firstword & HasAtom) {
|
|
|
+ MOZ_TRY(XDRAtom(xdr, &atom));
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&flagsword));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ RootedObject proto(cx);
|
|
|
+ if (firstword & HasGeneratorProto) {
|
|
|
+ proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
|
|
|
+- if (!proto)
|
|
|
++ if (!proto) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
|
|
|
+- if (uint16_t(flagsword) & JSFunction::EXTENDED)
|
|
|
++ if (uint16_t(flagsword) & JSFunction::EXTENDED) {
|
|
|
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
|
|
++ }
|
|
|
+ fun = NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
|
|
|
+ /* enclosingDynamicScope = */ nullptr, nullptr, proto,
|
|
|
+ allocKind, TenuredObject);
|
|
|
+- if (!fun)
|
|
|
++ if (!fun) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ script = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (firstword & IsLazy)
|
|
|
++ if (firstword & IsLazy) {
|
|
|
+ MOZ_TRY(XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy));
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_TRY(XDRScript(xdr, enclosingScope, sourceObject, fun, &script));
|
|
|
++ }
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ fun->setArgCount(flagsword >> 16);
|
|
|
+ fun->setFlags(uint16_t(flagsword));
|
|
|
+ fun->initAtom(atom);
|
|
|
+ if (firstword & IsLazy) {
|
|
|
+ MOZ_ASSERT(fun->lazyScript() == lazy);
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(fun->nonLazyScript() == script);
|
|
|
+ MOZ_ASSERT(fun->nargs() == script->numArgs());
|
|
|
+ }
|
|
|
+
|
|
|
+ bool singleton = firstword & HasSingletonType;
|
|
|
+- if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
|
|
|
++ if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ objp.set(fun);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify marker at end of function to detect buffer trunction.
|
|
|
+ MOZ_TRY(xdr->codeMarker(0x9E35CA1F));
|
|
|
+
|
|
|
+ // Required by AutoXDRTree to copy & paste snipet of sub-trees while keeping
|
|
|
+ // the alignment.
|
|
|
+@@ -714,18 +761,19 @@ js::fun_symbolHasInstance(JSContext* cx,
|
|
|
+ args.rval().setBoolean(false);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObject obj(cx, &func.toObject());
|
|
|
+
|
|
|
+ /* Step 2. */
|
|
|
+ bool result;
|
|
|
+- if (!OrdinaryHasInstance(cx, obj, args[0], &result))
|
|
|
++ if (!OrdinaryHasInstance(cx, obj, args[0], &result)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ args.rval().setBoolean(result);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance
|
|
|
+ */
|
|
|
+@@ -753,35 +801,37 @@ JS::OrdinaryHasInstance(JSContext* cx, H
|
|
|
+ /* Step 3. */
|
|
|
+ if (!v.isObject()) {
|
|
|
+ *bp = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Step 4. */
|
|
|
+ RootedValue pval(cx);
|
|
|
+- if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval))
|
|
|
++ if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Step 5. */
|
|
|
+ if (pval.isPrimitive()) {
|
|
|
+ /*
|
|
|
+ * Throw a runtime error if instanceof is called on a function that
|
|
|
+ * has a non-object as its .prototype value.
|
|
|
+ */
|
|
|
+ RootedValue val(cx, ObjectValue(*obj));
|
|
|
+ ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, val, nullptr);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Step 6. */
|
|
|
+ RootedObject pobj(cx, &pval.toObject());
|
|
|
+ bool isPrototype;
|
|
|
+- if (!IsPrototypeOf(cx, pobj, &v.toObject(), &isPrototype))
|
|
|
++ if (!IsPrototypeOf(cx, pobj, &v.toObject(), &isPrototype)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ *bp = isPrototype;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSFunction::trace(JSTracer* trc)
|
|
|
+ {
|
|
|
+ if (isExtended()) {
|
|
|
+@@ -790,23 +840,25 @@ JSFunction::trace(JSTracer* trc)
|
|
|
+ }
|
|
|
+
|
|
|
+ TraceNullableEdge(trc, &atom_, "atom");
|
|
|
+
|
|
|
+ if (isInterpreted()) {
|
|
|
+ // Functions can be be marked as interpreted despite having no script
|
|
|
+ // yet at some points when parsing, and can be lazy with no lazy script
|
|
|
+ // for self-hosted code.
|
|
|
+- if (hasScript() && !hasUncompletedScript())
|
|
|
++ if (hasScript() && !hasUncompletedScript()) {
|
|
|
+ TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
|
|
|
+- else if (isInterpretedLazy() && u.scripted.s.lazy_)
|
|
|
++ } else if (isInterpretedLazy() && u.scripted.s.lazy_) {
|
|
|
+ TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
|
|
|
+-
|
|
|
+- if (u.scripted.env_)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (u.scripted.env_) {
|
|
|
+ TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ fun_trace(JSTracer* trc, JSObject* obj)
|
|
|
+ {
|
|
|
+ obj->as<JSFunction>().trace(trc);
|
|
|
+ }
|
|
|
+@@ -816,18 +868,19 @@ CreateFunctionConstructor(JSContext* cx,
|
|
|
+ {
|
|
|
+ Rooted<GlobalObject*> global(cx, cx->global());
|
|
|
+ RootedObject functionProto(cx, &global->getPrototype(JSProto_Function).toObject());
|
|
|
+
|
|
|
+ RootedObject functionCtor(cx,
|
|
|
+ NewFunctionWithProto(cx, Function, 1, JSFunction::NATIVE_CTOR,
|
|
|
+ nullptr, HandlePropertyName(cx->names().Function),
|
|
|
+ functionProto, gc::AllocKind::FUNCTION, SingletonObject));
|
|
|
+- if (!functionCtor)
|
|
|
++ if (!functionCtor) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return functionCtor;
|
|
|
+ }
|
|
|
+
|
|
|
+ static JSObject*
|
|
|
+ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
|
|
|
+ {
|
|
|
+ Rooted<GlobalObject*> self(cx, cx->global());
|
|
|
+@@ -836,68 +889,77 @@ CreateFunctionPrototype(JSContext* cx, J
|
|
|
+ /*
|
|
|
+ * Bizarrely, |Function.prototype| must be an interpreted function, so
|
|
|
+ * give it the guts to be one.
|
|
|
+ */
|
|
|
+ RootedObject enclosingEnv(cx, &self->lexicalEnvironment());
|
|
|
+ RootedFunction functionProto(cx, NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
|
|
|
+ enclosingEnv, nullptr, objectProto,
|
|
|
+ gc::AllocKind::FUNCTION, SingletonObject));
|
|
|
+- if (!functionProto)
|
|
|
++ if (!functionProto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ const char* rawSource = "function () {\n}";
|
|
|
+ size_t sourceLen = strlen(rawSource);
|
|
|
+ size_t begin = 9;
|
|
|
+ MOZ_ASSERT(rawSource[begin] == '(');
|
|
|
+ UniqueTwoByteChars source(InflateString(cx, rawSource, sourceLen));
|
|
|
+- if (!source)
|
|
|
++ if (!source) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ ScriptSource* ss = cx->new_<ScriptSource>();
|
|
|
+- if (!ss)
|
|
|
++ if (!ss) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ ScriptSourceHolder ssHolder(ss);
|
|
|
+- if (!ss->setSource(cx, std::move(source), sourceLen))
|
|
|
++ if (!ss->setSource(cx, std::move(source), sourceLen)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ CompileOptions options(cx);
|
|
|
+ options.setIntroductionType("Function.prototype")
|
|
|
+ .setNoScriptRval(true);
|
|
|
+- if (!ss->initFromOptions(cx, options))
|
|
|
++ if (!ss->initFromOptions(cx, options)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ RootedScriptSourceObject sourceObject(cx, ScriptSourceObject::create(cx, ss));
|
|
|
+- if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
|
|
|
++ if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedScript script(cx, JSScript::Create(cx,
|
|
|
+ options,
|
|
|
+ sourceObject,
|
|
|
+ begin,
|
|
|
+ ss->length(),
|
|
|
+ 0,
|
|
|
+ ss->length()));
|
|
|
+- if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
|
|
|
++ if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ functionProto->initScript(script);
|
|
|
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, functionProto);
|
|
|
+- if (!protoGroup)
|
|
|
++ if (!protoGroup) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ protoGroup->setInterpretedFunction(functionProto);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The default 'new' group of Function.prototype is required by type
|
|
|
+ * inference to have unknown properties, to simplify handling of e.g.
|
|
|
+ * NewFunctionClone.
|
|
|
+ */
|
|
|
+ ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
|
|
+- if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto))
|
|
|
++ if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return functionProto;
|
|
|
+ }
|
|
|
+
|
|
|
+ static const ClassOps JSFunctionClassOps = {
|
|
|
+ nullptr, /* addProperty */
|
|
|
+ nullptr, /* delProperty */
|
|
|
+ fun_enumerate,
|
|
|
+@@ -928,54 +990,60 @@ const Class JSFunction::class_ = {
|
|
|
+ };
|
|
|
+
|
|
|
+ const Class* const js::FunctionClassPtr = &JSFunction::class_;
|
|
|
+
|
|
|
+ JSString*
|
|
|
+ js::FunctionToStringCache::lookup(JSScript* script) const
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < NumEntries; i++) {
|
|
|
+- if (entries_[i].script == script)
|
|
|
++ if (entries_[i].script == script) {
|
|
|
+ return entries_[i].string;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::FunctionToStringCache::put(JSScript* script, JSString* string)
|
|
|
+ {
|
|
|
+- for (size_t i = NumEntries - 1; i > 0; i--)
|
|
|
++ for (size_t i = NumEntries - 1; i > 0; i--) {
|
|
|
+ entries_[i] = entries_[i - 1];
|
|
|
++ }
|
|
|
+
|
|
|
+ entries_[0].set(script, string);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSString*
|
|
|
+ js::FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource)
|
|
|
+ {
|
|
|
+- if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
|
|
|
++ if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun)) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- if (IsAsmJSModule(fun))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (IsAsmJSModule(fun)) {
|
|
|
+ return AsmJSModuleToString(cx, fun, isToSource);
|
|
|
+- if (IsAsmJSFunction(fun))
|
|
|
++ }
|
|
|
++ if (IsAsmJSFunction(fun)) {
|
|
|
+ return AsmJSFunctionToString(cx, fun);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (IsWrappedAsyncFunction(fun)) {
|
|
|
+ RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
|
|
|
+ return FunctionToString(cx, unwrapped, isToSource);
|
|
|
+ }
|
|
|
+ if (IsWrappedAsyncGenerator(fun)) {
|
|
|
+ RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
|
|
|
+ return FunctionToString(cx, unwrapped, isToSource);
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedScript script(cx);
|
|
|
+- if (fun->hasScript())
|
|
|
++ if (fun->hasScript()) {
|
|
|
+ script = fun->nonLazyScript();
|
|
|
++ }
|
|
|
+
|
|
|
+ // Default class constructors are self-hosted, but have their source
|
|
|
+ // objects overridden to refer to the span of the class statement or
|
|
|
+ // expression. Non-default class constructors are never self-hosted. So,
|
|
|
+ // all class constructors always have source.
|
|
|
+ bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
|
|
|
+ !fun->isSelfHostedBuiltin());
|
|
|
+
|
|
|
+@@ -987,66 +1055,76 @@ js::FunctionToString(JSContext* cx, Hand
|
|
|
+ !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
|
|
|
+ {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fast path for the common case, to avoid StringBuffer overhead.
|
|
|
+ if (!addParentheses && haveSource) {
|
|
|
+ FunctionToStringCache& cache = cx->zone()->functionToStringCache();
|
|
|
+- if (JSString* str = cache.lookup(script))
|
|
|
++ if (JSString* str = cache.lookup(script)) {
|
|
|
+ return str;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t start = script->toStringStart(), end = script->toStringEnd();
|
|
|
+ JSString* str = (end - start <= ScriptSource::SourceDeflateLimit)
|
|
|
+ ? script->scriptSource()->substring(cx, start, end)
|
|
|
+ : script->scriptSource()->substringDontDeflate(cx, start, end);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ cache.put(script, str);
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuffer out(cx);
|
|
|
+ if (addParentheses) {
|
|
|
+- if (!out.append('('))
|
|
|
++ if (!out.append('(')) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (haveSource) {
|
|
|
+- if (!script->appendSourceDataForToString(cx, out))
|
|
|
++ if (!script->appendSourceDataForToString(cx, out)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ if (fun->isAsync()) {
|
|
|
+- if (!out.append("async "))
|
|
|
++ if (!out.append("async ")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!fun->isArrow()) {
|
|
|
+- if (!out.append("function"))
|
|
|
++ if (!out.append("function")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (fun->isGenerator()) {
|
|
|
+- if (!out.append('*'))
|
|
|
++ if (!out.append('*')) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fun->explicitName()) {
|
|
|
+- if (!out.append(' '))
|
|
|
++ if (!out.append(' ')) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (fun->isBoundFunction()) {
|
|
|
+ JSLinearString* boundName = JSFunction::getBoundFunctionName(cx, fun);
|
|
|
+- if (!boundName || !out.append(boundName))
|
|
|
++ if (!boundName || !out.append(boundName)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!out.append(fun->explicitName()))
|
|
|
++ if (!out.append(fun->explicitName())) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fun->isInterpreted() &&
|
|
|
+ (!fun->isSelfHostedBuiltin() ||
|
|
|
+ fun->infallibleIsDefaultClassConstructor(cx)))
|
|
|
+ {
|
|
|
+ // Default class constructors should always haveSource except;
|
|
|
+@@ -1055,38 +1133,42 @@ js::FunctionToString(JSContext* cx, Hand
|
|
|
+ //
|
|
|
+ // 2. The source is marked as "lazy", i.e., retrieved on demand, and
|
|
|
+ // the embedding has not provided a hook to retrieve sources.
|
|
|
+ MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
|
|
|
+ !cx->runtime()->sourceHook.ref() ||
|
|
|
+ !script->scriptSource()->sourceRetrievable() ||
|
|
|
+ fun->realm()->behaviors().discardSource());
|
|
|
+
|
|
|
+- if (!out.append("() {\n [sourceless code]\n}"))
|
|
|
++ if (!out.append("() {\n [sourceless code]\n}")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!out.append("() {\n [native code]\n}"))
|
|
|
++ if (!out.append("() {\n [native code]\n}")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addParentheses) {
|
|
|
+- if (!out.append(')'))
|
|
|
++ if (!out.append(')')) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return out.finishString();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSString*
|
|
|
+ fun_toStringHelper(JSContext* cx, HandleObject obj, bool isToSource)
|
|
|
+ {
|
|
|
+ if (!obj->is<JSFunction>()) {
|
|
|
+- if (JSFunToStringOp op = obj->getOpsFunToString())
|
|
|
++ if (JSFunToStringOp op = obj->getOpsFunToString()) {
|
|
|
+ return op(cx, obj, isToSource);
|
|
|
++ }
|
|
|
+
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
|
|
+ JSMSG_INCOMPATIBLE_PROTO,
|
|
|
+ js_Function_str, js_toString_str, "object");
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedFunction fun(cx, &obj->as<JSFunction>());
|
|
|
+@@ -1094,59 +1176,65 @@ fun_toStringHelper(JSContext* cx, Handle
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols)
|
|
|
+ {
|
|
|
+ jsid id = SYMBOL_TO_JSID(symbols.hasInstance);
|
|
|
+ Shape* shape = fun->lookupPure(id);
|
|
|
+ if (shape) {
|
|
|
+- if (!shape->isDataProperty())
|
|
|
++ if (!shape->isDataProperty()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ const Value hasInstance = fun->as<NativeObject>().getSlot(shape->slot());
|
|
|
+ return IsNativeFunction(hasInstance, js::fun_symbolHasInstance);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::fun_toString(JSContext* cx, unsigned argc, Value* vp)
|
|
|
+ {
|
|
|
+ CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
+ MOZ_ASSERT(IsFunctionObject(args.calleev()));
|
|
|
+
|
|
|
+ RootedObject obj(cx, ToObject(cx, args.thisv()));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSString* str = fun_toStringHelper(cx, obj, /* isToSource = */ false);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ args.rval().setString(str);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ fun_toSource(JSContext* cx, unsigned argc, Value* vp)
|
|
|
+ {
|
|
|
+ CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
+ MOZ_ASSERT(IsFunctionObject(args.calleev()));
|
|
|
+
|
|
|
+ RootedObject obj(cx, ToObject(cx, args.thisv()));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedString str(cx);
|
|
|
+- if (obj->isCallable())
|
|
|
++ if (obj->isCallable()) {
|
|
|
+ str = fun_toStringHelper(cx, obj, /* isToSource = */ true);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ str = ObjectToSource(cx, obj);
|
|
|
+- if (!str)
|
|
|
++ }
|
|
|
++ if (!str) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ args.rval().setString(str);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::fun_call(JSContext* cx, unsigned argc, Value* vp)
|
|
|
+ {
|
|
|
+@@ -1165,25 +1253,28 @@ js::fun_call(JSContext* cx, unsigned arg
|
|
|
+ // |Function.prototype.call| and would conclude, "Function.prototype.call
|
|
|
+ // is not a function". Grotesque.)
|
|
|
+ if (!IsCallable(func)) {
|
|
|
+ ReportIncompatibleMethod(cx, args, &JSFunction::class_);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t argCount = args.length();
|
|
|
+- if (argCount > 0)
|
|
|
++ if (argCount > 0) {
|
|
|
+ argCount--; // strip off provided |this|
|
|
|
++ }
|
|
|
+
|
|
|
+ InvokeArgs iargs(cx);
|
|
|
+- if (!iargs.init(cx, argCount))
|
|
|
++ if (!iargs.init(cx, argCount)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- for (size_t i = 0; i < argCount; i++)
|
|
|
++ }
|
|
|
++
|
|
|
++ for (size_t i = 0; i < argCount; i++) {
|
|
|
+ iargs[i].set(args[i + 1]);
|
|
|
++ }
|
|
|
+
|
|
|
+ return Call(cx, func, args.get(0), iargs, args.rval());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ES5 15.3.4.3
|
|
|
+ bool
|
|
|
+ js::fun_apply(JSContext* cx, unsigned argc, Value* vp)
|
|
|
+ {
|
|
|
+@@ -1196,69 +1287,75 @@ js::fun_apply(JSContext* cx, unsigned ar
|
|
|
+ // have side effects or throw an exception.
|
|
|
+ HandleValue fval = args.thisv();
|
|
|
+ if (!IsCallable(fval)) {
|
|
|
+ ReportIncompatibleMethod(cx, args, &JSFunction::class_);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 2.
|
|
|
+- if (args.length() < 2 || args[1].isNullOrUndefined())
|
|
|
++ if (args.length() < 2 || args[1].isNullOrUndefined()) {
|
|
|
+ return fun_call(cx, (args.length() > 0) ? 1 : 0, vp);
|
|
|
++ }
|
|
|
+
|
|
|
+ InvokeArgs args2(cx);
|
|
|
+
|
|
|
+ // A JS_OPTIMIZED_ARGUMENTS magic value means that 'arguments' flows into
|
|
|
+ // this apply call from a scripted caller and, as an optimization, we've
|
|
|
+ // avoided creating it since apply can simply pull the argument values from
|
|
|
+ // the calling frame (which we must do now).
|
|
|
+ if (args[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
|
|
|
+ // Step 3-6.
|
|
|
+ ScriptFrameIter iter(cx);
|
|
|
+ MOZ_ASSERT(iter.numActualArgs() <= ARGS_LENGTH_MAX);
|
|
|
+- if (!args2.init(cx, iter.numActualArgs()))
|
|
|
++ if (!args2.init(cx, iter.numActualArgs())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 7-8.
|
|
|
+ iter.unaliasedForEachActual(cx, CopyTo(args2.array()));
|
|
|
+ } else {
|
|
|
+ // Step 3.
|
|
|
+ if (!args[1].isObject()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
|
|
+ JSMSG_BAD_APPLY_ARGS, js_apply_str);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
|
|
|
+ // original version of ES5).
|
|
|
+ RootedObject aobj(cx, &args[1].toObject());
|
|
|
+ uint32_t length;
|
|
|
+- if (!GetLengthProperty(cx, aobj, &length))
|
|
|
++ if (!GetLengthProperty(cx, aobj, &length)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 6.
|
|
|
+- if (!args2.init(cx, length))
|
|
|
++ if (!args2.init(cx, length)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(length <= ARGS_LENGTH_MAX);
|
|
|
+
|
|
|
+ // Steps 7-8.
|
|
|
+- if (!GetElements(cx, aobj, length, args2.array()))
|
|
|
++ if (!GetElements(cx, aobj, length, args2.array())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 9.
|
|
|
+ return Call(cx, fval, args[0], args2, args.rval());
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
|
|
|
+ {
|
|
|
+- if (!isSelfHostedBuiltin())
|
|
|
++ if (!isSelfHostedBuiltin()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ bool isDefault = false;
|
|
|
+ if (isInterpretedLazy()) {
|
|
|
+ JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom();
|
|
|
+ isDefault = name == cx->names().DefaultDerivedClassConstructor ||
|
|
|
+ name == cx->names().DefaultBaseClassConstructor;
|
|
|
+ } else {
|
|
|
+ isDefault = nonLazyScript()->isDefaultClassConstructor();
|
|
|
+@@ -1292,18 +1389,19 @@ JSFunction::isDerivedClassConstructor()
|
|
|
+ MOZ_ASSERT_IF(derived, isClassConstructor());
|
|
|
+ return derived;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!fun->isBoundFunction());
|
|
|
+- if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun))
|
|
|
++ if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *length = fun->isNative() ? fun->nargs() : fun->nonLazyScript()->funLength();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandleValue v)
|
|
|
+ {
|
|
|
+@@ -1314,46 +1412,50 @@ JSFunction::getUnresolvedLength(JSContex
|
|
|
+ // they're handled differently from other functions.
|
|
|
+ if (fun->isBoundFunction()) {
|
|
|
+ MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
|
|
|
+ v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t length;
|
|
|
+- if (!JSFunction::getLength(cx, fun, &length))
|
|
|
++ if (!JSFunction::getLength(cx, fun, &length)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ v.setInt32(length);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom*
|
|
|
+ JSFunction::infallibleGetUnresolvedName(JSContext* cx)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!IsInternalFunctionObject(*this));
|
|
|
+ MOZ_ASSERT(!hasResolvedName());
|
|
|
+
|
|
|
+- if (JSAtom* name = explicitOrInferredName())
|
|
|
++ if (JSAtom* name = explicitOrInferredName()) {
|
|
|
+ return name;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Unnamed class expressions should not get a .name property at all.
|
|
|
+- if (isClassConstructor())
|
|
|
++ if (isClassConstructor()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return cx->names().empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSFunction::getUnresolvedName(JSContext* cx, HandleFunction fun, MutableHandleString v)
|
|
|
+ {
|
|
|
+ if (fun->isBoundFunction()) {
|
|
|
+ JSLinearString* name = JSFunction::getBoundFunctionName(cx, fun);
|
|
|
+- if (!name)
|
|
|
++ if (!name) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ v.set(name);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ v.set(fun->infallibleGetUnresolvedName(cx));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -1363,55 +1465,61 @@ JSFunction::getBoundFunctionName(JSConte
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(fun->isBoundFunction());
|
|
|
+ JSAtom* name = fun->explicitName();
|
|
|
+
|
|
|
+ // Bound functions are never unnamed.
|
|
|
+ MOZ_ASSERT(name);
|
|
|
+
|
|
|
+ // If the bound function prefix is present, return the name as is.
|
|
|
+- if (fun->hasBoundFunctionNamePrefix())
|
|
|
++ if (fun->hasBoundFunctionNamePrefix()) {
|
|
|
+ return name;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Otherwise return "bound " * (number of bound function targets) + name.
|
|
|
+ size_t boundTargets = 0;
|
|
|
+ for (JSFunction* boundFn = fun; boundFn->isBoundFunction(); ) {
|
|
|
+ boundTargets++;
|
|
|
+
|
|
|
+ JSObject* target = boundFn->getBoundFunctionTarget();
|
|
|
+- if (!target->is<JSFunction>())
|
|
|
++ if (!target->is<JSFunction>()) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ boundFn = &target->as<JSFunction>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // |function /*unnamed*/ (){...}.bind()| is a common case, handle it here.
|
|
|
+- if (name->empty() && boundTargets == 1)
|
|
|
++ if (name->empty() && boundTargets == 1) {
|
|
|
+ return cx->names().boundWithSpace;
|
|
|
++ }
|
|
|
+
|
|
|
+ static constexpr char boundWithSpaceChars[] = "bound ";
|
|
|
+ static constexpr size_t boundWithSpaceCharsLength =
|
|
|
+ ArrayLength(boundWithSpaceChars) - 1; // No trailing '\0'.
|
|
|
+ MOZ_ASSERT(StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
|
|
|
+
|
|
|
+ StringBuffer sb(cx);
|
|
|
+- if (name->hasTwoByteChars() && !sb.ensureTwoByteChars())
|
|
|
++ if (name->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ CheckedInt<size_t> len(boundTargets);
|
|
|
+ len *= boundWithSpaceCharsLength;
|
|
|
+ len += name->length();
|
|
|
+ if (!len.isValid()) {
|
|
|
+ ReportAllocationOverflow(cx);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+- if (!sb.reserve(len.value()))
|
|
|
++ if (!sb.reserve(len.value())) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- while (boundTargets--)
|
|
|
++ }
|
|
|
++
|
|
|
++ while (boundTargets--) {
|
|
|
+ sb.infallibleAppend(boundWithSpaceChars, boundWithSpaceCharsLength);
|
|
|
++ }
|
|
|
+ sb.infallibleAppendSubstring(name, 0, name->length());
|
|
|
+
|
|
|
+ return sb.finishString();
|
|
|
+ }
|
|
|
+
|
|
|
+ static const js::Value&
|
|
|
+ BoundFunctionEnvironmentSlotValue(const JSFunction* fun, uint32_t slotIndex)
|
|
|
+ {
|
|
|
+@@ -1457,69 +1565,77 @@ JSFunction::getBoundFunctionArgumentCoun
|
|
|
+
|
|
|
+ static JSAtom*
|
|
|
+ AppendBoundFunctionPrefix(JSContext* cx, JSString* str)
|
|
|
+ {
|
|
|
+ static constexpr char boundWithSpaceChars[] = "bound ";
|
|
|
+ MOZ_ASSERT(StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
|
|
|
+
|
|
|
+ StringBuffer sb(cx);
|
|
|
+- if (!sb.append(boundWithSpaceChars) || !sb.append(str))
|
|
|
++ if (!sb.append(boundWithSpaceChars) || !sb.append(str)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return sb.finishAtom();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSFunction::finishBoundFunctionInit(JSContext* cx, HandleFunction bound, HandleObject targetObj,
|
|
|
+ int32_t argCount)
|
|
|
+ {
|
|
|
+ bound->setIsBoundFunction();
|
|
|
+ MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj);
|
|
|
+
|
|
|
+ // 9.4.1.3 BoundFunctionCreate, steps 1, 3-5, 8-12 (Already performed).
|
|
|
+
|
|
|
+ // 9.4.1.3 BoundFunctionCreate, step 6.
|
|
|
+- if (targetObj->isConstructor())
|
|
|
++ if (targetObj->isConstructor()) {
|
|
|
+ bound->setIsConstructor();
|
|
|
++ }
|
|
|
+
|
|
|
+ // 9.4.1.3 BoundFunctionCreate, step 2.
|
|
|
+ RootedObject proto(cx);
|
|
|
+- if (!GetPrototype(cx, targetObj, &proto))
|
|
|
++ if (!GetPrototype(cx, targetObj, &proto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // 9.4.1.3 BoundFunctionCreate, step 7.
|
|
|
+ if (bound->staticPrototype() != proto) {
|
|
|
+- if (!SetPrototype(cx, bound, proto))
|
|
|
++ if (!SetPrototype(cx, bound, proto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ double length = 0.0;
|
|
|
+
|
|
|
+ // Try to avoid invoking the resolve hook.
|
|
|
+ if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedLength()) {
|
|
|
+ RootedValue targetLength(cx);
|
|
|
+- if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength))
|
|
|
++ if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ length = Max(0.0, targetLength.toNumber() - argCount);
|
|
|
+ } else {
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 5.
|
|
|
+ bool hasLength;
|
|
|
+ RootedId idRoot(cx, NameToId(cx->names().length));
|
|
|
+- if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength))
|
|
|
++ if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 6.
|
|
|
+ if (hasLength) {
|
|
|
+ RootedValue targetLength(cx);
|
|
|
+- if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength))
|
|
|
++ if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (targetLength.isNumber())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (targetLength.isNumber()) {
|
|
|
+ length = Max(0.0, JS::ToInteger(targetLength.toNumber()) - argCount);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 7 (implicit).
|
|
|
+ }
|
|
|
+
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 8.
|
|
|
+ bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, NumberValue(length));
|
|
|
+
|
|
|
+@@ -1530,50 +1646,56 @@ JSFunction::finishBoundFunctionInit(JSCo
|
|
|
+ if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedName()) {
|
|
|
+ JSFunction* targetFn = &targetObj->as<JSFunction>();
|
|
|
+
|
|
|
+ // If the target is a bound function with a prefixed name, we can't
|
|
|
+ // lazily compute the full name in getBoundFunctionName(), therefore
|
|
|
+ // we need to append the bound function name prefix here.
|
|
|
+ if (targetFn->isBoundFunction() && targetFn->hasBoundFunctionNamePrefix()) {
|
|
|
+ name = AppendBoundFunctionPrefix(cx, targetFn->explicitName());
|
|
|
+- if (!name)
|
|
|
++ if (!name) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ bound->setPrefixedBoundFunctionName(name);
|
|
|
+ } else {
|
|
|
+ name = targetFn->infallibleGetUnresolvedName(cx);
|
|
|
+- if (name)
|
|
|
++ if (name) {
|
|
|
+ bound->setAtom(name);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 19.2.3.2 Function.prototype.bind, steps 9-11.
|
|
|
+ if (!name) {
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 9.
|
|
|
+ RootedValue targetName(cx);
|
|
|
+- if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName))
|
|
|
++ if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // 19.2.3.2 Function.prototype.bind, step 10.
|
|
|
+- if (!targetName.isString())
|
|
|
++ if (!targetName.isString()) {
|
|
|
+ targetName.setString(cx->names().empty);
|
|
|
++ }
|
|
|
+
|
|
|
+ // If the target itself is a bound function (with a resolved name), we
|
|
|
+ // can't compute the full name in getBoundFunctionName() based only on
|
|
|
+ // the number of bound target functions, therefore we need to store
|
|
|
+ // the complete prefixed name here.
|
|
|
+ if (targetObj->is<JSFunction>() && targetObj->as<JSFunction>().isBoundFunction()) {
|
|
|
+ name = AppendBoundFunctionPrefix(cx, targetName.toString());
|
|
|
+- if (!name)
|
|
|
++ if (!name) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ bound->setPrefixedBoundFunctionName(name);
|
|
|
+ } else {
|
|
|
+ name = AtomizeString(cx, targetName.toString());
|
|
|
+- if (!name)
|
|
|
++ if (!name) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ bound->setAtom(name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+@@ -1597,63 +1719,68 @@ JSFunction::createScriptForLazilyInterpr
|
|
|
+ bool canRelazify = !lazy->numInnerFunctions() && !lazy->hasDirectEval();
|
|
|
+
|
|
|
+ if (script) {
|
|
|
+ // This function is non-canonical function, and the canonical
|
|
|
+ // function is already delazified.
|
|
|
+ fun->setUnlazifiedScript(script);
|
|
|
+ // Remember the lazy script on the compiled script, so it can be
|
|
|
+ // stored on the function again in case of re-lazification.
|
|
|
+- if (canRelazify)
|
|
|
++ if (canRelazify) {
|
|
|
+ script->setLazyScript(lazy);
|
|
|
++ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fun != lazy->functionNonDelazifying()) {
|
|
|
+ // This function is non-canonical function, and the canonical
|
|
|
+ // function is lazy.
|
|
|
+ // Delazify the canonical function, which will result in calling
|
|
|
+ // this function again with the canonical function.
|
|
|
+- if (!LazyScript::functionDelazifying(cx, lazy))
|
|
|
++ if (!LazyScript::functionDelazifying(cx, lazy)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ script = lazy->functionNonDelazifying()->nonLazyScript();
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ fun->setUnlazifiedScript(script);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This is lazy canonical-function.
|
|
|
+
|
|
|
+ MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
|
|
|
+
|
|
|
+ // Parse and compile the script from source.
|
|
|
+ size_t lazyLength = lazy->sourceEnd() - lazy->sourceStart();
|
|
|
+ UncompressedSourceCache::AutoHoldEntry holder;
|
|
|
+ ScriptSource::PinnedChars chars(cx, lazy->scriptSource(), holder,
|
|
|
+ lazy->sourceStart(), lazyLength);
|
|
|
+- if (!chars.get())
|
|
|
++ if (!chars.get()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!frontend::CompileLazyFunction(cx, lazy, chars.get(), lazyLength)) {
|
|
|
+ // The frontend shouldn't fail after linking the function and the
|
|
|
+ // non-lazy script together.
|
|
|
+ MOZ_ASSERT(fun->isInterpretedLazy());
|
|
|
+ MOZ_ASSERT(fun->lazyScript() == lazy);
|
|
|
+ MOZ_ASSERT(!lazy->hasScript());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ script = fun->nonLazyScript();
|
|
|
+
|
|
|
+ // Remember the compiled script on the lazy script itself, in case
|
|
|
+ // there are clones of the function still pointing to the lazy script.
|
|
|
+- if (!lazy->maybeScript())
|
|
|
++ if (!lazy->maybeScript()) {
|
|
|
+ lazy->initScript(script);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Try to insert the newly compiled script into the lazy script cache.
|
|
|
+ if (canRelazify) {
|
|
|
+ // A script's starting column isn't set by the bytecode emitter, so
|
|
|
+ // specify this from the lazy script so that if an identical lazy
|
|
|
+ // script is encountered later a match can be determined.
|
|
|
+ script->setColumn(lazy->column());
|
|
|
+
|
|
|
+@@ -1661,66 +1788,73 @@ JSFunction::createScriptForLazilyInterpr
|
|
|
+ // stored on the function again in case of re-lazification.
|
|
|
+ // Only functions without inner functions are re-lazified.
|
|
|
+ script->setLazyScript(lazy);
|
|
|
+ }
|
|
|
+
|
|
|
+ // XDR the newly delazified function.
|
|
|
+ if (script->scriptSource()->hasEncoder()) {
|
|
|
+ RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
|
|
|
+- if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
|
|
|
++ if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Lazily cloned self-hosted script. */
|
|
|
+ MOZ_ASSERT(fun->isSelfHostedBuiltin());
|
|
|
+ RootedAtom funAtom(cx, &fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom());
|
|
|
+- if (!funAtom)
|
|
|
++ if (!funAtom) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ Rooted<PropertyName*> funName(cx, funAtom->asPropertyName());
|
|
|
+ return cx->runtime()->cloneSelfHostedFunctionScript(cx, funName, fun);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSFunction::maybeRelazify(JSRuntime* rt)
|
|
|
+ {
|
|
|
+ // Try to relazify functions with a non-lazy script. Note: functions can be
|
|
|
+ // marked as interpreted despite having no script yet at some points when
|
|
|
+ // parsing.
|
|
|
+- if (!hasScript() || !u.scripted.s.script_)
|
|
|
++ if (!hasScript() || !u.scripted.s.script_) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Don't relazify functions in compartments that are active.
|
|
|
+ Realm* realm = this->realm();
|
|
|
+ if (!rt->allowRelazificationForTesting) {
|
|
|
+- if (realm->compartment()->gcState.hasEnteredRealm)
|
|
|
++ if (realm->compartment()->gcState.hasEnteredRealm) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!realm->hasBeenEnteredIgnoringJit());
|
|
|
+ }
|
|
|
+
|
|
|
+ // The caller should have checked we're not in the self-hosting zone (it's
|
|
|
+ // shared with worker runtimes so relazifying functions in it will race).
|
|
|
+ MOZ_ASSERT(!realm->isSelfHostingRealm());
|
|
|
+
|
|
|
+ // Don't relazify if the realm is being debugged.
|
|
|
+- if (realm->isDebuggee())
|
|
|
++ if (realm->isDebuggee()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Don't relazify if the realm and/or runtime is instrumented to
|
|
|
+ // collect code coverage for analysis.
|
|
|
+- if (realm->collectCoverageForDebug())
|
|
|
++ if (realm->collectCoverageForDebug()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Don't relazify functions with JIT code.
|
|
|
+- if (!u.scripted.s.script_->isRelazifiable())
|
|
|
++ if (!u.scripted.s.script_->isRelazifiable()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ // To delazify self-hosted builtins we need the name of the function
|
|
|
+ // to clone. This name is stored in the first extended slot. Since
|
|
|
+ // that slot is sometimes also used for other purposes, make sure it
|
|
|
+ // contains a string.
|
|
|
+ if (isSelfHostedBuiltin() &&
|
|
|
+ (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
|
|
|
+ {
|
|
|
+@@ -1771,101 +1905,116 @@ CreateDynamicFunction(JSContext* cx, con
|
|
|
+ unsigned lineno;
|
|
|
+ bool mutedErrors;
|
|
|
+ uint32_t pcOffset;
|
|
|
+ DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
|
|
|
+ &mutedErrors);
|
|
|
+
|
|
|
+ const char* introductionType = "Function";
|
|
|
+ if (isAsync) {
|
|
|
+- if (isGenerator)
|
|
|
++ if (isGenerator) {
|
|
|
+ introductionType = "AsyncGenerator";
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ introductionType = "AsyncFunction";
|
|
|
++ }
|
|
|
+ } else if (isGenerator) {
|
|
|
+ introductionType = "GeneratorFunction";
|
|
|
+ }
|
|
|
+
|
|
|
+ const char* introducerFilename = filename;
|
|
|
+- if (maybeScript && maybeScript->scriptSource()->introducerFilename())
|
|
|
++ if (maybeScript && maybeScript->scriptSource()->introducerFilename()) {
|
|
|
+ introducerFilename = maybeScript->scriptSource()->introducerFilename();
|
|
|
++ }
|
|
|
+
|
|
|
+ CompileOptions options(cx);
|
|
|
+ options.setMutedErrors(mutedErrors)
|
|
|
+ .setFileAndLine(filename, 1)
|
|
|
+ .setNoScriptRval(false)
|
|
|
+ .setIntroductionInfo(introducerFilename, introductionType, lineno, maybeScript, pcOffset);
|
|
|
+
|
|
|
+ StringBuffer sb(cx);
|
|
|
+
|
|
|
+ if (isAsync) {
|
|
|
+- if (!sb.append("async "))
|
|
|
++ if (!sb.append("async ")) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+- if (!sb.append("function"))
|
|
|
++ if (!sb.append("function")) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (isGenerator) {
|
|
|
+- if (!sb.append('*'))
|
|
|
++ if (!sb.append('*')) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!sb.append(" anonymous("))
|
|
|
++ if (!sb.append(" anonymous(")) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (args.length() > 1) {
|
|
|
+ RootedString str(cx);
|
|
|
+
|
|
|
+ // Steps 10, 14.d.
|
|
|
+ unsigned n = args.length() - 1;
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < n; i++) {
|
|
|
+ // Steps 14.a-b, 14.d.i-ii.
|
|
|
+ str = ToString<CanGC>(cx, args[i]);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 14.b, 14.d.iii.
|
|
|
+- if (!sb.append(str))
|
|
|
++ if (!sb.append(str)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (i < args.length() - 2) {
|
|
|
+ // Step 14.d.iii.
|
|
|
+- if (!sb.append(','))
|
|
|
++ if (!sb.append(',')) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!sb.append('\n'))
|
|
|
++ if (!sb.append('\n')) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Remember the position of ")".
|
|
|
+ Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length()));
|
|
|
+ MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');
|
|
|
+
|
|
|
+- if (!sb.append(FunctionConstructorMedialSigils))
|
|
|
++ if (!sb.append(FunctionConstructorMedialSigils)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (args.length() > 0) {
|
|
|
+ // Steps 13, 14.e, 15.
|
|
|
+ RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1]));
|
|
|
+- if (!body || !sb.append(body))
|
|
|
++ if (!body || !sb.append(body)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!sb.append(FunctionConstructorFinalBrace))
|
|
|
++ if (!sb.append(FunctionConstructorFinalBrace)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // The parser only accepts two byte strings.
|
|
|
+- if (!sb.ensureTwoByteChars())
|
|
|
++ if (!sb.ensureTwoByteChars()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedString functionText(cx, sb.finishString());
|
|
|
+- if (!functionText)
|
|
|
++ if (!functionText) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Block this call if security callbacks forbid it.
|
|
|
+ Handle<GlobalObject*> global = cx->global();
|
|
|
+ RootedValue v(cx, StringValue(functionText));
|
|
|
+ if (!GlobalObject::isRuntimeCodeGenEnabled(cx, v, global)) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+@@ -1880,92 +2029,103 @@ CreateDynamicFunction(JSContext* cx, con
|
|
|
+
|
|
|
+ // Initialize the function with the default prototype:
|
|
|
+ // Leave as nullptr to get the default from clasp for normal functions.
|
|
|
+ // Use %Generator% for generators and the unwrapped function of async
|
|
|
+ // functions and async generators.
|
|
|
+ RootedObject defaultProto(cx);
|
|
|
+ if (isGenerator || isAsync) {
|
|
|
+ defaultProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global);
|
|
|
+- if (!defaultProto)
|
|
|
++ if (!defaultProto) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 30-37 (reordered).
|
|
|
+ RootedObject globalLexical(cx, &global->lexicalEnvironment());
|
|
|
+ JSFunction::Flags flags = (isGenerator || isAsync)
|
|
|
+ ? JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC
|
|
|
+ : JSFunction::INTERPRETED_LAMBDA;
|
|
|
+ gc::AllocKind allocKind = isAsync ? gc::AllocKind::FUNCTION_EXTENDED
|
|
|
+ : gc::AllocKind::FUNCTION;
|
|
|
+ RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0,
|
|
|
+ flags, globalLexical,
|
|
|
+ anonymousAtom, defaultProto,
|
|
|
+ allocKind, TenuredObject));
|
|
|
+- if (!fun)
|
|
|
++ if (!fun) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (!JSFunction::setTypeForScriptedFunction(cx, fun))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!JSFunction::setTypeForScriptedFunction(cx, fun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 7.a-b, 8.a-b, 9.a-b, 16-28.
|
|
|
+ AutoStableStringChars stableChars(cx);
|
|
|
+- if (!stableChars.initTwoByte(cx, functionText))
|
|
|
++ if (!stableChars.initTwoByte(cx, functionText)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
|
|
|
+ SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
|
|
|
+ ? SourceBufferHolder::GiveOwnership
|
|
|
+ : SourceBufferHolder::NoOwnership;
|
|
|
+ SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
|
|
|
+ if (isAsync) {
|
|
|
+ if (isGenerator) {
|
|
|
+- if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, parameterListEnd))
|
|
|
++ if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, parameterListEnd)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd))
|
|
|
++ if (!CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (isGenerator) {
|
|
|
+- if (!CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd))
|
|
|
++ if (!CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd))
|
|
|
++ if (!CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Steps 6, 29.
|
|
|
+ RootedObject proto(cx);
|
|
|
+- if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
|
|
++ if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (isAsync) {
|
|
|
+ // Create the async function wrapper.
|
|
|
+ JSObject* wrapped;
|
|
|
+ if (isGenerator) {
|
|
|
+ wrapped = proto
|
|
|
+ ? WrapAsyncGeneratorWithProto(cx, fun, proto)
|
|
|
+ : WrapAsyncGenerator(cx, fun);
|
|
|
+ } else {
|
|
|
+ // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype.
|
|
|
+ wrapped = proto
|
|
|
+ ? WrapAsyncFunctionWithProto(cx, fun, proto)
|
|
|
+ : WrapAsyncFunction(cx, fun);
|
|
|
+ }
|
|
|
+- if (!wrapped)
|
|
|
++ if (!wrapped) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ fun = &wrapped->as<JSFunction>();
|
|
|
+ } else {
|
|
|
+ // Steps 7.d, 8.d (implicit).
|
|
|
+ // Call SetPrototype if an explicit prototype was given.
|
|
|
+- if (proto && !SetPrototype(cx, fun, proto))
|
|
|
++ if (proto && !SetPrototype(cx, fun, proto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 38.
|
|
|
+ args.rval().setObject(*fun);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+@@ -2006,63 +2166,70 @@ JSFunction::isBuiltinFunctionConstructor
|
|
|
+ return maybeNative() == Function || maybeNative() == Generator;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSFunction::needsExtraBodyVarEnvironment() const
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!isInterpretedLazy());
|
|
|
+
|
|
|
+- if (isNative())
|
|
|
++ if (isNative()) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (!nonLazyScript()->functionHasExtraBodyVarScope())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!nonLazyScript()->functionHasExtraBodyVarScope()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return nonLazyScript()->functionExtraBodyVarScope()->hasEnvironment();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSFunction::needsNamedLambdaEnvironment() const
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!isInterpretedLazy());
|
|
|
+
|
|
|
+- if (!isNamedLambda())
|
|
|
++ if (!isNamedLambda()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ LexicalScope* scope = nonLazyScript()->maybeNamedLambdaScope();
|
|
|
+- if (!scope)
|
|
|
++ if (!scope) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return scope->hasEnvironment();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFunction*
|
|
|
+ js::NewScriptedFunction(JSContext* cx, unsigned nargs,
|
|
|
+ JSFunction::Flags flags, HandleAtom atom,
|
|
|
+ HandleObject proto /* = nullptr */,
|
|
|
+ gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
|
|
|
+ NewObjectKind newKind /* = GenericObject */,
|
|
|
+ HandleObject enclosingEnvArg /* = nullptr */)
|
|
|
+ {
|
|
|
+ RootedObject enclosingEnv(cx, enclosingEnvArg);
|
|
|
+- if (!enclosingEnv)
|
|
|
++ if (!enclosingEnv) {
|
|
|
+ enclosingEnv = &cx->global()->lexicalEnvironment();
|
|
|
++ }
|
|
|
+ return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingEnv,
|
|
|
+ atom, proto, allocKind, newKind);
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ static JSObject*
|
|
|
+ SkipEnvironmentObjects(JSObject* env)
|
|
|
+ {
|
|
|
+- if (!env)
|
|
|
++ if (!env) {
|
|
|
+ return nullptr;
|
|
|
+- while (env->is<EnvironmentObject>())
|
|
|
++ }
|
|
|
++ while (env->is<EnvironmentObject>()) {
|
|
|
+ env = &env->as<EnvironmentObject>().enclosingEnvironment();
|
|
|
++ }
|
|
|
+ return env;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ NewFunctionEnvironmentIsWellFormed(JSContext* cx, HandleObject env)
|
|
|
+ {
|
|
|
+ // Assert that the terminating environment is null, global, or a debug
|
|
|
+ // scope proxy. All other cases of polluting global scope behavior are
|
|
|
+@@ -2082,42 +2249,47 @@ js::NewFunctionWithProto(JSContext* cx,
|
|
|
+ NewObjectKind newKind /* = GenericObject */)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(allocKind == gc::AllocKind::FUNCTION ||
|
|
|
+ allocKind == gc::AllocKind::FUNCTION_EXTENDED);
|
|
|
+ MOZ_ASSERT_IF(native, !enclosingEnv);
|
|
|
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
|
|
|
+
|
|
|
+ JSFunction* fun = NewObjectWithClassProto<JSFunction>(cx, proto, allocKind, newKind);
|
|
|
+- if (!fun)
|
|
|
++ if (!fun) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
|
|
|
+ flags = JSFunction::Flags(flags | JSFunction::EXTENDED);
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Initialize all function members. */
|
|
|
+ fun->setArgCount(uint16_t(nargs));
|
|
|
+ fun->setFlags(flags);
|
|
|
+ if (fun->isInterpreted()) {
|
|
|
+ MOZ_ASSERT(!native);
|
|
|
+- if (fun->isInterpretedLazy())
|
|
|
++ if (fun->isInterpretedLazy()) {
|
|
|
+ fun->initLazyScript(nullptr);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun->initScript(nullptr);
|
|
|
++ }
|
|
|
+ fun->initEnvironment(enclosingEnv);
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(fun->isNative());
|
|
|
+ MOZ_ASSERT(native);
|
|
|
+- if (fun->isWasmOptimized())
|
|
|
++ if (fun->isWasmOptimized()) {
|
|
|
+ fun->initWasmNative(native);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun->initNative(native, nullptr);
|
|
|
++ }
|
|
|
+ }
|
|
|
+- if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
|
|
|
++ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
|
|
|
+ fun->initializeExtended();
|
|
|
++ }
|
|
|
+ fun->initAtom(atom);
|
|
|
+
|
|
|
+ return fun;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::CanReuseScriptForClone(JS::Realm* realm, HandleFunction fun,
|
|
|
+ HandleObject newParent)
|
|
|
+@@ -2126,76 +2298,83 @@ js::CanReuseScriptForClone(JS::Realm* re
|
|
|
+
|
|
|
+ if (realm != fun->realm() ||
|
|
|
+ fun->isSingleton() ||
|
|
|
+ ObjectGroup::useSingletonForClone(fun))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (newParent->is<GlobalObject>())
|
|
|
++ if (newParent->is<GlobalObject>()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Don't need to clone the script if newParent is a syntactic scope, since
|
|
|
+ // in that case we have some actual scope objects on our scope chain and
|
|
|
+ // whatnot; whoever put them there should be responsible for setting our
|
|
|
+ // script's flags appropriately. We hit this case for JSOP_LAMBDA, for
|
|
|
+ // example.
|
|
|
+- if (IsSyntacticEnvironment(newParent))
|
|
|
++ if (IsSyntacticEnvironment(newParent)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // We need to clone the script if we're not already marked as having a
|
|
|
+ // non-syntactic scope.
|
|
|
+ return fun->hasScript()
|
|
|
+ ? fun->nonLazyScript()->hasNonSyntacticScope()
|
|
|
+ : fun->lazyScript()->hasNonSyntacticScope();
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline JSFunction*
|
|
|
+ NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
|
|
|
+ gc::AllocKind allocKind, HandleObject proto)
|
|
|
+ {
|
|
|
+ RootedObject cloneProto(cx, proto);
|
|
|
+ if (!proto && (fun->isGenerator() || fun->isAsync())) {
|
|
|
+ cloneProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
|
|
|
+- if (!cloneProto)
|
|
|
++ if (!cloneProto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedFunction clone(cx);
|
|
|
+ clone = NewObjectWithClassProto<JSFunction>(cx, cloneProto, allocKind, newKind);
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // JSFunction::HAS_INFERRED_NAME can be set at compile-time and at
|
|
|
+ // runtime. In the latter case we should actually clear the flag before
|
|
|
+ // cloning the function, but since we can't differentiate between both
|
|
|
+ // cases here, we'll end up with a momentarily incorrect function name.
|
|
|
+ // This will be fixed up in SetFunctionNameIfNoOwnName(), which should
|
|
|
+ // happen through JSOP_SETFUNNAME directly after JSOP_LAMBDA.
|
|
|
+ constexpr uint16_t NonCloneableFlags = JSFunction::EXTENDED |
|
|
|
+ JSFunction::RESOLVED_LENGTH |
|
|
|
+ JSFunction::RESOLVED_NAME;
|
|
|
+
|
|
|
+ uint16_t flags = fun->flags() & ~NonCloneableFlags;
|
|
|
+- if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
|
|
|
++ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
|
|
|
+ flags |= JSFunction::EXTENDED;
|
|
|
++ }
|
|
|
+
|
|
|
+ clone->setArgCount(fun->nargs());
|
|
|
+ clone->setFlags(flags);
|
|
|
+
|
|
|
+ JSAtom* atom = fun->displayAtom();
|
|
|
+- if (atom)
|
|
|
++ if (atom) {
|
|
|
+ cx->markAtom(atom);
|
|
|
++ }
|
|
|
+ clone->initAtom(atom);
|
|
|
+
|
|
|
+ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
|
|
|
+ if (fun->isExtended() && fun->compartment() == cx->compartment()) {
|
|
|
+- for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++)
|
|
|
++ for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++) {
|
|
|
+ clone->initExtendedSlot(i, fun->getExtendedSlot(i));
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ clone->initializeExtended();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2206,99 +2385,106 @@ js::CloneFunctionReuseScript(JSContext*
|
|
|
+ HandleObject proto /* = nullptr */)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
|
|
|
+ MOZ_ASSERT(fun->isInterpreted());
|
|
|
+ MOZ_ASSERT(!fun->isBoundFunction());
|
|
|
+ MOZ_ASSERT(CanReuseScriptForClone(cx->realm(), fun, enclosingEnv));
|
|
|
+
|
|
|
+ RootedFunction clone(cx, NewFunctionClone(cx, fun, newKind, allocKind, proto));
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (fun->hasScript()) {
|
|
|
+ clone->initScript(fun->nonLazyScript());
|
|
|
+ clone->initEnvironment(enclosingEnv);
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(fun->isInterpretedLazy());
|
|
|
+ MOZ_ASSERT(fun->compartment() == clone->compartment());
|
|
|
+ LazyScript* lazy = fun->lazyScriptOrNull();
|
|
|
+ clone->initLazyScript(lazy);
|
|
|
+ clone->initEnvironment(enclosingEnv);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clone the function, reusing its script. We can use the same group as
|
|
|
+ * the original function provided that its prototype is correct.
|
|
|
+ */
|
|
|
+- if (fun->staticPrototype() == clone->staticPrototype())
|
|
|
++ if (fun->staticPrototype() == clone->staticPrototype()) {
|
|
|
+ clone->setGroup(fun->group());
|
|
|
++ }
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFunction*
|
|
|
+ js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject enclosingEnv,
|
|
|
+ HandleScope newScope, gc::AllocKind allocKind /* = FUNCTION */,
|
|
|
+ HandleObject proto /* = nullptr */)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
|
|
|
+ MOZ_ASSERT(fun->isInterpreted());
|
|
|
+ MOZ_ASSERT(!fun->isBoundFunction());
|
|
|
+
|
|
|
+ JSScript::AutoDelazify funScript(cx, fun);
|
|
|
+- if (!funScript)
|
|
|
++ if (!funScript) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto));
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ clone->initScript(nullptr);
|
|
|
+ clone->initEnvironment(enclosingEnv);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Across compartments or if we have to introduce a non-syntactic scope we
|
|
|
+ * have to clone the script for interpreted functions. Cross-compartment
|
|
|
+ * cloning only happens via JSAPI (JS::CloneFunctionObject) which
|
|
|
+ * dynamically ensures that 'script' has no enclosing lexical scope (only
|
|
|
+ * the global scope or other non-lexical scope).
|
|
|
+ */
|
|
|
+ #ifdef DEBUG
|
|
|
+ RootedObject terminatingEnv(cx, enclosingEnv);
|
|
|
+- while (IsSyntacticEnvironment(terminatingEnv))
|
|
|
++ while (IsSyntacticEnvironment(terminatingEnv)) {
|
|
|
+ terminatingEnv = terminatingEnv->enclosingEnvironment();
|
|
|
++ }
|
|
|
+ MOZ_ASSERT_IF(!terminatingEnv->is<GlobalObject>(),
|
|
|
+ newScope->hasOnChain(ScopeKind::NonSyntactic));
|
|
|
+ #endif
|
|
|
+
|
|
|
+ RootedScript script(cx, fun->nonLazyScript());
|
|
|
+ MOZ_ASSERT(script->realm() == fun->realm());
|
|
|
+ MOZ_ASSERT(cx->compartment() == clone->compartment(),
|
|
|
+ "Otherwise we could relazify clone below!");
|
|
|
+
|
|
|
+ RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newScope, clone, script));
|
|
|
+- if (!clonedScript)
|
|
|
++ if (!clonedScript) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ Debugger::onNewScript(cx, clonedScript);
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFunction*
|
|
|
+ js::CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(fun->isNative());
|
|
|
+ MOZ_ASSERT(IsAsmJSModule(fun));
|
|
|
+ MOZ_ASSERT(fun->isExtended());
|
|
|
+ MOZ_ASSERT(cx->compartment() == fun->compartment());
|
|
|
+
|
|
|
+ JSFunction* clone = NewFunctionClone(cx, fun, GenericObject, gc::AllocKind::FUNCTION_EXTENDED,
|
|
|
+ /* proto = */ nullptr);
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(fun->native() == InstantiateAsmJS);
|
|
|
+ MOZ_ASSERT(!fun->hasJitInfo());
|
|
|
+ clone->initNative(InstantiateAsmJS, nullptr);
|
|
|
+
|
|
|
+ clone->setGroup(fun->group());
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+@@ -2308,74 +2494,84 @@ js::CloneSelfHostingIntrinsic(JSContext*
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(fun->isNative());
|
|
|
+ MOZ_ASSERT(fun->realm()->isSelfHostingRealm());
|
|
|
+ MOZ_ASSERT(!fun->isExtended());
|
|
|
+ MOZ_ASSERT(cx->compartment() != fun->compartment());
|
|
|
+
|
|
|
+ JSFunction* clone = NewFunctionClone(cx, fun, SingletonObject, gc::AllocKind::FUNCTION,
|
|
|
+ /* proto = */ nullptr);
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ clone->initNative(fun->native(), fun->hasJitInfo() ? fun->jitInfo() : nullptr);
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ static JSAtom*
|
|
|
+ SymbolToFunctionName(JSContext* cx, JS::Symbol* symbol, FunctionPrefixKind prefixKind)
|
|
|
+ {
|
|
|
+ // Step 4.a.
|
|
|
+ JSAtom* desc = symbol->description();
|
|
|
+
|
|
|
+ // Step 4.b, no prefix fastpath.
|
|
|
+- if (!desc && prefixKind == FunctionPrefixKind::None)
|
|
|
++ if (!desc && prefixKind == FunctionPrefixKind::None) {
|
|
|
+ return cx->names().empty;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 5 (reordered).
|
|
|
+ StringBuffer sb(cx);
|
|
|
+ if (prefixKind == FunctionPrefixKind::Get) {
|
|
|
+- if (!sb.append("get "))
|
|
|
++ if (!sb.append("get ")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else if (prefixKind == FunctionPrefixKind::Set) {
|
|
|
+- if (!sb.append("set "))
|
|
|
++ if (!sb.append("set ")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 4.b.
|
|
|
+ if (desc) {
|
|
|
+ // Step 4.c.
|
|
|
+- if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
|
|
|
++ if (!sb.append('[') || !sb.append(desc) || !sb.append(']')) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return sb.finishAtom();
|
|
|
+ }
|
|
|
+
|
|
|
+ static JSAtom*
|
|
|
+ NameToFunctionName(JSContext* cx, HandleValue name, FunctionPrefixKind prefixKind)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(name.isString() || name.isNumber());
|
|
|
+
|
|
|
+- if (prefixKind == FunctionPrefixKind::None)
|
|
|
++ if (prefixKind == FunctionPrefixKind::None) {
|
|
|
+ return ToAtom<CanGC>(cx, name);
|
|
|
++ }
|
|
|
+
|
|
|
+ JSString* nameStr = ToString(cx, name);
|
|
|
+- if (!nameStr)
|
|
|
++ if (!nameStr) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ StringBuffer sb(cx);
|
|
|
+ if (prefixKind == FunctionPrefixKind::Get) {
|
|
|
+- if (!sb.append("get "))
|
|
|
++ if (!sb.append("get ")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!sb.append("set "))
|
|
|
++ if (!sb.append("set ")) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+- if (!sb.append(nameStr))
|
|
|
++ if (!sb.append(nameStr)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return sb.finishAtom();
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Return an atom for use as the name of a builtin method with the given
|
|
|
+ * property id.
|
|
|
+ *
|
|
|
+ * Function names are always strings. If id is the well-known @@iterator
|
|
|
+@@ -2386,24 +2582,26 @@ NameToFunctionName(JSContext* cx, Handle
|
|
|
+ */
|
|
|
+ JSAtom*
|
|
|
+ js::IdToFunctionName(JSContext* cx, HandleId id,
|
|
|
+ FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id) || JSID_IS_INT(id));
|
|
|
+
|
|
|
+ // No prefix fastpath.
|
|
|
+- if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None)
|
|
|
++ if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None) {
|
|
|
+ return JSID_TO_ATOM(id);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 3 (implicit).
|
|
|
+
|
|
|
+ // Step 4.
|
|
|
+- if (JSID_IS_SYMBOL(id))
|
|
|
++ if (JSID_IS_SYMBOL(id)) {
|
|
|
+ return SymbolToFunctionName(cx, JSID_TO_SYMBOL(id), prefixKind);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 5.
|
|
|
+ RootedValue idv(cx, IdToValue(id));
|
|
|
+ return NameToFunctionName(cx, idv, prefixKind);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name,
|
|
|
+@@ -2416,28 +2614,30 @@ js::SetFunctionNameIfNoOwnName(JSContext
|
|
|
+ // end up not adding a new inferred name if |fun| is a class constructor.
|
|
|
+ if (fun->hasInferredName()) {
|
|
|
+ MOZ_ASSERT(fun->isSingleton());
|
|
|
+ fun->clearInferredName();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fun->isClassConstructor()) {
|
|
|
+ // A class may have static 'name' method or accessor.
|
|
|
+- if (fun->contains(cx, cx->names().name))
|
|
|
++ if (fun->contains(cx, cx->names().name)) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ // Anonymous function shouldn't have own 'name' property at this point.
|
|
|
+ MOZ_ASSERT(!fun->containsPure(cx->names().name));
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom* funName = name.isSymbol()
|
|
|
+ ? SymbolToFunctionName(cx, name.toSymbol(), prefixKind)
|
|
|
+ : NameToFunctionName(cx, name, prefixKind);
|
|
|
+- if (!funName)
|
|
|
++ if (!funName) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // RESOLVED_NAME shouldn't yet be set, at least as long as we don't
|
|
|
+ // support the "static public fields" or "decorators" proposal.
|
|
|
+ // These two proposals allow to access class constructors before
|
|
|
+ // JSOP_SETFUNNAME is executed, which means user code may have set the
|
|
|
+ // RESOLVED_NAME flag when we reach this point.
|
|
|
+ MOZ_ASSERT(!fun->hasResolvedName());
|
|
|
+
|
|
|
+@@ -2446,36 +2646,40 @@ js::SetFunctionNameIfNoOwnName(JSContext
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFunction*
|
|
|
+ js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native,
|
|
|
+ unsigned nargs, unsigned flags, gc::AllocKind allocKind /* = AllocKind::FUNCTION */)
|
|
|
+ {
|
|
|
+ RootedAtom atom(cx, IdToFunctionName(cx, id));
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedFunction fun(cx);
|
|
|
+- if (!native)
|
|
|
++ if (!native) {
|
|
|
+ fun = NewScriptedFunction(cx, nargs,
|
|
|
+ JSFunction::INTERPRETED_LAZY, atom,
|
|
|
+ /* proto = */ nullptr,
|
|
|
+ allocKind, GenericObject, obj);
|
|
|
+- else if (flags & JSFUN_CONSTRUCTOR)
|
|
|
++ } else if (flags & JSFUN_CONSTRUCTOR) {
|
|
|
+ fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
|
|
|
+-
|
|
|
+- if (!fun)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!fun) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue funVal(cx, ObjectValue(*fun));
|
|
|
+- if (!DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK))
|
|
|
++ if (!DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return fun;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::ReportIncompatibleMethod(JSContext* cx, const CallArgs& args, const Class* clasp)
|
|
|
+ {
|
|
|
+ RootedValue thisv(cx, args.thisv());
|
|
|
+diff --git a/js/src/vm/JSFunction.h b/js/src/vm/JSFunction.h
|
|
|
+--- a/js/src/vm/JSFunction.h
|
|
|
++++ b/js/src/vm/JSFunction.h
|
|
|
+@@ -187,18 +187,19 @@ class JSFunction : public js::NativeObje
|
|
|
+ static inline JS::Result<JSFunction*, JS::OOM&>
|
|
|
+ create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
|
|
|
+ js::HandleShape shape, js::HandleObjectGroup group);
|
|
|
+
|
|
|
+ /* Call objects must be created for each invocation of this function. */
|
|
|
+ bool needsCallObject() const {
|
|
|
+ MOZ_ASSERT(!isInterpretedLazy());
|
|
|
+
|
|
|
+- if (isNative())
|
|
|
++ if (isNative()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Note: this should be kept in sync with
|
|
|
+ // FunctionBox::needsCallObjectRegardlessOfBindings().
|
|
|
+ MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() ||
|
|
|
+ nonLazyScript()->needsHomeObject() ||
|
|
|
+ nonLazyScript()->isDerivedClassConstructor() ||
|
|
|
+ isGenerator() ||
|
|
|
+ isAsync(),
|
|
|
+@@ -290,18 +291,19 @@ class JSFunction : public js::NativeObje
|
|
|
+ bool hasResolvedLength() const { return flags() & RESOLVED_LENGTH; }
|
|
|
+ bool hasResolvedName() const { return flags() & RESOLVED_NAME; }
|
|
|
+
|
|
|
+ bool isSelfHostedOrIntrinsic() const { return flags() & SELF_HOSTED; }
|
|
|
+ bool isSelfHostedBuiltin() const { return isSelfHostedOrIntrinsic() && !isNative(); }
|
|
|
+ bool isIntrinsic() const { return isSelfHostedOrIntrinsic() && isNative(); }
|
|
|
+
|
|
|
+ bool hasJITCode() const {
|
|
|
+- if (!hasScript())
|
|
|
++ if (!hasScript()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
|
|
|
+ }
|
|
|
+ bool hasJitEntry() const {
|
|
|
+ return hasScript() || isNativeWithJitEntry();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Compound attributes: */
|
|
|
+@@ -387,20 +389,21 @@ class JSFunction : public js::NativeObje
|
|
|
+ bool wasNewScriptCleared() const {
|
|
|
+ return flags_ & NEW_SCRIPT_CLEARED;
|
|
|
+ }
|
|
|
+ void setNewScriptCleared() {
|
|
|
+ flags_ |= NEW_SCRIPT_CLEARED;
|
|
|
+ }
|
|
|
+
|
|
|
+ void setAsyncKind(js::FunctionAsyncKind asyncKind) {
|
|
|
+- if (isInterpretedLazy())
|
|
|
++ if (isInterpretedLazy()) {
|
|
|
+ lazyScript()->setAsyncKind(asyncKind);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ nonLazyScript()->setAsyncKind(asyncKind);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
|
|
|
+ js::MutableHandleValue v);
|
|
|
+
|
|
|
+ JSAtom* infallibleGetUnresolvedName(JSContext* cx);
|
|
|
+
|
|
|
+ static bool getUnresolvedName(JSContext* cx, js::HandleFunction fun,
|
|
|
+@@ -528,18 +531,19 @@ class JSFunction : public js::NativeObje
|
|
|
+ // use existingScriptNonDelazifying().
|
|
|
+ //
|
|
|
+ // - For functions known to have a JSScript, nonLazyScript() will get it.
|
|
|
+
|
|
|
+ static JSScript* getOrCreateScript(JSContext* cx, js::HandleFunction fun) {
|
|
|
+ MOZ_ASSERT(fun->isInterpreted());
|
|
|
+ MOZ_ASSERT(cx);
|
|
|
+ if (fun->isInterpretedLazy()) {
|
|
|
+- if (!createScriptForLazilyInterpretedFunction(cx, fun))
|
|
|
++ if (!createScriptForLazilyInterpretedFunction(cx, fun)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return fun->nonLazyScript();
|
|
|
+ }
|
|
|
+ return fun->nonLazyScript();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSScript* existingScriptNonDelazifying() const {
|
|
|
+ MOZ_ASSERT(isInterpreted());
|
|
|
+ if (isInterpretedLazy()) {
|
|
|
+@@ -554,18 +558,19 @@ class JSFunction : public js::NativeObje
|
|
|
+ return fun->nonLazyScript();
|
|
|
+ }
|
|
|
+ return nonLazyScript();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSScript* existingScript() {
|
|
|
+ MOZ_ASSERT(isInterpreted());
|
|
|
+ if (isInterpretedLazy()) {
|
|
|
+- if (shadowZone()->needsIncrementalBarrier())
|
|
|
++ if (shadowZone()->needsIncrementalBarrier()) {
|
|
|
+ js::LazyScript::writeBarrierPre(lazyScript());
|
|
|
++ }
|
|
|
+ JSScript* script = existingScriptNonDelazifying();
|
|
|
+ flags_ &= ~INTERPRETED_LAZY;
|
|
|
+ flags_ |= INTERPRETED;
|
|
|
+ initScript(script);
|
|
|
+ }
|
|
|
+ return nonLazyScript();
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -593,55 +598,61 @@ class JSFunction : public js::NativeObje
|
|
|
+ }
|
|
|
+
|
|
|
+ js::LazyScript* lazyScriptOrNull() const {
|
|
|
+ MOZ_ASSERT(isInterpretedLazy());
|
|
|
+ return u.scripted.s.lazy_;
|
|
|
+ }
|
|
|
+
|
|
|
+ js::GeneratorKind generatorKind() const {
|
|
|
+- if (!isInterpreted())
|
|
|
++ if (!isInterpreted()) {
|
|
|
+ return js::GeneratorKind::NotGenerator;
|
|
|
+- if (hasScript())
|
|
|
++ }
|
|
|
++ if (hasScript()) {
|
|
|
+ return nonLazyScript()->generatorKind();
|
|
|
+- if (js::LazyScript* lazy = lazyScriptOrNull())
|
|
|
++ }
|
|
|
++ if (js::LazyScript* lazy = lazyScriptOrNull()) {
|
|
|
+ return lazy->generatorKind();
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(isSelfHostedBuiltin());
|
|
|
+ return js::GeneratorKind::NotGenerator;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool isGenerator() const { return generatorKind() == js::GeneratorKind::Generator; }
|
|
|
+
|
|
|
+ js::FunctionAsyncKind asyncKind() const {
|
|
|
+ return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool isAsync() const {
|
|
|
+- if (isInterpretedLazy())
|
|
|
++ if (isInterpretedLazy()) {
|
|
|
+ return lazyScript()->isAsync();
|
|
|
+- if (hasScript())
|
|
|
++ }
|
|
|
++ if (hasScript()) {
|
|
|
+ return nonLazyScript()->isAsync();
|
|
|
++ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void setScript(JSScript* script_) {
|
|
|
+ mutableScript() = script_;
|
|
|
+ }
|
|
|
+
|
|
|
+ void initScript(JSScript* script_) {
|
|
|
+ mutableScript().init(script_);
|
|
|
+ }
|
|
|
+
|
|
|
+ void setUnlazifiedScript(JSScript* script) {
|
|
|
+ MOZ_ASSERT(isInterpretedLazy());
|
|
|
+ if (lazyScriptOrNull()) {
|
|
|
+ // Trigger a pre barrier on the lazy script being overwritten.
|
|
|
+ js::LazyScript::writeBarrierPre(lazyScriptOrNull());
|
|
|
+- if (!lazyScript()->maybeScript())
|
|
|
++ if (!lazyScript()->maybeScript()) {
|
|
|
+ lazyScript()->initScript(script);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ flags_ &= ~INTERPRETED_LAZY;
|
|
|
+ flags_ |= INTERPRETED;
|
|
|
+ initScript(script);
|
|
|
+ }
|
|
|
+
|
|
|
+ void initLazyScript(js::LazyScript* lazy) {
|
|
|
+ MOZ_ASSERT(isInterpreted());
|
|
|
+@@ -785,18 +796,19 @@ class JSFunction : public js::NativeObje
|
|
|
+
|
|
|
+ /* GC support. */
|
|
|
+ js::gc::AllocKind getAllocKind() const {
|
|
|
+ static_assert(js::gc::AllocKind::FUNCTION != js::gc::AllocKind::FUNCTION_EXTENDED,
|
|
|
+ "extended/non-extended AllocKinds have to be different "
|
|
|
+ "for getAllocKind() to have a reason to exist");
|
|
|
+
|
|
|
+ js::gc::AllocKind kind = js::gc::AllocKind::FUNCTION;
|
|
|
+- if (isExtended())
|
|
|
++ if (isExtended()) {
|
|
|
+ kind = js::gc::AllocKind::FUNCTION_EXTENDED;
|
|
|
++ }
|
|
|
+ MOZ_ASSERT_IF(isTenured(), kind == asTenured().getAllocKind());
|
|
|
+ return kind;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
|
|
|
+ "shadow interface must match actual interface");
|
|
|
+
|
|
|
+diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp
|
|
|
+--- a/js/src/vm/JSONParser.cpp
|
|
|
++++ b/js/src/vm/JSONParser.cpp
|
|
|
+@@ -24,54 +24,59 @@
|
|
|
+ using namespace js;
|
|
|
+
|
|
|
+ using mozilla::IsAsciiDigit;
|
|
|
+ using mozilla::RangedPtr;
|
|
|
+
|
|
|
+ JSONParserBase::~JSONParserBase()
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < stack.length(); i++) {
|
|
|
+- if (stack[i].state == FinishArrayElement)
|
|
|
++ if (stack[i].state == FinishArrayElement) {
|
|
|
+ js_delete(&stack[i].elements());
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ js_delete(&stack[i].properties());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- for (size_t i = 0; i < freeElements.length(); i++)
|
|
|
++ for (size_t i = 0; i < freeElements.length(); i++) {
|
|
|
+ js_delete(freeElements[i]);
|
|
|
++ }
|
|
|
+
|
|
|
+- for (size_t i = 0; i < freeProperties.length(); i++)
|
|
|
++ for (size_t i = 0; i < freeProperties.length(); i++) {
|
|
|
+ js_delete(freeProperties[i]);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONParserBase::trace(JSTracer* trc)
|
|
|
+ {
|
|
|
+ for (auto& elem : stack) {
|
|
|
+- if (elem.state == FinishArrayElement)
|
|
|
++ if (elem.state == FinishArrayElement) {
|
|
|
+ elem.elements().trace(trc);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ elem.properties().trace(trc);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ void
|
|
|
+ JSONParser<CharT>::getTextPosition(uint32_t* column, uint32_t* line)
|
|
|
+ {
|
|
|
+ CharPtr ptr = begin;
|
|
|
+ uint32_t col = 1;
|
|
|
+ uint32_t row = 1;
|
|
|
+ for (; ptr < current; ptr++) {
|
|
|
+ if (*ptr == '\n' || *ptr == '\r') {
|
|
|
+ ++row;
|
|
|
+ col = 1;
|
|
|
+ // \r\n is treated as a single newline.
|
|
|
+- if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n')
|
|
|
++ if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n') {
|
|
|
+ ++ptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ ++col;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *column = col;
|
|
|
+ *line = row;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -125,61 +130,67 @@ JSONParser<CharT>::readString()
|
|
|
+ CharPtr start = current;
|
|
|
+ for (; current < end; current++) {
|
|
|
+ if (*current == '"') {
|
|
|
+ size_t length = current - start;
|
|
|
+ current++;
|
|
|
+ JSFlatString* str = (ST == JSONParser::PropertyName)
|
|
|
+ ? AtomizeChars(cx, start.get(), length)
|
|
|
+ : NewStringCopyN<CanGC>(cx, start.get(), length);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+ return stringToken(str);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (*current == '\\')
|
|
|
++ if (*current == '\\') {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (*current <= 0x001F) {
|
|
|
+ error("bad control character in string literal");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Slow case: string contains escaped characters. Copy a maximal sequence
|
|
|
+ * of unescaped characters into a temporary buffer, then an escaped
|
|
|
+ * character, and repeat until the entire string is consumed.
|
|
|
+ */
|
|
|
+ StringBuffer buffer(cx);
|
|
|
+ do {
|
|
|
+- if (start < current && !buffer.append(start.get(), current.get()))
|
|
|
++ if (start < current && !buffer.append(start.get(), current.get())) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+
|
|
|
+- if (current >= end)
|
|
|
++ if (current >= end) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ char16_t c = *current++;
|
|
|
+ if (c == '"') {
|
|
|
+ JSFlatString* str = (ST == JSONParser::PropertyName)
|
|
|
+ ? buffer.finishAtom()
|
|
|
+ : buffer.finishString();
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+ return stringToken(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (c != '\\') {
|
|
|
+ --current;
|
|
|
+ error("bad character in string literal");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (current >= end)
|
|
|
++ if (current >= end) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ switch (*current++) {
|
|
|
+ case '"': c = '"'; break;
|
|
|
+ case '/': c = '/'; break;
|
|
|
+ case '\\': c = '\\'; break;
|
|
|
+ case 'b': c = '\b'; break;
|
|
|
+ case 'f': c = '\f'; break;
|
|
|
+ case 'n': c = '\n'; break;
|
|
|
+@@ -190,49 +201,52 @@ JSONParser<CharT>::readString()
|
|
|
+ if (end - current < 4 ||
|
|
|
+ !(JS7_ISHEX(current[0]) &&
|
|
|
+ JS7_ISHEX(current[1]) &&
|
|
|
+ JS7_ISHEX(current[2]) &&
|
|
|
+ JS7_ISHEX(current[3])))
|
|
|
+ {
|
|
|
+ // Point to the first non-hexadecimal character (which may be
|
|
|
+ // missing).
|
|
|
+- if (current == end || !JS7_ISHEX(current[0]))
|
|
|
++ if (current == end || !JS7_ISHEX(current[0])) {
|
|
|
+ ; // already at correct location
|
|
|
+- else if (current + 1 == end || !JS7_ISHEX(current[1]))
|
|
|
++ } else if (current + 1 == end || !JS7_ISHEX(current[1])) {
|
|
|
+ current += 1;
|
|
|
+- else if (current + 2 == end || !JS7_ISHEX(current[2]))
|
|
|
++ } else if (current + 2 == end || !JS7_ISHEX(current[2])) {
|
|
|
+ current += 2;
|
|
|
+- else if (current + 3 == end || !JS7_ISHEX(current[3]))
|
|
|
++ } else if (current + 3 == end || !JS7_ISHEX(current[3])) {
|
|
|
+ current += 3;
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_CRASH("logic error determining first erroneous character");
|
|
|
++ }
|
|
|
+
|
|
|
+ error("bad Unicode escape");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ c = (JS7_UNHEX(current[0]) << 12)
|
|
|
+ | (JS7_UNHEX(current[1]) << 8)
|
|
|
+ | (JS7_UNHEX(current[2]) << 4)
|
|
|
+ | (JS7_UNHEX(current[3]));
|
|
|
+ current += 4;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ current--;
|
|
|
+ error("bad escaped character");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+- if (!buffer.append(c))
|
|
|
++ if (!buffer.append(c)) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+
|
|
|
+ start = current;
|
|
|
+ for (; current < end; current++) {
|
|
|
+- if (*current == '"' || *current == '\\' || *current <= 0x001F)
|
|
|
++ if (*current == '"' || *current == '\\' || *current <= 0x001F) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ } while (current < end);
|
|
|
+
|
|
|
+ error("unterminated string");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+@@ -259,52 +273,55 @@ JSONParser<CharT>::readNumber()
|
|
|
+
|
|
|
+ /* 0|[1-9][0-9]+ */
|
|
|
+ if (!IsAsciiDigit(*current)) {
|
|
|
+ error("unexpected non-digit");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ if (*current++ != '0') {
|
|
|
+ for (; current < end; current++) {
|
|
|
+- if (!IsAsciiDigit(*current))
|
|
|
++ if (!IsAsciiDigit(*current)) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Fast path: no fractional or exponent part. */
|
|
|
+ if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
|
|
|
+ mozilla::Range<const CharT> chars(digitStart.get(), current - digitStart);
|
|
|
+ if (chars.length() < strlen("9007199254740992")) {
|
|
|
+ // If the decimal number is shorter than the length of 2**53, (the
|
|
|
+ // largest number a double can represent with integral precision),
|
|
|
+ // parse it using a decimal-only parser. This comparison is
|
|
|
+ // conservative but faster than a fully-precise check.
|
|
|
+ double d = ParseDecimalNumber(chars);
|
|
|
+ return numberToken(negative ? -d : d);
|
|
|
+ }
|
|
|
+
|
|
|
+ double d;
|
|
|
+- if (!GetFullInteger(cx, digitStart.get(), current.get(), 10, &d))
|
|
|
++ if (!GetFullInteger(cx, digitStart.get(), current.get(), 10, &d)) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+ return numberToken(negative ? -d : d);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* (\.[0-9]+)? */
|
|
|
+ if (current < end && *current == '.') {
|
|
|
+ if (++current == end) {
|
|
|
+ error("missing digits after decimal point");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ if (!IsAsciiDigit(*current)) {
|
|
|
+ error("unterminated fractional number");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ while (++current < end) {
|
|
|
+- if (!IsAsciiDigit(*current))
|
|
|
++ if (!IsAsciiDigit(*current)) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ([eE][\+\-]?[0-9]+)? */
|
|
|
+ if (current < end && (*current == 'e' || *current == 'E')) {
|
|
|
+ if (++current == end) {
|
|
|
+ error("missing digits after exponent indicator");
|
|
|
+ return token(Error);
|
|
|
+@@ -315,39 +332,42 @@ JSONParser<CharT>::readNumber()
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!IsAsciiDigit(*current)) {
|
|
|
+ error("exponent part is missing a number");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+ while (++current < end) {
|
|
|
+- if (!IsAsciiDigit(*current))
|
|
|
++ if (!IsAsciiDigit(*current)) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ double d;
|
|
|
+- if (!FullStringToDouble(cx, digitStart.get(), current.get(), &d))
|
|
|
++ if (!FullStringToDouble(cx, digitStart.get(), current.get(), &d)) {
|
|
|
+ return token(OOM);
|
|
|
++ }
|
|
|
+ return numberToken(negative ? -d : d);
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline bool
|
|
|
+ IsJSONWhitespace(char16_t c)
|
|
|
+ {
|
|
|
+ return c == '\t' || c == '\r' || c == '\n' || c == ' ';
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advance()
|
|
|
+ {
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("unexpected end of data");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (*current) {
|
|
|
+ case '"':
|
|
|
+ return readString<LiteralValue>();
|
|
|
+@@ -420,25 +440,27 @@ JSONParser<CharT>::advance()
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advanceAfterObjectOpen()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(current[-1] == '{');
|
|
|
+
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("end of data while reading object contents");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (*current == '"')
|
|
|
++ if (*current == '"') {
|
|
|
+ return readString<PropertyName>();
|
|
|
++ }
|
|
|
+
|
|
|
+ if (*current == '}') {
|
|
|
+ current++;
|
|
|
+ return token(ObjectClose);
|
|
|
+ }
|
|
|
+
|
|
|
+ error("expected property name or '}'");
|
|
|
+ return token(Error);
|
|
|
+@@ -473,18 +495,19 @@ AssertPastValue(const RangedPtr<const Ch
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advanceAfterArrayElement()
|
|
|
+ {
|
|
|
+ AssertPastValue(current);
|
|
|
+
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("end of data when ',' or ']' was expected");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*current == ',') {
|
|
|
+ current++;
|
|
|
+ return token(Comma);
|
|
|
+@@ -500,38 +523,41 @@ JSONParser<CharT>::advanceAfterArrayElem
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advancePropertyName()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(current[-1] == ',');
|
|
|
+
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("end of data when property name was expected");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (*current == '"')
|
|
|
++ if (*current == '"') {
|
|
|
+ return readString<PropertyName>();
|
|
|
++ }
|
|
|
+
|
|
|
+ error("expected double-quoted property name");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advancePropertyColon()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(current[-1] == '"');
|
|
|
+
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("end of data after property name when ':' was expected");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*current == ':') {
|
|
|
+ current++;
|
|
|
+ return token(Colon);
|
|
|
+@@ -542,18 +568,19 @@ JSONParser<CharT>::advancePropertyColon(
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ JSONParserBase::Token
|
|
|
+ JSONParser<CharT>::advanceAfterProperty()
|
|
|
+ {
|
|
|
+ AssertPastValue(current);
|
|
|
+
|
|
|
+- while (current < end && IsJSONWhitespace(*current))
|
|
|
++ while (current < end && IsJSONWhitespace(*current)) {
|
|
|
+ current++;
|
|
|
++ }
|
|
|
+ if (current >= end) {
|
|
|
+ error("end of data after property value in object");
|
|
|
+ return token(Error);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*current == ',') {
|
|
|
+ current++;
|
|
|
+ return token(Comma);
|
|
|
+@@ -569,52 +596,58 @@ JSONParser<CharT>::advanceAfterProperty(
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector& properties)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(&properties == &stack.back().properties());
|
|
|
+
|
|
|
+ JSObject* obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), GenericObject);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ vp.setObject(*obj);
|
|
|
+- if (!freeProperties.append(&properties))
|
|
|
++ if (!freeProperties.append(&properties)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ stack.popBack();
|
|
|
+
|
|
|
+ if (!stack.empty() && stack.back().state == FinishArrayElement) {
|
|
|
+ const ElementVector& elements = stack.back().elements();
|
|
|
+- if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length()))
|
|
|
++ if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(&elements == &stack.back().elements());
|
|
|
+
|
|
|
+ ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
|
|
|
+ GenericObject);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ vp.setObject(*obj);
|
|
|
+- if (!freeElements.append(&elements))
|
|
|
++ if (!freeElements.append(&elements)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ stack.popBack();
|
|
|
+
|
|
|
+ if (!stack.empty() && stack.back().state == FinishArrayElement) {
|
|
|
+ const ElementVector& elements = stack.back().elements();
|
|
|
+- if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length()))
|
|
|
++ if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename CharT>
|
|
|
+ bool
|
|
|
+ JSONParser<CharT>::parse(MutableHandleValue vp)
|
|
|
+@@ -629,60 +662,69 @@ JSONParser<CharT>::parse(MutableHandleVa
|
|
|
+ while (true) {
|
|
|
+ switch (state) {
|
|
|
+ case FinishObjectMember: {
|
|
|
+ PropertyVector& properties = stack.back().properties();
|
|
|
+ properties.back().value = value;
|
|
|
+
|
|
|
+ token = advanceAfterProperty();
|
|
|
+ if (token == ObjectClose) {
|
|
|
+- if (!finishObject(&value, properties))
|
|
|
++ if (!finishObject(&value, properties)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (token != Comma) {
|
|
|
+- if (token == OOM)
|
|
|
++ if (token == OOM) {
|
|
|
+ return false;
|
|
|
+- if (token != Error)
|
|
|
++ }
|
|
|
++ if (token != Error) {
|
|
|
+ error("expected ',' or '}' after property-value pair in object literal");
|
|
|
++ }
|
|
|
+ return errorReturn();
|
|
|
+ }
|
|
|
+ token = advancePropertyName();
|
|
|
+ /* FALL THROUGH */
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONMember:
|
|
|
+ if (token == String) {
|
|
|
+ jsid id = AtomToId(atomValue());
|
|
|
+ PropertyVector& properties = stack.back().properties();
|
|
|
+- if (!properties.append(IdValuePair(id)))
|
|
|
++ if (!properties.append(IdValuePair(id))) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ token = advancePropertyColon();
|
|
|
+ if (token != Colon) {
|
|
|
+ MOZ_ASSERT(token == Error);
|
|
|
+ return errorReturn();
|
|
|
+ }
|
|
|
+ goto JSONValue;
|
|
|
+ }
|
|
|
+- if (token == OOM)
|
|
|
++ if (token == OOM) {
|
|
|
+ return false;
|
|
|
+- if (token != Error)
|
|
|
++ }
|
|
|
++ if (token != Error) {
|
|
|
+ error("property names must be double-quoted strings");
|
|
|
++ }
|
|
|
+ return errorReturn();
|
|
|
+
|
|
|
+ case FinishArrayElement: {
|
|
|
+ ElementVector& elements = stack.back().elements();
|
|
|
+- if (!elements.append(value.get()))
|
|
|
++ if (!elements.append(value.get())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ token = advanceAfterArrayElement();
|
|
|
+- if (token == Comma)
|
|
|
++ if (token == Comma) {
|
|
|
+ goto JSONValue;
|
|
|
++ }
|
|
|
+ if (token == ArrayClose) {
|
|
|
+- if (!finishArray(&value, elements))
|
|
|
++ if (!finishArray(&value, elements)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ MOZ_ASSERT(token == Error);
|
|
|
+ return errorReturn();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONValue:
|
|
|
+ case JSONValue:
|
|
|
+@@ -707,52 +749,56 @@ JSONParser<CharT>::parse(MutableHandleVa
|
|
|
+
|
|
|
+ case ArrayOpen: {
|
|
|
+ ElementVector* elements;
|
|
|
+ if (!freeElements.empty()) {
|
|
|
+ elements = freeElements.popCopy();
|
|
|
+ elements->clear();
|
|
|
+ } else {
|
|
|
+ elements = cx->new_<ElementVector>(cx);
|
|
|
+- if (!elements)
|
|
|
++ if (!elements) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (!stack.append(elements)) {
|
|
|
+ js_delete(elements);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ token = advance();
|
|
|
+ if (token == ArrayClose) {
|
|
|
+- if (!finishArray(&value, *elements))
|
|
|
++ if (!finishArray(&value, *elements)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ goto JSONValueSwitch;
|
|
|
+ }
|
|
|
+
|
|
|
+ case ObjectOpen: {
|
|
|
+ PropertyVector* properties;
|
|
|
+ if (!freeProperties.empty()) {
|
|
|
+ properties = freeProperties.popCopy();
|
|
|
+ properties->clear();
|
|
|
+ } else {
|
|
|
+ properties = cx->new_<PropertyVector>(cx);
|
|
|
+- if (!properties)
|
|
|
++ if (!properties) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (!stack.append(properties)) {
|
|
|
+ js_delete(properties);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ token = advanceAfterObjectOpen();
|
|
|
+ if (token == ObjectClose) {
|
|
|
+- if (!finishObject(&value, *properties))
|
|
|
++ if (!finishObject(&value, *properties)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ goto JSONMember;
|
|
|
+ }
|
|
|
+
|
|
|
+ case ArrayClose:
|
|
|
+ case ObjectClose:
|
|
|
+ case Colon:
|
|
|
+@@ -767,18 +813,19 @@ JSONParser<CharT>::parse(MutableHandleVa
|
|
|
+ return false;
|
|
|
+
|
|
|
+ case Error:
|
|
|
+ return errorReturn();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (stack.empty())
|
|
|
++ if (stack.empty()) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ state = stack.back().state;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (; current < end; current++) {
|
|
|
+ if (!IsJSONWhitespace(*current)) {
|
|
|
+ error("unexpected non-whitespace character after JSON data");
|
|
|
+ return errorReturn();
|
|
|
+ }
|
|
|
+diff --git a/js/src/vm/JSONPrinter.cpp b/js/src/vm/JSONPrinter.cpp
|
|
|
+--- a/js/src/vm/JSONPrinter.cpp
|
|
|
++++ b/js/src/vm/JSONPrinter.cpp
|
|
|
+@@ -13,34 +13,37 @@
|
|
|
+ #include <stdarg.h>
|
|
|
+
|
|
|
+ #include "util/DoubleToString.h"
|
|
|
+
|
|
|
+ using namespace js;
|
|
|
+
|
|
|
+ JSONPrinter::~JSONPrinter()
|
|
|
+ {
|
|
|
+- if (dtoaState_)
|
|
|
++ if (dtoaState_) {
|
|
|
+ DestroyDtoaState(dtoaState_);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::indent()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(indentLevel_ >= 0);
|
|
|
+ out_.printf("\n");
|
|
|
+- for (int i = 0; i < indentLevel_; i++)
|
|
|
++ for (int i = 0; i < indentLevel_; i++) {
|
|
|
+ out_.printf(" ");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::propertyName(const char* name)
|
|
|
+ {
|
|
|
+- if (!first_)
|
|
|
++ if (!first_) {
|
|
|
+ out_.printf(",");
|
|
|
++ }
|
|
|
+ indent();
|
|
|
+ out_.printf("\"%s\":", name);
|
|
|
+ first_ = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::beginObject()
|
|
|
+ {
|
|
|
+@@ -105,18 +108,19 @@ JSONPrinter::formatProperty(const char*
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::value(const char* format, ...)
|
|
|
+ {
|
|
|
+ va_list ap;
|
|
|
+ va_start(ap, format);
|
|
|
+
|
|
|
+- if (!first_)
|
|
|
++ if (!first_) {
|
|
|
+ out_.printf(",");
|
|
|
++ }
|
|
|
+ out_.printf("\"");
|
|
|
+ out_.vprintf(format, ap);
|
|
|
+ out_.printf("\"");
|
|
|
+
|
|
|
+ va_end(ap);
|
|
|
+ first_ = false;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -125,18 +129,19 @@ JSONPrinter::property(const char* name,
|
|
|
+ {
|
|
|
+ propertyName(name);
|
|
|
+ out_.printf("%" PRId32, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::value(int val)
|
|
|
+ {
|
|
|
+- if (!first_)
|
|
|
++ if (!first_) {
|
|
|
+ out_.printf(",");
|
|
|
++ }
|
|
|
+ out_.printf("%d", val);
|
|
|
+ first_ = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSONPrinter::property(const char* name, uint32_t value)
|
|
|
+ {
|
|
|
+ propertyName(name);
|
|
|
+diff --git a/js/src/vm/JSObject-inl.h b/js/src/vm/JSObject-inl.h
|
|
|
+--- a/js/src/vm/JSObject-inl.h
|
|
|
++++ b/js/src/vm/JSObject-inl.h
|
|
|
+@@ -34,18 +34,19 @@
|
|
|
+ #include "vm/TypeInference-inl.h"
|
|
|
+
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ // This is needed here for ensureShape() below.
|
|
|
+ inline bool
|
|
|
+ MaybeConvertUnboxedObjectToNative(JSContext* cx, JSObject* obj)
|
|
|
+ {
|
|
|
+- if (obj->is<UnboxedPlainObject>())
|
|
|
++ if (obj->is<UnboxedPlainObject>()) {
|
|
|
+ return UnboxedPlainObject::convertToNative(cx, obj);
|
|
|
++ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static MOZ_ALWAYS_INLINE bool
|
|
|
+ ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
|
|
|
+
|
|
|
+@@ -54,39 +55,42 @@ ClassMayResolveId(const JSAtomState& nam
|
|
|
+ // resolve hook.
|
|
|
+ MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
|
|
|
+ // Tell the analysis our mayResolve hooks won't trigger GC.
|
|
|
+ JS::AutoSuppressGCAnalysis nogc;
|
|
|
+- if (!mayResolve(names, id, maybeObj))
|
|
|
++ if (!mayResolve(names, id, maybeObj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ } // namespace js
|
|
|
+
|
|
|
+ inline js::Shape*
|
|
|
+ JSObject::maybeShape() const
|
|
|
+ {
|
|
|
+- if (!is<js::ShapedObject>())
|
|
|
++ if (!is<js::ShapedObject>()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return as<js::ShapedObject>().shape();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::Shape*
|
|
|
+ JSObject::ensureShape(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (!js::MaybeConvertUnboxedObjectToNative(cx, this))
|
|
|
++ if (!js::MaybeConvertUnboxedObjectToNative(cx, this)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ js::Shape* shape = maybeShape();
|
|
|
+ MOZ_ASSERT(shape);
|
|
|
+ return shape;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSObject::finalize(js::FreeOp* fop)
|
|
|
+ {
|
|
|
+@@ -97,26 +101,30 @@ JSObject::finalize(js::FreeOp* fop)
|
|
|
+ if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
|
|
|
+ /* Assert we're on the main thread. */
|
|
|
+ MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ const js::Class* clasp = getClass();
|
|
|
+ js::NativeObject* nobj = nullptr;
|
|
|
+- if (clasp->isNative())
|
|
|
++ if (clasp->isNative()) {
|
|
|
+ nobj = &as<js::NativeObject>();
|
|
|
+- if (clasp->hasFinalize())
|
|
|
++ }
|
|
|
++ if (clasp->hasFinalize()) {
|
|
|
+ clasp->doFinalize(fop, this);
|
|
|
++ }
|
|
|
+
|
|
|
+- if (!nobj)
|
|
|
++ if (!nobj) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (nobj->hasDynamicSlots())
|
|
|
++ if (nobj->hasDynamicSlots()) {
|
|
|
+ fop->free_(nobj->slots_);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (nobj->hasDynamicElements()) {
|
|
|
+ js::ObjectElements* elements = nobj->getElementsHeader();
|
|
|
+ if (elements->isCopyOnWrite()) {
|
|
|
+ if (elements->ownerObject() == this) {
|
|
|
+ // Don't free the elements until object finalization finishes,
|
|
|
+ // so that other objects can access these elements while they
|
|
|
+ // are themselves finalized.
|
|
|
+@@ -133,52 +141,56 @@ JSObject::finalize(js::FreeOp* fop)
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE void
|
|
|
+ js::NativeObject::sweepDictionaryListPointer()
|
|
|
+ {
|
|
|
+ // For dictionary objects (which must be native), it's possible that
|
|
|
+ // unreachable shapes may be marked whose listp points into this object. In
|
|
|
+ // case this happens, null out the shape's pointer so that a moving GC will
|
|
|
+ // not try to access the dead object.
|
|
|
+- if (shape()->listp == shapePtr())
|
|
|
++ if (shape()->listp == shapePtr()) {
|
|
|
+ shape()->listp = nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE void
|
|
|
+ js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(this == Forwarded(old));
|
|
|
+
|
|
|
+ // Dictionary objects can be allocated in the nursery and when they are
|
|
|
+ // tenured the shape's pointer into the object needs to be updated.
|
|
|
+- if (shape()->listp == old->shapePtr())
|
|
|
++ if (shape()->listp == old->shapePtr()) {
|
|
|
+ shape()->listp = shapePtr();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ inline bool
|
|
|
+ JSObject::setSingleton(JSContext* cx, js::HandleObject obj)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!IsInsideNursery(obj));
|
|
|
+
|
|
|
+ js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->group_, obj->getClass(),
|
|
|
+ obj->taggedProto());
|
|
|
+- if (!group)
|
|
|
++ if (!group) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ obj->group_ = group;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ inline js::ObjectGroup*
|
|
|
+ JSObject::getGroup(JSContext* cx, js::HandleObject obj)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(cx->compartment() == obj->compartment());
|
|
|
+ if (obj->hasLazyGroup()) {
|
|
|
+- if (cx->compartment() != obj->compartment())
|
|
|
++ if (cx->compartment() != obj->compartment()) {
|
|
|
+ MOZ_CRASH();
|
|
|
++ }
|
|
|
+ return makeLazyGroup(cx, obj);
|
|
|
+ }
|
|
|
+ return obj->group_;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSObject::setGroup(js::ObjectGroup* group)
|
|
|
+ {
|
|
|
+@@ -228,89 +240,96 @@ js::HasProperty(JSContext* cx, HandleObj
|
|
|
+ return HasProperty(cx, obj, id, found);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ js::GetElement(JSContext* cx, HandleObject obj, HandleValue receiver, uint32_t index,
|
|
|
+ MutableHandleValue vp)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!IndexToId(cx, index, &id))
|
|
|
++ if (!IndexToId(cx, index, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return GetProperty(cx, obj, receiver, id, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ js::GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
|
|
|
+ MutableHandleValue vp)
|
|
|
+ {
|
|
|
+ RootedValue receiverValue(cx, ObjectValue(*receiver));
|
|
|
+ return GetElement(cx, obj, receiverValue, index, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ js::GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp)
|
|
|
+ {
|
|
|
+- if (obj->getOpsGetProperty())
|
|
|
++ if (obj->getOpsGetProperty()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+- if (index > JSID_INT_MAX)
|
|
|
++ if (index > JSID_INT_MAX) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return GetPropertyNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ js::DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
|
|
|
+ {
|
|
|
+ MarkTypePropertyNonData(cx, obj, id);
|
|
|
+- if (DeletePropertyOp op = obj->getOpsDeleteProperty())
|
|
|
++ if (DeletePropertyOp op = obj->getOpsDeleteProperty()) {
|
|
|
+ return op(cx, obj, id, result);
|
|
|
++ }
|
|
|
+ return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ js::DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!IndexToId(cx, index, &id))
|
|
|
++ if (!IndexToId(cx, index, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return DeleteProperty(cx, obj, id, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ js::MaybeHasInterestingSymbolProperty(JSContext* cx, JSObject* obj, Symbol* symbol,
|
|
|
+ JSObject** holder)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(symbol->isInterestingSymbol());
|
|
|
+
|
|
|
+ jsid id = SYMBOL_TO_JSID(symbol);
|
|
|
+ do {
|
|
|
+ if (obj->maybeHasInterestingSymbolProperty() ||
|
|
|
+ MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj)))
|
|
|
+ {
|
|
|
+- if (holder)
|
|
|
++ if (holder) {
|
|
|
+ *holder = obj;
|
|
|
++ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ obj = obj->staticPrototype();
|
|
|
+ } while (obj);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ js::GetInterestingSymbolProperty(JSContext* cx, HandleObject obj, Symbol* sym, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+ JSObject* holder;
|
|
|
+ if (!MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder)) {
|
|
|
+ #ifdef DEBUG
|
|
|
+ RootedValue receiver(cx, ObjectValue(*obj));
|
|
|
+ RootedId id(cx, SYMBOL_TO_JSID(sym));
|
|
|
+- if (!GetProperty(cx, obj, receiver, id, vp))
|
|
|
++ if (!GetProperty(cx, obj, receiver, id, vp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(vp.isUndefined());
|
|
|
+ #endif
|
|
|
+ vp.setUndefined();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObject holderRoot(cx, holder);
|
|
|
+ RootedValue receiver(cx, ObjectValue(*obj));
|
|
|
+@@ -318,35 +337,37 @@ js::GetInterestingSymbolProperty(JSConte
|
|
|
+ return GetProperty(cx, holderRoot, receiver, id, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* * */
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSObject::isQualifiedVarObj() const
|
|
|
+ {
|
|
|
+- if (is<js::DebugEnvironmentProxy>())
|
|
|
++ if (is<js::DebugEnvironmentProxy>()) {
|
|
|
+ return as<js::DebugEnvironmentProxy>().environment().isQualifiedVarObj();
|
|
|
++ }
|
|
|
+ bool rv = hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
|
|
|
+ MOZ_ASSERT_IF(rv,
|
|
|
+ is<js::GlobalObject>() ||
|
|
|
+ is<js::CallObject>() ||
|
|
|
+ is<js::VarEnvironmentObject>() ||
|
|
|
+ is<js::ModuleEnvironmentObject>() ||
|
|
|
+ is<js::NonSyntacticVariablesObject>() ||
|
|
|
+ (is<js::WithEnvironmentObject>() &&
|
|
|
+ !as<js::WithEnvironmentObject>().isSyntactic()));
|
|
|
+ return rv;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSObject::isUnqualifiedVarObj() const
|
|
|
+ {
|
|
|
+- if (is<js::DebugEnvironmentProxy>())
|
|
|
++ if (is<js::DebugEnvironmentProxy>()) {
|
|
|
+ return as<js::DebugEnvironmentProxy>().environment().isUnqualifiedVarObj();
|
|
|
++ }
|
|
|
+ return is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>();
|
|
|
+ }
|
|
|
+
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ ClassCanHaveFixedData(const Class* clasp)
|
|
|
+ {
|
|
|
+@@ -407,18 +428,19 @@ JSObject::nonCCWGlobal() const
|
|
|
+ */
|
|
|
+ return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSObject::hasAllFlags(js::BaseShape::Flag flags) const
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(flags);
|
|
|
+- if (js::Shape* shape = maybeShape())
|
|
|
++ if (js::Shape* shape = maybeShape()) {
|
|
|
+ return shape->hasAllObjectFlags(flags);
|
|
|
++ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSObject::nonProxyIsExtensible() const
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!uninlinedIsProxy());
|
|
|
+
|
|
|
+@@ -447,18 +469,19 @@ JSObject::hasUncacheableProto() const
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ JSObject::maybeHasInterestingSymbolProperty() const
|
|
|
+ {
|
|
|
+ const js::NativeObject* nobj;
|
|
|
+ if (isNative()) {
|
|
|
+ nobj = &as<js::NativeObject>();
|
|
|
+ } else if (is<js::UnboxedPlainObject>()) {
|
|
|
+ nobj = as<js::UnboxedPlainObject>().maybeExpando();
|
|
|
+- if (!nobj)
|
|
|
++ if (!nobj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return nobj->hasInterestingSymbol();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+@@ -526,18 +549,19 @@ IsNativeFunction(const JSObject* obj, JS
|
|
|
+
|
|
|
+ // Return whether looking up a method on 'obj' definitely resolves to the
|
|
|
+ // original specified native function. The method may conservatively return
|
|
|
+ // 'false' in the case of proxies or other non-native objects.
|
|
|
+ static MOZ_ALWAYS_INLINE bool
|
|
|
+ HasNativeMethodPure(JSObject* obj, PropertyName* name, JSNative native, JSContext* cx)
|
|
|
+ {
|
|
|
+ Value v;
|
|
|
+- if (!GetPropertyPure(cx, obj, NameToId(name), &v))
|
|
|
++ if (!GetPropertyPure(cx, obj, NameToId(name), &v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return IsNativeFunction(v, native);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Return whether 'obj' definitely has no @@toPrimitive method.
|
|
|
+ static MOZ_ALWAYS_INLINE bool
|
|
|
+ HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx)
|
|
|
+ {
|
|
|
+@@ -550,31 +574,33 @@ HasNoToPrimitiveMethodPure(JSObject* obj
|
|
|
+ MOZ_ASSERT(LookupPropertyPure(cx, obj, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop));
|
|
|
+ MOZ_ASSERT(!prop);
|
|
|
+ #endif
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject* pobj;
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop))
|
|
|
++ if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return !prop;
|
|
|
+ }
|
|
|
+
|
|
|
+ extern bool
|
|
|
+ ToPropertyKeySlow(JSContext* cx, HandleValue argument, MutableHandleId result);
|
|
|
+
|
|
|
+ /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ ToPropertyKey(JSContext* cx, HandleValue argument, MutableHandleId result)
|
|
|
+ {
|
|
|
+- if (MOZ_LIKELY(argument.isPrimitive()))
|
|
|
++ if (MOZ_LIKELY(argument.isPrimitive())) {
|
|
|
+ return ValueToId<CanGC>(cx, argument, result);
|
|
|
++ }
|
|
|
+
|
|
|
+ return ToPropertyKeySlow(cx, argument, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Return true if this is a compiler-created internal function accessed by
|
|
|
+ * its own object. Such a function object must not be accessible to script
|
|
|
+ * or embedding code.
|
|
|
+@@ -768,26 +794,28 @@ NewObjectWithGroup(JSContext* cx, Handle
|
|
|
+ /*
|
|
|
+ * As for gc::GetGCObjectKind, where numElements is a guess at the final size of
|
|
|
+ * the object, zero if the final size is unknown. This should only be used for
|
|
|
+ * objects that do not require any fixed slots.
|
|
|
+ */
|
|
|
+ static inline gc::AllocKind
|
|
|
+ GuessObjectGCKind(size_t numElements)
|
|
|
+ {
|
|
|
+- if (numElements)
|
|
|
++ if (numElements) {
|
|
|
+ return gc::GetGCObjectKind(numElements);
|
|
|
++ }
|
|
|
+ return gc::AllocKind::OBJECT4;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline gc::AllocKind
|
|
|
+ GuessArrayGCKind(size_t numElements)
|
|
|
+ {
|
|
|
+- if (numElements)
|
|
|
++ if (numElements) {
|
|
|
+ return gc::GetGCArrayKind(numElements);
|
|
|
++ }
|
|
|
+ return gc::AllocKind::OBJECT8;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Returns ESClass::Other if the value isn't an object, or if the object
|
|
|
+ // isn't of one of the enumerated classes. Otherwise returns the appropriate
|
|
|
+ // class.
|
|
|
+ inline bool
|
|
|
+ GetClassOfValue(JSContext* cx, HandleValue v, ESClass* cls)
|
|
|
+@@ -808,18 +836,19 @@ InitClass(JSContext* cx, HandleObject ob
|
|
|
+ const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
|
|
|
+ NativeObject** ctorp = nullptr);
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE const char*
|
|
|
+ GetObjectClassName(JSContext* cx, HandleObject obj)
|
|
|
+ {
|
|
|
+ cx->check(obj);
|
|
|
+
|
|
|
+- if (obj->is<ProxyObject>())
|
|
|
++ if (obj->is<ProxyObject>()) {
|
|
|
+ return Proxy::className(cx, obj);
|
|
|
++ }
|
|
|
+
|
|
|
+ return obj->getClass()->name;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ IsCallable(const Value& v)
|
|
|
+ {
|
|
|
+ return v.isObject() && v.toObject().isCallable();
|
|
|
+@@ -845,30 +874,32 @@ CreateThis(JSContext* cx, HandleObject c
|
|
|
+ MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
|
|
|
+ thisv.setMagic(JS_UNINITIALIZED_LEXICAL);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(thisv.isMagic(JS_IS_CONSTRUCTING));
|
|
|
+
|
|
|
+ JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ thisv.setObject(*obj);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ } /* namespace js */
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+ JSObject::isCallable() const
|
|
|
+ {
|
|
|
+- if (is<JSFunction>())
|
|
|
++ if (is<JSFunction>()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ if (is<js::ProxyObject>()) {
|
|
|
+ const js::ProxyObject& p = as<js::ProxyObject>();
|
|
|
+ return p.handler()->isCallable(const_cast<JSObject*>(this));
|
|
|
+ }
|
|
|
+ return callHook() != nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE bool
|
|
|
+diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp
|
|
|
+--- a/js/src/vm/JSObject.cpp
|
|
|
++++ b/js/src/vm/JSObject.cpp
|
|
|
+@@ -110,30 +110,37 @@ js::ReportNotObjectWithName(JSContext* c
|
|
|
+ JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_NAME,
|
|
|
+ name, chars);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_PUBLIC_API(const char*)
|
|
|
+ JS::InformalValueTypeName(const Value& v)
|
|
|
+ {
|
|
|
+- if (v.isObject())
|
|
|
++ if (v.isObject()) {
|
|
|
+ return v.toObject().getClass()->name;
|
|
|
+- if (v.isString())
|
|
|
++ }
|
|
|
++ if (v.isString()) {
|
|
|
+ return "string";
|
|
|
+- if (v.isSymbol())
|
|
|
++ }
|
|
|
++ if (v.isSymbol()) {
|
|
|
+ return "symbol";
|
|
|
+- if (v.isNumber())
|
|
|
++ }
|
|
|
++ if (v.isNumber()) {
|
|
|
+ return "number";
|
|
|
+- if (v.isBoolean())
|
|
|
++ }
|
|
|
++ if (v.isBoolean()) {
|
|
|
+ return "boolean";
|
|
|
+- if (v.isNull())
|
|
|
++ }
|
|
|
++ if (v.isNull()) {
|
|
|
+ return "null";
|
|
|
+- if (v.isUndefined())
|
|
|
++ }
|
|
|
++ if (v.isUndefined()) {
|
|
|
+ return "undefined";
|
|
|
++ }
|
|
|
+ return "value";
|
|
|
+ }
|
|
|
+
|
|
|
+ // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor
|
|
|
+ JS_PUBLIC_API(bool)
|
|
|
+ JS::FromPropertyDescriptor(JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+ AssertHeapIsIdle();
|
|
|
+@@ -150,67 +157,76 @@ JS::FromPropertyDescriptor(JSContext* cx
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::FromPropertyDescriptorToObject(JSContext* cx, Handle<PropertyDescriptor> desc,
|
|
|
+ MutableHandleValue vp)
|
|
|
+ {
|
|
|
+ // Step 2-3.
|
|
|
+ RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ const JSAtomState& names = cx->names();
|
|
|
+
|
|
|
+ // Step 4.
|
|
|
+ if (desc.hasValue()) {
|
|
|
+- if (!DefineDataProperty(cx, obj, names.value, desc.value()))
|
|
|
++ if (!DefineDataProperty(cx, obj, names.value, desc.value())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 5.
|
|
|
+ RootedValue v(cx);
|
|
|
+ if (desc.hasWritable()) {
|
|
|
+ v.setBoolean(desc.writable());
|
|
|
+- if (!DefineDataProperty(cx, obj, names.writable, v))
|
|
|
++ if (!DefineDataProperty(cx, obj, names.writable, v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 6.
|
|
|
+ if (desc.hasGetterObject()) {
|
|
|
+- if (JSObject* get = desc.getterObject())
|
|
|
++ if (JSObject* get = desc.getterObject()) {
|
|
|
+ v.setObject(*get);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ v.setUndefined();
|
|
|
+- if (!DefineDataProperty(cx, obj, names.get, v))
|
|
|
++ }
|
|
|
++ if (!DefineDataProperty(cx, obj, names.get, v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 7.
|
|
|
+ if (desc.hasSetterObject()) {
|
|
|
+- if (JSObject* set = desc.setterObject())
|
|
|
++ if (JSObject* set = desc.setterObject()) {
|
|
|
+ v.setObject(*set);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ v.setUndefined();
|
|
|
+- if (!DefineDataProperty(cx, obj, names.set, v))
|
|
|
++ }
|
|
|
++ if (!DefineDataProperty(cx, obj, names.set, v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 8.
|
|
|
+ if (desc.hasEnumerable()) {
|
|
|
+ v.setBoolean(desc.enumerable());
|
|
|
+- if (!DefineDataProperty(cx, obj, names.enumerable, v))
|
|
|
++ if (!DefineDataProperty(cx, obj, names.enumerable, v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 9.
|
|
|
+ if (desc.hasConfigurable()) {
|
|
|
+ v.setBoolean(desc.configurable());
|
|
|
+- if (!DefineDataProperty(cx, obj, names.configurable, v))
|
|
|
++ if (!DefineDataProperty(cx, obj, names.configurable, v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vp.setObject(*obj);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
|
|
|
+@@ -220,55 +236,59 @@ js::GetFirstArgumentAsObject(JSContext*
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
|
|
+ method, "0", "s");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ HandleValue v = args[0];
|
|
|
+ if (!v.isObject()) {
|
|
|
+ UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
|
|
|
+ bytes.get(), "not an object");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ objp.set(&v.toObject());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
|
|
+ bool* foundp)
|
|
|
+ {
|
|
|
+- if (!HasProperty(cx, obj, id, foundp))
|
|
|
++ if (!HasProperty(cx, obj, id, foundp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (!*foundp) {
|
|
|
+ vp.setUndefined();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return GetProperty(cx, obj, obj, id, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
|
|
|
+ MOZ_ASSERT_IF(details, JS::StringIsASCII(details));
|
|
|
+
|
|
|
+ UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (details)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (details) {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get(), details);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get());
|
|
|
++ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*** PropertyDescriptor operations and DefineProperties ******************************************/
|
|
|
+
|
|
|
+ static const char js_getter_str[] = "getter";
|
|
|
+@@ -286,97 +306,110 @@ CheckCallable(JSContext* cx, JSObject* o
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
|
|
|
+ MutableHandle<PropertyDescriptor> desc)
|
|
|
+ {
|
|
|
+ // step 2
|
|
|
+ RootedObject obj(cx, NonNullObjectWithName(cx, "property descriptor", descval));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // step 3
|
|
|
+ desc.clear();
|
|
|
+
|
|
|
+ bool found = false;
|
|
|
+ RootedId id(cx);
|
|
|
+ RootedValue v(cx);
|
|
|
+ unsigned attrs = 0;
|
|
|
+
|
|
|
+ // step 4
|
|
|
+ id = NameToId(cx->names().enumerable);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (found) {
|
|
|
+- if (ToBoolean(v))
|
|
|
++ if (ToBoolean(v)) {
|
|
|
+ attrs |= JSPROP_ENUMERATE;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ attrs |= JSPROP_IGNORE_ENUMERATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 5
|
|
|
+ id = NameToId(cx->names().configurable);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (found) {
|
|
|
+- if (!ToBoolean(v))
|
|
|
++ if (!ToBoolean(v)) {
|
|
|
+ attrs |= JSPROP_PERMANENT;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ attrs |= JSPROP_IGNORE_PERMANENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 6
|
|
|
+ id = NameToId(cx->names().value);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
+- if (found)
|
|
|
++ }
|
|
|
++ if (found) {
|
|
|
+ desc.value().set(v);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ attrs |= JSPROP_IGNORE_VALUE;
|
|
|
++ }
|
|
|
+
|
|
|
+ // step 7
|
|
|
+ id = NameToId(cx->names().writable);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (found) {
|
|
|
+- if (!ToBoolean(v))
|
|
|
++ if (!ToBoolean(v)) {
|
|
|
+ attrs |= JSPROP_READONLY;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ attrs |= JSPROP_IGNORE_READONLY;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 8
|
|
|
+ bool hasGetOrSet;
|
|
|
+ id = NameToId(cx->names().get);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ hasGetOrSet = found;
|
|
|
+ if (found) {
|
|
|
+ if (v.isObject()) {
|
|
|
+- if (checkAccessors)
|
|
|
++ 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,
|
|
|
+ js_getter_str);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ attrs |= JSPROP_GETTER;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 9
|
|
|
+ id = NameToId(cx->names().set);
|
|
|
+- if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
|
|
|
++ if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ hasGetOrSet |= found;
|
|
|
+ if (found) {
|
|
|
+ if (v.isObject()) {
|
|
|
+- if (checkAccessors)
|
|
|
++ 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,
|
|
|
+ js_setter_str);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ attrs |= JSPROP_SETTER;
|
|
|
+ }
|
|
|
+@@ -395,54 +428,61 @@ js::ToPropertyDescriptor(JSContext* cx,
|
|
|
+ desc.setAttributes(attrs);
|
|
|
+ MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ Result<>
|
|
|
+ js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
|
|
|
+ {
|
|
|
+- if (desc.hasGetterObject())
|
|
|
++ if (desc.hasGetterObject()) {
|
|
|
+ MOZ_TRY(CheckCallable(cx, desc.getterObject(), js_getter_str));
|
|
|
+-
|
|
|
+- if (desc.hasSetterObject())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (desc.hasSetterObject()) {
|
|
|
+ MOZ_TRY(CheckCallable(cx, desc.setterObject(), js_setter_str));
|
|
|
++ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
|
|
|
+ {
|
|
|
+ desc.assertValid();
|
|
|
+
|
|
|
+ if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
|
|
|
+- if (!desc.hasWritable())
|
|
|
++ if (!desc.hasWritable()) {
|
|
|
+ desc.attributesRef() |= JSPROP_READONLY;
|
|
|
++ }
|
|
|
+ desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
|
|
|
+ } else {
|
|
|
+- if (!desc.hasGetterObject())
|
|
|
++ if (!desc.hasGetterObject()) {
|
|
|
+ desc.setGetterObject(nullptr);
|
|
|
+- if (!desc.hasSetterObject())
|
|
|
++ }
|
|
|
++ if (!desc.hasSetterObject()) {
|
|
|
+ desc.setSetterObject(nullptr);
|
|
|
++ }
|
|
|
+ desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER;
|
|
|
+ }
|
|
|
+- if (!desc.hasConfigurable())
|
|
|
++ if (!desc.hasConfigurable()) {
|
|
|
+ desc.attributesRef() |= JSPROP_PERMANENT;
|
|
|
++ }
|
|
|
+ desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
|
|
|
+
|
|
|
+ desc.assertComplete();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors,
|
|
|
+ AutoIdVector* ids, MutableHandle<PropertyDescriptorVector> descs)
|
|
|
+ {
|
|
|
+- if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
|
|
|
++ if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedId id(cx);
|
|
|
+ for (size_t i = 0, len = ids->length(); i < len; i++) {
|
|
|
+ id = (*ids)[i];
|
|
|
+ Rooted<PropertyDescriptor> desc(cx);
|
|
|
+ RootedValue v(cx);
|
|
|
+ if (!GetProperty(cx, props, props, id, &v) ||
|
|
|
+ !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
|
|
|
+@@ -455,84 +495,91 @@ js::ReadPropertyDescriptors(JSContext* c
|
|
|
+ }
|
|
|
+
|
|
|
+ /*** Seal and freeze *****************************************************************************/
|
|
|
+
|
|
|
+ static unsigned
|
|
|
+ GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level)
|
|
|
+ {
|
|
|
+ /* Make all attributes permanent; if freezing, make data attributes read-only. */
|
|
|
+- if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
|
|
|
++ if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
|
|
+ return JSPROP_PERMANENT | JSPROP_READONLY;
|
|
|
++ }
|
|
|
+ return JSPROP_PERMANENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */
|
|
|
+ bool
|
|
|
+ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
|
|
|
+ {
|
|
|
+ cx->check(obj);
|
|
|
+
|
|
|
+ // Steps 3-5. (Steps 1-2 are redundant assertions.)
|
|
|
+- if (!PreventExtensions(cx, obj))
|
|
|
++ if (!PreventExtensions(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 6-9, loosely interpreted.
|
|
|
+ if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() &&
|
|
|
+ !obj->is<TypedArrayObject>() && !obj->is<MappedArgumentsObject>())
|
|
|
+ {
|
|
|
+ HandleNativeObject nobj = obj.as<NativeObject>();
|
|
|
+
|
|
|
+ // Seal/freeze non-dictionary objects by constructing a new shape
|
|
|
+ // hierarchy mirroring the original one, which can be shared if many
|
|
|
+ // objects with the same structure are sealed/frozen. If we use the
|
|
|
+ // generic path below then any non-empty object will be converted to
|
|
|
+ // dictionary mode.
|
|
|
+ RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(),
|
|
|
+ nobj->taggedProto(),
|
|
|
+ nobj->numFixedSlots(),
|
|
|
+ nobj->lastProperty()->getObjectFlags()));
|
|
|
+- if (!last)
|
|
|
++ if (!last) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Get an in-order list of the shapes in this object.
|
|
|
+ using ShapeVec = GCVector<Shape*, 8>;
|
|
|
+ Rooted<ShapeVec> shapes(cx, ShapeVec(cx));
|
|
|
+ for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
|
|
|
+- if (!shapes.append(&r.front()))
|
|
|
++ if (!shapes.append(&r.front())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ Reverse(shapes.begin(), shapes.end());
|
|
|
+
|
|
|
+ for (Shape* shape : shapes) {
|
|
|
+ Rooted<StackShape> child(cx, StackShape(shape));
|
|
|
+ child.setAttrs(child.attrs() | GetSealedOrFrozenAttributes(child.attrs(), level));
|
|
|
+
|
|
|
+- if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen)
|
|
|
++ if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen) {
|
|
|
+ MarkTypePropertyNonWritable(cx, nobj, child.get().propid);
|
|
|
++ }
|
|
|
+
|
|
|
+ last = cx->zone()->propertyTree().getChild(cx, last, child);
|
|
|
+- if (!last)
|
|
|
++ if (!last) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
|
|
|
+ MOZ_ALWAYS_TRUE(nobj->setLastProperty(cx, last));
|
|
|
+
|
|
|
+ // Ordinarily ArraySetLength handles this, but we're going behind its back
|
|
|
+ // right now, so we must do this manually.
|
|
|
+ if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
|
|
|
+ MOZ_ASSERT(!nobj->denseElementsAreCopyOnWrite());
|
|
|
+ obj->as<ArrayObject>().setNonWritableLength(cx);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Steps 6-7.
|
|
|
+ AutoIdVector keys(cx);
|
|
|
+- if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys))
|
|
|
++ if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedId id(cx);
|
|
|
+ Rooted<PropertyDescriptor> desc(cx);
|
|
|
+
|
|
|
+ const unsigned AllowConfigure = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
|
|
|
+ JSPROP_IGNORE_VALUE;
|
|
|
+ const unsigned AllowConfigureAndWritable = AllowConfigure & ~JSPROP_IGNORE_READONLY;
|
|
|
+
|
|
|
+@@ -541,87 +588,97 @@ js::SetIntegrityLevel(JSContext* cx, Han
|
|
|
+ id = keys[i];
|
|
|
+
|
|
|
+ if (level == IntegrityLevel::Sealed) {
|
|
|
+ // 8.a.i.
|
|
|
+ desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
|
|
|
+ } else {
|
|
|
+ // 9.a.i-ii.
|
|
|
+ Rooted<PropertyDescriptor> currentDesc(cx);
|
|
|
+- if (!GetOwnPropertyDescriptor(cx, obj, id, ¤tDesc))
|
|
|
++ if (!GetOwnPropertyDescriptor(cx, obj, id, ¤tDesc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // 9.a.iii.
|
|
|
+- if (!currentDesc.object())
|
|
|
++ if (!currentDesc.object()) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+
|
|
|
+ // 9.a.iii.1-2
|
|
|
+- if (currentDesc.isAccessorDescriptor())
|
|
|
++ if (currentDesc.isAccessorDescriptor()) {
|
|
|
+ desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ desc.setAttributes(AllowConfigureAndWritable | JSPROP_PERMANENT | JSPROP_READONLY);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8.a.i-ii. / 9.a.iii.3-4
|
|
|
+- if (!DefineProperty(cx, obj, id, desc))
|
|
|
++ if (!DefineProperty(cx, obj, id, desc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Finally, freeze or seal the dense elements.
|
|
|
+- if (obj->isNative())
|
|
|
++ if (obj->isNative()) {
|
|
|
+ ObjectElements::FreezeOrSeal(cx, &obj->as<NativeObject>(), level);
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ ResolveLazyProperties(JSContext* cx, HandleNativeObject obj)
|
|
|
+ {
|
|
|
+ const Class* clasp = obj->getClass();
|
|
|
+ if (JSEnumerateOp enumerate = clasp->getEnumerate()) {
|
|
|
+- if (!enumerate(cx, obj))
|
|
|
++ if (!enumerate(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (clasp->getNewEnumerate() && clasp->getResolve()) {
|
|
|
+ AutoIdVector properties(cx);
|
|
|
+- if (!clasp->getNewEnumerate()(cx, obj, properties, /* enumerableOnly = */ false))
|
|
|
++ if (!clasp->getNewEnumerate()(cx, obj, properties, /* enumerableOnly = */ false)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedId id(cx);
|
|
|
+ for (size_t i = 0; i < properties.length(); i++) {
|
|
|
+ id = properties[i];
|
|
|
+ bool found;
|
|
|
+- if (!HasOwnProperty(cx, obj, id, &found))
|
|
|
++ if (!HasOwnProperty(cx, obj, id, &found)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ES6 draft rev33 (12 Feb 2015) 7.3.15
|
|
|
+ bool
|
|
|
+ js::TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* result)
|
|
|
+ {
|
|
|
+ // Steps 3-6. (Steps 1-2 are redundant assertions.)
|
|
|
+ bool status;
|
|
|
+- if (!IsExtensible(cx, obj, &status))
|
|
|
++ if (!IsExtensible(cx, obj, &status)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (status) {
|
|
|
+ *result = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fast path for native objects.
|
|
|
+ if (obj->isNative()) {
|
|
|
+ HandleNativeObject nobj = obj.as<NativeObject>();
|
|
|
+
|
|
|
+ // Force lazy properties to be resolved.
|
|
|
+- if (!ResolveLazyProperties(cx, nobj))
|
|
|
++ if (!ResolveLazyProperties(cx, nobj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Typed array elements are non-configurable, writable properties, so
|
|
|
+ // if any elements are present, the typed array cannot be frozen.
|
|
|
+ if (nobj->is<TypedArrayObject>() && nobj->as<TypedArrayObject>().length() > 0 &&
|
|
|
+ level == IntegrityLevel::Frozen)
|
|
|
+ {
|
|
|
+ *result = false;
|
|
|
+ return true;
|
|
|
+@@ -660,32 +717,35 @@ js::TestIntegrityLevel(JSContext* cx, Ha
|
|
|
+ {
|
|
|
+ *result = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Steps 7-8.
|
|
|
+ AutoIdVector props(cx);
|
|
|
+- if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
|
|
|
++ if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 9.
|
|
|
+ RootedId id(cx);
|
|
|
+ Rooted<PropertyDescriptor> desc(cx);
|
|
|
+ for (size_t i = 0, len = props.length(); i < len; i++) {
|
|
|
+ id = props[i];
|
|
|
+
|
|
|
+ // Steps 9.a-b.
|
|
|
+- if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
|
|
|
++ if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 9.c.
|
|
|
+- if (!desc.object())
|
|
|
++ if (!desc.object()) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 9.c.i-ii.
|
|
|
+ if (desc.configurable() ||
|
|
|
+ (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable()))
|
|
|
+ {
|
|
|
+ *result = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -702,20 +762,22 @@ js::TestIntegrityLevel(JSContext* cx, Ha
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Get the GC kind to use for scripted 'new' on the given class.
|
|
|
+ * FIXME bug 547327: estimate the size from the allocation site.
|
|
|
+ */
|
|
|
+ static inline gc::AllocKind
|
|
|
+ NewObjectGCKind(const js::Class* clasp)
|
|
|
+ {
|
|
|
+- if (clasp == &ArrayObject::class_)
|
|
|
++ if (clasp == &ArrayObject::class_) {
|
|
|
+ return gc::AllocKind::OBJECT8;
|
|
|
+- if (clasp == &JSFunction::class_)
|
|
|
++ }
|
|
|
++ if (clasp == &JSFunction::class_) {
|
|
|
+ return gc::AllocKind::OBJECT2;
|
|
|
++ }
|
|
|
+ return gc::AllocKind::OBJECT4;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline JSObject*
|
|
|
+ NewObject(JSContext* cx, HandleObjectGroup group, gc::AllocKind kind,
|
|
|
+ NewObjectKind newKind, uint32_t initialShapeFlags = 0)
|
|
|
+ {
|
|
|
+ const Class* clasp = group->clasp();
|
|
|
+@@ -728,35 +790,37 @@ NewObject(JSContext* cx, HandleObjectGro
|
|
|
+ // enough fixed slots to cover the number of reserved slots in the object,
|
|
|
+ // regardless of the allocation kind specified.
|
|
|
+ size_t nfixed = ClassCanHaveFixedData(clasp)
|
|
|
+ ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
|
|
|
+ : GetGCKindSlots(kind, clasp);
|
|
|
+
|
|
|
+ RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed,
|
|
|
+ initialShapeFlags));
|
|
|
+- if (!shape)
|
|
|
++ if (!shape) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
|
|
|
+
|
|
|
+ JSObject* obj;
|
|
|
+ if (clasp->isJSFunction()) {
|
|
|
+ JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSFunction::create(cx, kind, heap, shape, group));
|
|
|
+ } else if (MOZ_LIKELY(clasp->isNative())) {
|
|
|
+ JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, kind, heap, shape, group));
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(IsTypedObjectClass(clasp));
|
|
|
+ JS_TRY_VAR_OR_RETURN_NULL(cx, obj, TypedObject::create(cx, kind, heap, shape, group));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (newKind == SingletonObject) {
|
|
|
+ RootedObject nobj(cx, obj);
|
|
|
+- if (!JSObject::setSingleton(cx, nobj))
|
|
|
++ if (!JSObject::setSingleton(cx, nobj)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ obj = nobj;
|
|
|
+ }
|
|
|
+
|
|
|
+ probes::CreateObject(cx, obj);
|
|
|
+ return obj;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+@@ -780,37 +844,41 @@ js::NewObjectWithTaggedProtoIsCachable(J
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::NewObjectWithGivenTaggedProto(JSContext* cx, const Class* clasp,
|
|
|
+ Handle<TaggedProto> proto,
|
|
|
+ gc::AllocKind allocKind, NewObjectKind newKind,
|
|
|
+ uint32_t initialShapeFlags)
|
|
|
+ {
|
|
|
+- if (CanBeFinalizedInBackground(allocKind, clasp))
|
|
|
++ if (CanBeFinalizedInBackground(allocKind, clasp)) {
|
|
|
+ allocKind = GetBackgroundAllocKind(allocKind);
|
|
|
++ }
|
|
|
+
|
|
|
+ bool isCachable = NewObjectWithTaggedProtoIsCachable(cx, proto, newKind, clasp);
|
|
|
+ if (isCachable) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
|
|
|
+ JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
|
|
|
+- if (obj)
|
|
|
++ if (obj) {
|
|
|
+ return obj;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr));
|
|
|
+- if (!group)
|
|
|
++ if (!group) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedObject obj(cx, NewObject(cx, group, allocKind, newKind, initialShapeFlags));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
|
|
|
+ cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -824,53 +892,60 @@ NewObjectIsCachable(JSContext* cx, NewOb
|
|
|
+ newKind == GenericObject &&
|
|
|
+ clasp->isNative();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::NewObjectWithClassProtoCommon(JSContext* cx, const Class* clasp, HandleObject protoArg,
|
|
|
+ gc::AllocKind allocKind, NewObjectKind newKind)
|
|
|
+ {
|
|
|
+- if (protoArg)
|
|
|
++ if (protoArg) {
|
|
|
+ return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg), allocKind, newKind);
|
|
|
+-
|
|
|
+- if (CanBeFinalizedInBackground(allocKind, clasp))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (CanBeFinalizedInBackground(allocKind, clasp)) {
|
|
|
+ allocKind = GetBackgroundAllocKind(allocKind);
|
|
|
++ }
|
|
|
+
|
|
|
+ Handle<GlobalObject*> global = cx->global();
|
|
|
+
|
|
|
+ bool isCachable = NewObjectIsCachable(cx, newKind, clasp);
|
|
|
+ if (isCachable) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ if (cache.lookupGlobal(clasp, global, allocKind, &entry)) {
|
|
|
+ gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
|
|
|
+ JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
|
|
|
+- if (obj)
|
|
|
++ if (obj) {
|
|
|
+ return obj;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the appropriate proto for clasp. Built-in classes have a cached
|
|
|
+ // proto on cx->global(); all others get %ObjectPrototype%.
|
|
|
+ JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
|
|
|
+- if (protoKey == JSProto_Null)
|
|
|
++ if (protoKey == JSProto_Null) {
|
|
|
+ protoKey = JSProto_Object;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSObject* proto = GlobalObject::getOrCreatePrototype(cx, protoKey);
|
|
|
+- if (!proto)
|
|
|
++ if (!proto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto)));
|
|
|
+- if (!group)
|
|
|
++ if (!group) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSObject* obj = NewObject(cx, group, allocKind, newKind);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ cache.lookupGlobal(clasp, global, allocKind, &entry);
|
|
|
+ cache.fillGlobal(entry, clasp, global, allocKind, &obj->as<NativeObject>());
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -897,34 +972,37 @@ NewObjectWithGroupIsCachable(JSContext*
|
|
|
+ * Create a plain object with the specified group. This bypasses getNewGroup to
|
|
|
+ * avoid losing creation site information for objects made by scripted 'new'.
|
|
|
+ */
|
|
|
+ JSObject*
|
|
|
+ js::NewObjectWithGroupCommon(JSContext* cx, HandleObjectGroup group,
|
|
|
+ gc::AllocKind allocKind, NewObjectKind newKind)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
|
|
|
+- if (CanBeFinalizedInBackground(allocKind, group->clasp()))
|
|
|
++ if (CanBeFinalizedInBackground(allocKind, group->clasp())) {
|
|
|
+ allocKind = GetBackgroundAllocKind(allocKind);
|
|
|
++ }
|
|
|
+
|
|
|
+ bool isCachable = NewObjectWithGroupIsCachable(cx, group, newKind);
|
|
|
+ if (isCachable) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ if (cache.lookupGroup(group, allocKind, &entry)) {
|
|
|
+ JSObject* obj = cache.newObjectFromHit(cx, entry,
|
|
|
+ GetInitialHeap(newKind, group->clasp()));
|
|
|
+- if (obj)
|
|
|
++ if (obj) {
|
|
|
+ return obj;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject* obj = NewObject(cx, group, allocKind, newKind);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
|
|
|
+ NewObjectCache& cache = cx->caches().newObjectCache;
|
|
|
+ NewObjectCache::EntryIndex entry = -1;
|
|
|
+ cache.lookupGroup(group, allocKind, &entry);
|
|
|
+ cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -933,96 +1011,106 @@ js::NewObjectWithGroupCommon(JSContext*
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj)
|
|
|
+ {
|
|
|
+ jsbytecode* pc;
|
|
|
+ RootedScript script(cx, cx->currentScript(&pc));
|
|
|
+ gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
|
|
|
+ NewObjectKind newKind = GenericObject;
|
|
|
+- if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_))
|
|
|
++ if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_)) {
|
|
|
+ newKind = SingletonObject;
|
|
|
++ }
|
|
|
+ RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (script) {
|
|
|
+ /* Try to specialize the group of the object to the scripted call site. */
|
|
|
+- if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject))
|
|
|
++ if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pobj.set(obj);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee)
|
|
|
+ {
|
|
|
+ RootedObject proto(cx);
|
|
|
+- if (!GetPrototypeFromConstructor(cx, callee, &proto))
|
|
|
++ if (!GetPrototypeFromConstructor(cx, callee, &proto)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ gc::AllocKind kind = NewObjectGCKind(newclasp);
|
|
|
+ return NewObjectWithClassProto(cx, newclasp, proto, kind);
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline JSObject*
|
|
|
+ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group,
|
|
|
+ NewObjectKind newKind)
|
|
|
+ {
|
|
|
+ bool isUnboxed;
|
|
|
+ TypeNewScript* maybeNewScript;
|
|
|
+ {
|
|
|
+ AutoSweepObjectGroup sweep(group);
|
|
|
+ isUnboxed = group->maybeUnboxedLayout(sweep);
|
|
|
+ maybeNewScript = group->newScript(sweep);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (isUnboxed && newKind != SingletonObject)
|
|
|
++ if (isUnboxed && newKind != SingletonObject) {
|
|
|
+ return UnboxedPlainObject::create(cx, group, newKind);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (maybeNewScript) {
|
|
|
+ if (maybeNewScript->analyzed()) {
|
|
|
+ // The definite properties analysis has been performed for this
|
|
|
+ // group, so get the shape and alloc kind to use from the
|
|
|
+ // TypeNewScript's template.
|
|
|
+ RootedPlainObject templateObject(cx, maybeNewScript->templateObject());
|
|
|
+ MOZ_ASSERT(templateObject->group() == group);
|
|
|
+
|
|
|
+ RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
|
|
|
+- if (!res)
|
|
|
++ if (!res) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (newKind == SingletonObject) {
|
|
|
+ Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype()));
|
|
|
+- if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto))
|
|
|
++ if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ res->setGroup(group);
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The initial objects registered with a TypeNewScript can't be in the
|
|
|
+ // nursery.
|
|
|
+- if (newKind == GenericObject)
|
|
|
++ if (newKind == GenericObject) {
|
|
|
+ newKind = TenuredObject;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Not enough objects with this group have been created yet, so make a
|
|
|
+ // plain object and register it with the group. Use the maximum number
|
|
|
+ // of fixed slots, as is also required by the TypeNewScript.
|
|
|
+ gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS);
|
|
|
+ PlainObject* res = NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
|
|
|
+- if (!res)
|
|
|
++ if (!res) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Make sure group->newScript is still there.
|
|
|
+ AutoSweepObjectGroup sweep(group);
|
|
|
+- if (newKind != SingletonObject && group->newScript(sweep))
|
|
|
++ if (newKind != SingletonObject && group->newScript(sweep)) {
|
|
|
+ group->newScript(sweep)->registerNewObject(res);
|
|
|
++ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
|
|
|
+
|
|
|
+ if (newKind == SingletonObject) {
|
|
|
+ Rooted<TaggedProto> protoRoot(cx, group->proto());
|
|
|
+@@ -1035,25 +1123,27 @@ JSObject*
|
|
|
+ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject newTarget,
|
|
|
+ HandleObject proto, NewObjectKind newKind /* = GenericObject */)
|
|
|
+ {
|
|
|
+ RootedObject res(cx);
|
|
|
+
|
|
|
+ if (proto) {
|
|
|
+ RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
|
|
|
+ newTarget));
|
|
|
+- if (!group)
|
|
|
++ if (!group) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ {
|
|
|
+ AutoSweepObjectGroup sweep(group);
|
|
|
+ if (group->newScript(sweep) && !group->newScript(sweep)->analyzed()) {
|
|
|
+ bool regenerate;
|
|
|
+- if (!group->newScript(sweep)->maybeAnalyze(cx, group, ®enerate))
|
|
|
++ if (!group->newScript(sweep)->maybeAnalyze(cx, group, ®enerate)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ if (regenerate) {
|
|
|
+ // The script was analyzed successfully and may have changed
|
|
|
+ // the new type table, so refetch the group.
|
|
|
+ group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
|
|
|
+ newTarget);
|
|
|
+ AutoSweepObjectGroup sweepNewGroup(group);
|
|
|
+ MOZ_ASSERT(group && group->newScript(sweepNewGroup));
|
|
|
+ }
|
|
|
+@@ -1062,41 +1152,44 @@ js::CreateThisForFunctionWithProto(JSCon
|
|
|
+
|
|
|
+ res = CreateThisForFunctionWithGroup(cx, group, newKind);
|
|
|
+ } else {
|
|
|
+ res = NewBuiltinClassInstance<PlainObject>(cx, newKind);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (res) {
|
|
|
+ JSScript* script = JSFunction::getOrCreateScript(cx, callee.as<JSFunction>());
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ TypeScript::SetThis(cx, script, TypeSet::ObjectType(res));
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
|
|
|
+ {
|
|
|
+ RootedValue protov(cx);
|
|
|
+- if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov))
|
|
|
++ if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ proto.set(protov.isObject() ? &protov.toObject() : nullptr);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
|
|
|
+ NewObjectKind newKind)
|
|
|
+ {
|
|
|
+ RootedObject proto(cx);
|
|
|
+- if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
|
|
++ if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind);
|
|
|
+
|
|
|
+ if (obj && newKind == SingletonObject) {
|
|
|
+ RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
|
|
+
|
|
|
+ /* Reshape the singleton before passing it as the 'this' value. */
|
|
|
+ NativeObject::clear(cx, nobj);
|
|
|
+@@ -1117,74 +1210,81 @@ JSObject::nonNativeSetProperty(JSContext
|
|
|
+ return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
|
|
|
+ HandleValue receiver, ObjectOpResult& result)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!IndexToId(cx, index, &id))
|
|
|
++ if (!IndexToId(cx, index, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return nonNativeSetProperty(cx, obj, id, v, receiver, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_FRIEND_API(bool)
|
|
|
+ JS_CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target,
|
|
|
+ HandleObject obj, PropertyCopyBehavior copyBehavior)
|
|
|
+ {
|
|
|
+ // |target| must not be a CCW because we need to enter its realm below and
|
|
|
+ // CCWs are not associated with a single realm.
|
|
|
+ MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
|
|
|
+
|
|
|
+ // |obj| and |cx| are generally not same-compartment with |target| here.
|
|
|
+ cx->check(obj, id);
|
|
|
+ Rooted<PropertyDescriptor> desc(cx);
|
|
|
+
|
|
|
+- if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
|
|
|
++ if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(desc.object());
|
|
|
+
|
|
|
+ // Silently skip JSGetterOp/JSSetterOp-implemented accessors.
|
|
|
+- if (desc.getter() && !desc.hasGetterObject())
|
|
|
++ if (desc.getter() && !desc.hasGetterObject()) {
|
|
|
+ return true;
|
|
|
+- if (desc.setter() && !desc.hasSetterObject())
|
|
|
++ }
|
|
|
++ if (desc.setter() && !desc.hasSetterObject()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (copyBehavior == MakeNonConfigurableIntoConfigurable) {
|
|
|
+ // Mask off the JSPROP_PERMANENT bit.
|
|
|
+ desc.attributesRef() &= ~JSPROP_PERMANENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAutoRealm ar(cx, target);
|
|
|
+ cx->markId(id);
|
|
|
+ RootedId wrappedId(cx, id);
|
|
|
+- if (!cx->compartment()->wrap(cx, &desc))
|
|
|
++ if (!cx->compartment()->wrap(cx, &desc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return DefineProperty(cx, target, wrappedId, desc);
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_FRIEND_API(bool)
|
|
|
+ JS_CopyPropertiesFrom(JSContext* cx, HandleObject target, HandleObject obj)
|
|
|
+ {
|
|
|
+ // Both |obj| and |target| must not be CCWs because we need to enter their
|
|
|
+ // realms below and CCWs are not associated with a single realm.
|
|
|
+ MOZ_ASSERT(!IsCrossCompartmentWrapper(obj));
|
|
|
+ MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
|
|
|
+
|
|
|
+ JSAutoRealm ar(cx, obj);
|
|
|
+
|
|
|
+ AutoIdVector props(cx);
|
|
|
+- if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props))
|
|
|
++ if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < props.length(); ++i) {
|
|
|
+- if (!JS_CopyPropertyFrom(cx, props[i], target, obj))
|
|
|
++ if (!JS_CopyPropertyFrom(cx, props[i], target, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ CopyProxyObject(JSContext* cx, Handle<ProxyObject*> from, Handle<ProxyObject*> to)
|
|
|
+ {
|
|
|
+@@ -1192,28 +1292,30 @@ CopyProxyObject(JSContext* cx, Handle<Pr
|
|
|
+
|
|
|
+ if (from->is<WrapperObject>() &&
|
|
|
+ (Wrapper::wrapperHandler(from)->flags() &
|
|
|
+ Wrapper::CROSS_COMPARTMENT))
|
|
|
+ {
|
|
|
+ to->setCrossCompartmentPrivate(GetProxyPrivate(from));
|
|
|
+ } else {
|
|
|
+ RootedValue v(cx, GetProxyPrivate(from));
|
|
|
+- if (!cx->compartment()->wrap(cx, &v))
|
|
|
++ if (!cx->compartment()->wrap(cx, &v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ to->setSameCompartmentPrivate(v);
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(from->numReservedSlots() == to->numReservedSlots());
|
|
|
+
|
|
|
+ RootedValue v(cx);
|
|
|
+ for (size_t n = 0; n < from->numReservedSlots(); n++) {
|
|
|
+ v = GetProxyReservedSlot(from, n);
|
|
|
+- if (!cx->compartment()->wrap(cx, &v))
|
|
|
++ if (!cx->compartment()->wrap(cx, &v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ SetProxyReservedSlot(to, n, v);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
|
|
|
+@@ -1221,90 +1323,99 @@ js::CloneObject(JSContext* cx, HandleObj
|
|
|
+ if (!obj->isNative() && !obj->is<ProxyObject>()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObject clone(cx);
|
|
|
+ if (obj->isNative()) {
|
|
|
+ clone = NewObjectWithGivenTaggedProto(cx, obj->getClass(), proto);
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (obj->as<NativeObject>().hasPrivate())
|
|
|
++ if (obj->as<NativeObject>().hasPrivate()) {
|
|
|
+ clone->as<NativeObject>().setPrivate(obj->as<NativeObject>().getPrivate());
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ ProxyOptions options;
|
|
|
+ options.setClass(obj->getClass());
|
|
|
+
|
|
|
+ clone = ProxyObject::New(cx, GetProxyHandler(obj), JS::NullHandleValue, proto, options);
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>()))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>())) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ GetScriptArrayObjectElements(HandleArrayObject arr, MutableHandle<GCVector<Value>> values)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!arr->isSingleton());
|
|
|
+ MOZ_ASSERT(!arr->isIndexed());
|
|
|
+
|
|
|
+ size_t length = arr->length();
|
|
|
+- if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
|
|
|
++ if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t initlen = arr->getDenseInitializedLength();
|
|
|
+- for (size_t i = 0; i < initlen; i++)
|
|
|
++ for (size_t i = 0; i < initlen; i++) {
|
|
|
+ values[i].set(arr->getDenseElement(i));
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ GetScriptPlainObjectProperties(HandleObject obj, MutableHandle<IdValueVector> properties)
|
|
|
+ {
|
|
|
+ if (obj->is<PlainObject>()) {
|
|
|
+ PlainObject* nobj = &obj->as<PlainObject>();
|
|
|
+
|
|
|
+- if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
|
|
|
++ if (!properties.appendN(IdValuePair(), nobj->slotSpan())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
|
|
|
+ Shape& shape = r.front();
|
|
|
+ MOZ_ASSERT(shape.isDataDescriptor());
|
|
|
+ uint32_t slot = shape.slot();
|
|
|
+ properties[slot].get().id = shape.propid();
|
|
|
+ properties[slot].get().value = nobj->getSlot(slot);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
|
|
|
+ Value v = nobj->getDenseElement(i);
|
|
|
+- if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
|
|
|
++ if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->is<UnboxedPlainObject>()) {
|
|
|
+ UnboxedPlainObject* nobj = &obj->as<UnboxedPlainObject>();
|
|
|
+
|
|
|
+ const UnboxedLayout& layout = nobj->layout();
|
|
|
+- if (!properties.appendN(IdValuePair(), layout.properties().length()))
|
|
|
++ if (!properties.appendN(IdValuePair(), layout.properties().length())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
|
+ const UnboxedLayout::Property& property = layout.properties()[i];
|
|
|
+ properties[i].get().id = NameToId(property.name);
|
|
|
+ properties[i].get().value = nobj->getValue(property);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+@@ -1314,18 +1425,19 @@ GetScriptPlainObjectProperties(HandleObj
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ DeepCloneValue(JSContext* cx, Value* vp, NewObjectKind newKind)
|
|
|
+ {
|
|
|
+ if (vp->isObject()) {
|
|
|
+ RootedObject obj(cx, &vp->toObject());
|
|
|
+ obj = DeepCloneObjectLiteral(cx, obj, newKind);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ vp->setObject(*obj);
|
|
|
+ } else {
|
|
|
+ cx->markAtomValue(*vp);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+@@ -1335,62 +1447,69 @@ js::DeepCloneObjectLiteral(JSContext* cx
|
|
|
+ MOZ_ASSERT_IF(obj->isSingleton(),
|
|
|
+ cx->realm()->behaviors().getSingletonsAsTemplates());
|
|
|
+ MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
|
|
|
+ obj->is<ArrayObject>());
|
|
|
+ MOZ_ASSERT(newKind != SingletonObject);
|
|
|
+
|
|
|
+ if (obj->is<ArrayObject>()) {
|
|
|
+ Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
|
|
+- if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values))
|
|
|
++ if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Deep clone any elements.
|
|
|
+ for (uint32_t i = 0; i < values.length(); ++i) {
|
|
|
+- if (!DeepCloneValue(cx, values[i].address(), newKind))
|
|
|
++ if (!DeepCloneValue(cx, values[i].address(), newKind)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
|
|
|
+- if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite())
|
|
|
++ if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite()) {
|
|
|
+ arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
|
|
|
++ }
|
|
|
+
|
|
|
+ return ObjectGroup::newArrayObject(cx, values.begin(), values.length(), newKind,
|
|
|
+ arrayKind);
|
|
|
+ }
|
|
|
+
|
|
|
+ Rooted<IdValueVector> properties(cx, IdValueVector(cx));
|
|
|
+- if (!GetScriptPlainObjectProperties(obj, &properties))
|
|
|
++ if (!GetScriptPlainObjectProperties(obj, &properties)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < properties.length(); i++) {
|
|
|
+ cx->markId(properties[i].get().id);
|
|
|
+- if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
|
|
|
++ if (!DeepCloneValue(cx, &properties[i].get().value, newKind)) {
|
|
|
+ return nullptr;
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (obj->isSingleton())
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (obj->isSingleton()) {
|
|
|
+ newKind = SingletonObject;
|
|
|
++ }
|
|
|
+
|
|
|
+ return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
|
|
|
+ HandleNativeObject dst,
|
|
|
+ HandleNativeObject src)
|
|
|
+ {
|
|
|
+ cx->check(src, dst);
|
|
|
+ MOZ_ASSERT(src->getClass() == dst->getClass());
|
|
|
+ MOZ_ASSERT(dst->lastProperty()->getObjectFlags() == 0);
|
|
|
+ MOZ_ASSERT(!src->isSingleton());
|
|
|
+ MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
|
|
|
+
|
|
|
+- if (!dst->ensureElements(cx, src->getDenseInitializedLength()))
|
|
|
++ if (!dst->ensureElements(cx, src->getDenseInitializedLength())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ uint32_t initialized = src->getDenseInitializedLength();
|
|
|
+ for (uint32_t i = 0; i < initialized; ++i) {
|
|
|
+ dst->setDenseInitializedLength(i + 1);
|
|
|
+ dst->initDenseElement(i, src->getDenseElement(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!src->hasPrivate());
|
|
|
+@@ -1398,39 +1517,44 @@ InitializePropertiesFromCompatibleNative
|
|
|
+ if (src->staticPrototype() == dst->staticPrototype()) {
|
|
|
+ shape = src->lastProperty();
|
|
|
+ } else {
|
|
|
+ // We need to generate a new shape for dst that has dst's proto but all
|
|
|
+ // the property information from src. Note that we asserted above that
|
|
|
+ // dst's object flags are 0.
|
|
|
+ shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(),
|
|
|
+ dst->numFixedSlots(), 0);
|
|
|
+- if (!shape)
|
|
|
++ if (!shape) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Get an in-order list of the shapes in the src object.
|
|
|
+ Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
|
|
|
+ for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) {
|
|
|
+- if (!shapes.append(&r.front()))
|
|
|
++ if (!shapes.append(&r.front())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ Reverse(shapes.begin(), shapes.end());
|
|
|
+
|
|
|
+ for (Shape* shapeToClone : shapes) {
|
|
|
+ Rooted<StackShape> child(cx, StackShape(shapeToClone));
|
|
|
+ shape = cx->zone()->propertyTree().getChild(cx, shape, child);
|
|
|
+- if (!shape)
|
|
|
++ if (!shape) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ size_t span = shape->slotSpan();
|
|
|
+- if (!dst->setLastProperty(cx, shape))
|
|
|
++ if (!dst->setLastProperty(cx, shape)) {
|
|
|
+ return false;
|
|
|
+- for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++)
|
|
|
++ }
|
|
|
++ for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++) {
|
|
|
+ dst->setSlot(i, src->getSlot(i));
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_FRIEND_API(bool)
|
|
|
+ JS_InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
|
|
|
+ HandleObject dst,
|
|
|
+ HandleObject src)
|
|
|
+@@ -1464,90 +1588,100 @@ js::XDRObjectLiteral(XDRState<mode>* xdr
|
|
|
+
|
|
|
+ RootedValue tmpValue(cx), tmpIdValue(cx);
|
|
|
+ RootedId tmpId(cx);
|
|
|
+
|
|
|
+ if (isArray) {
|
|
|
+ Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ RootedArrayObject arr(cx, &obj->as<ArrayObject>());
|
|
|
+- if (!GetScriptArrayObjectElements(arr, &values))
|
|
|
++ if (!GetScriptArrayObjectElements(arr, &values)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t initialized;
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ initialized = values.length();
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&initialized));
|
|
|
+- if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized))
|
|
|
++ if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Recursively copy dense elements.
|
|
|
+- for (unsigned i = 0; i < initialized; i++)
|
|
|
++ for (unsigned i = 0; i < initialized; i++) {
|
|
|
+ MOZ_TRY(XDRScriptConst(xdr, values[i]));
|
|
|
++ }
|
|
|
+
|
|
|
+ uint32_t copyOnWrite;
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ copyOnWrite = obj->is<ArrayObject>() &&
|
|
|
+ obj->as<ArrayObject>().denseElementsAreCopyOnWrite();
|
|
|
+ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(©OnWrite));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ ObjectGroup::NewArrayKind arrayKind = copyOnWrite
|
|
|
+ ? ObjectGroup::NewArrayKind::CopyOnWrite
|
|
|
+ : ObjectGroup::NewArrayKind::Normal;
|
|
|
+ obj.set(ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
|
|
|
+ TenuredObject, arrayKind));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Code the properties in the object.
|
|
|
+ Rooted<IdValueVector> properties(cx, IdValueVector(cx));
|
|
|
+- if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(obj, &properties))
|
|
|
++ if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(obj, &properties)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ uint32_t nproperties = properties.length();
|
|
|
+ MOZ_TRY(xdr->codeUint32(&nproperties));
|
|
|
+
|
|
|
+- if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties))
|
|
|
++ if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < nproperties; i++) {
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ tmpIdValue = IdToValue(properties[i].get().id);
|
|
|
+ tmpValue = properties[i].get().value;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(XDRScriptConst(xdr, &tmpIdValue));
|
|
|
+ MOZ_TRY(XDRScriptConst(xdr, &tmpValue));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+- if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId))
|
|
|
++ if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ properties[i].get().id = tmpId;
|
|
|
+ properties[i].get().value = tmpValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Code whether the object is a singleton.
|
|
|
+ uint32_t isSingleton;
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ isSingleton = obj->isSingleton() ? 1 : 0;
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&isSingleton));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
|
|
|
+ obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ template XDRResult
|
|
|
+ js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj);
|
|
|
+
|
|
|
+@@ -1561,63 +1695,68 @@ NativeObject::fillInAfterSwap(JSContext*
|
|
|
+ // This object has just been swapped with some other object, and its shape
|
|
|
+ // no longer reflects its allocated size. Correct this information and
|
|
|
+ // fill the slots in with the specified values.
|
|
|
+ MOZ_ASSERT(obj->slotSpan() == values.length());
|
|
|
+
|
|
|
+ // Make sure the shape's numFixedSlots() is correct.
|
|
|
+ size_t nfixed = gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
|
|
|
+ if (nfixed != obj->shape()->numFixedSlots()) {
|
|
|
+- if (!NativeObject::generateOwnShape(cx, obj))
|
|
|
++ if (!NativeObject::generateOwnShape(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ obj->shape()->setNumFixedSlots(nfixed);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (obj->hasPrivate())
|
|
|
++ if (obj->hasPrivate()) {
|
|
|
+ obj->setPrivate(priv);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_ASSERT(!priv);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (obj->slots_) {
|
|
|
+ js_free(obj->slots_);
|
|
|
+ obj->slots_ = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), obj->getClass())) {
|
|
|
+ obj->slots_ = cx->pod_malloc<HeapSlot>(ndynamic);
|
|
|
+- if (!obj->slots_)
|
|
|
++ if (!obj->slots_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
|
|
|
+ }
|
|
|
+
|
|
|
+ obj->initSlotRange(0, values.begin(), values.length());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSObject::fixDictionaryShapeAfterSwap()
|
|
|
+ {
|
|
|
+ // Dictionary shapes can point back to their containing objects, so after
|
|
|
+ // swapping the guts of those objects fix the pointers up.
|
|
|
+- if (isNative() && as<NativeObject>().inDictionaryMode())
|
|
|
++ if (isNative() && as<NativeObject>().inDictionaryMode()) {
|
|
|
+ as<NativeObject>().shape()->listp = as<NativeObject>().shapePtr();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static MOZ_MUST_USE bool
|
|
|
+ CopyProxyValuesBeforeSwap(JSContext* cx, ProxyObject* proxy, AutoValueVector& values)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(values.empty());
|
|
|
+
|
|
|
+ // Remove the GCPtrValues we're about to swap from the store buffer, to
|
|
|
+ // ensure we don't trace bogus values.
|
|
|
+ gc::StoreBuffer& sb = cx->runtime()->gc.storeBuffer();
|
|
|
+
|
|
|
+ // Reserve space for the private slot and the reserved slots.
|
|
|
+- if (!values.reserve(1 + proxy->numReservedSlots()))
|
|
|
++ if (!values.reserve(1 + proxy->numReservedSlots())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ js::detail::ProxyValueArray* valArray = js::detail::GetProxyDataLayout(proxy)->values();
|
|
|
+ sb.unputValue(&valArray->privateSlot);
|
|
|
+ values.infallibleAppend(valArray->privateSlot);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < proxy->numReservedSlots(); i++) {
|
|
|
+ sb.unputValue(&valArray->reservedSlots.slots[i]);
|
|
|
+ values.infallibleAppend(valArray->reservedSlots.slots[i]);
|
|
|
+@@ -1635,23 +1774,25 @@ ProxyObject::initExternalValueArrayAfter
|
|
|
+
|
|
|
+ // |values| contains the private slot and the reserved slots.
|
|
|
+ MOZ_ASSERT(values.length() == 1 + nreserved);
|
|
|
+
|
|
|
+ size_t nbytes = js::detail::ProxyValueArray::sizeOf(nreserved);
|
|
|
+
|
|
|
+ auto* valArray =
|
|
|
+ reinterpret_cast<js::detail::ProxyValueArray*>(cx->zone()->pod_malloc<uint8_t>(nbytes));
|
|
|
+- if (!valArray)
|
|
|
++ if (!valArray) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ valArray->privateSlot = values[0];
|
|
|
+
|
|
|
+- for (size_t i = 0; i < nreserved; i++)
|
|
|
++ for (size_t i = 0; i < nreserved; i++) {
|
|
|
+ valArray->reservedSlots.slots[i] = values[i + 1];
|
|
|
++ }
|
|
|
+
|
|
|
+ // Note: we allocate external slots iff the proxy had an inline
|
|
|
+ // ProxyValueArray, so at this point reservedSlots points into the
|
|
|
+ // old object and we don't have to free anything.
|
|
|
+ data.reservedSlots = &valArray->reservedSlots;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1664,20 +1805,22 @@ JSObject::swap(JSContext* cx, HandleObje
|
|
|
+ IsBackgroundFinalized(b->asTenured().getAllocKind()));
|
|
|
+ MOZ_ASSERT(a->compartment() == b->compartment());
|
|
|
+
|
|
|
+ // You must have entered the objects' compartment before calling this.
|
|
|
+ MOZ_ASSERT(cx->compartment() == a->compartment());
|
|
|
+
|
|
|
+ AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
|
+
|
|
|
+- if (!JSObject::getGroup(cx, a))
|
|
|
++ if (!JSObject::getGroup(cx, a)) {
|
|
|
+ oomUnsafe.crash("JSObject::swap");
|
|
|
+- if (!JSObject::getGroup(cx, b))
|
|
|
++ }
|
|
|
++ if (!JSObject::getGroup(cx, b)) {
|
|
|
+ oomUnsafe.crash("JSObject::swap");
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Neither object may be in the nursery, but ensure we update any embedded
|
|
|
+ * nursery pointers in either object.
|
|
|
+ */
|
|
|
+ MOZ_ASSERT(!IsInsideNursery(a) && !IsInsideNursery(b));
|
|
|
+ cx->runtime()->gc.storeBuffer().putWholeCell(a);
|
|
|
+ cx->runtime()->gc.storeBuffer().putWholeCell(b);
|
|
|
+@@ -1721,20 +1864,22 @@ JSObject::swap(JSContext* cx, HandleObje
|
|
|
+
|
|
|
+ js_memcpy(tmp, a, size);
|
|
|
+ js_memcpy(a, b, size);
|
|
|
+ js_memcpy(b, tmp, size);
|
|
|
+
|
|
|
+ a->fixDictionaryShapeAfterSwap();
|
|
|
+ b->fixDictionaryShapeAfterSwap();
|
|
|
+
|
|
|
+- if (aIsProxyWithInlineValues)
|
|
|
++ if (aIsProxyWithInlineValues) {
|
|
|
+ b->as<ProxyObject>().setInlineValueArray();
|
|
|
+- if (bIsProxyWithInlineValues)
|
|
|
++ }
|
|
|
++ if (bIsProxyWithInlineValues) {
|
|
|
+ a->as<ProxyObject>().setInlineValueArray();
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ // Avoid GC in here to avoid confusing the tracing code with our
|
|
|
+ // intermediate state.
|
|
|
+ gc::AutoSuppressGC suppress(cx);
|
|
|
+
|
|
|
+ // When the objects have different sizes, they will have different
|
|
|
+ // numbers of fixed slots before and after the swap, so the slots for
|
|
|
+ // native objects will need to be rearranged.
|
|
|
+@@ -1742,67 +1887,75 @@ JSObject::swap(JSContext* cx, HandleObje
|
|
|
+ NativeObject* nb = b->isNative() ? &b->as<NativeObject>() : nullptr;
|
|
|
+
|
|
|
+ // Remember the original values from the objects.
|
|
|
+ AutoValueVector avals(cx);
|
|
|
+ void* apriv = nullptr;
|
|
|
+ if (na) {
|
|
|
+ apriv = na->hasPrivate() ? na->getPrivate() : nullptr;
|
|
|
+ for (size_t i = 0; i < na->slotSpan(); i++) {
|
|
|
+- if (!avals.append(na->getSlot(i)))
|
|
|
++ if (!avals.append(na->getSlot(i))) {
|
|
|
+ oomUnsafe.crash("JSObject::swap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ AutoValueVector bvals(cx);
|
|
|
+ void* bpriv = nullptr;
|
|
|
+ if (nb) {
|
|
|
+ bpriv = nb->hasPrivate() ? nb->getPrivate() : nullptr;
|
|
|
+ for (size_t i = 0; i < nb->slotSpan(); i++) {
|
|
|
+- if (!bvals.append(nb->getSlot(i)))
|
|
|
++ if (!bvals.append(nb->getSlot(i))) {
|
|
|
+ oomUnsafe.crash("JSObject::swap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Do the same for proxies storing ProxyValueArray inline.
|
|
|
+ ProxyObject* proxyA = a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr;
|
|
|
+ ProxyObject* proxyB = b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr;
|
|
|
+
|
|
|
+ if (aIsProxyWithInlineValues) {
|
|
|
+- if (!CopyProxyValuesBeforeSwap(cx, proxyA, avals))
|
|
|
++ if (!CopyProxyValuesBeforeSwap(cx, proxyA, avals)) {
|
|
|
+ oomUnsafe.crash("CopyProxyValuesBeforeSwap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (bIsProxyWithInlineValues) {
|
|
|
+- if (!CopyProxyValuesBeforeSwap(cx, proxyB, bvals))
|
|
|
++ if (!CopyProxyValuesBeforeSwap(cx, proxyB, bvals)) {
|
|
|
+ oomUnsafe.crash("CopyProxyValuesBeforeSwap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Swap the main fields of the objects, whether they are native objects or proxies.
|
|
|
+ char tmp[sizeof(JSObject_Slots0)];
|
|
|
+ js_memcpy(&tmp, a, sizeof tmp);
|
|
|
+ js_memcpy(a, b, sizeof tmp);
|
|
|
+ js_memcpy(b, &tmp, sizeof tmp);
|
|
|
+
|
|
|
+ a->fixDictionaryShapeAfterSwap();
|
|
|
+ b->fixDictionaryShapeAfterSwap();
|
|
|
+
|
|
|
+ if (na) {
|
|
|
+- if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv))
|
|
|
++ if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv)) {
|
|
|
+ oomUnsafe.crash("fillInAfterSwap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (nb) {
|
|
|
+- if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv))
|
|
|
++ if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv)) {
|
|
|
+ oomUnsafe.crash("fillInAfterSwap");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (aIsProxyWithInlineValues) {
|
|
|
+- if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals))
|
|
|
++ if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals)) {
|
|
|
+ oomUnsafe.crash("initExternalValueArray");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (bIsProxyWithInlineValues) {
|
|
|
+- if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals))
|
|
|
++ if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals)) {
|
|
|
+ oomUnsafe.crash("initExternalValueArray");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Swapping the contents of two objects invalidates type sets which contain
|
|
|
+ // either of the objects, so mark all such sets as unknown.
|
|
|
+ MarkObjectGroupUnknownProperties(cx, a->group());
|
|
|
+ MarkObjectGroupUnknownProperties(cx, b->group());
|
|
|
+
|
|
|
+@@ -1822,28 +1975,30 @@ JSObject::swap(JSContext* cx, HandleObje
|
|
|
+
|
|
|
+ NotifyGCPostSwap(a, b, r);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ SetClassObject(JSObject* obj, JSProtoKey key, JSObject* cobj, JSObject* proto)
|
|
|
+ {
|
|
|
+- if (!obj->is<GlobalObject>())
|
|
|
++ if (!obj->is<GlobalObject>()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
|
|
|
+ obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ ClearClassObject(JSObject* obj, JSProtoKey key)
|
|
|
+ {
|
|
|
+- if (!obj->is<GlobalObject>())
|
|
|
++ if (!obj->is<GlobalObject>()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
|
|
|
+ obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ static NativeObject*
|
|
|
+ DefineConstructorAndPrototype(JSContext* cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
|
|
|
+ HandleObject protoProto, const Class* clasp,
|
|
|
+@@ -1874,18 +2029,19 @@ DefineConstructorAndPrototype(JSContext*
|
|
|
+ * on (1) and (2).
|
|
|
+ */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Create the prototype object. (GlobalObject::createBlankPrototype isn't
|
|
|
+ * used because it won't let us use protoProto as the proto.
|
|
|
+ */
|
|
|
+ RootedNativeObject proto(cx, NewNativeObjectWithClassProto(cx, clasp, protoProto, SingletonObject));
|
|
|
+- if (!proto)
|
|
|
++ if (!proto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Whether we need to define a constructor property on |obj| and which
|
|
|
+ * attributes to use.
|
|
|
+ */
|
|
|
+ bool defineConstructorProperty = false;
|
|
|
+ uint32_t propertyAttrs = 0;
|
|
|
+
|
|
|
+@@ -1906,18 +2062,19 @@ DefineConstructorAndPrototype(JSContext*
|
|
|
+ propertyAttrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
|
|
|
+ ? JSPROP_READONLY | JSPROP_PERMANENT
|
|
|
+ : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ctor = proto;
|
|
|
+ } else {
|
|
|
+ RootedFunction fun(cx, NewNativeConstructor(cx, constructor, nargs, atom));
|
|
|
+- if (!fun)
|
|
|
++ if (!fun) {
|
|
|
+ goto bad;
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set the class object early for standard class constructors. Type
|
|
|
+ * inference may need to access these, and js::GetBuiltinPrototype will
|
|
|
+ * fail if it tries to do a reentrant reconstruction of the class.
|
|
|
+ */
|
|
|
+ if (key != JSProto_Null) {
|
|
|
+ SetClassObject(obj, key, fun, proto);
|
|
|
+@@ -1928,79 +2085,87 @@ DefineConstructorAndPrototype(JSContext*
|
|
|
+ propertyAttrs = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Optionally construct the prototype object, before the class has
|
|
|
+ * been fully initialized. Allow the ctor to replace proto with a
|
|
|
+ * different object, as is done for operator new.
|
|
|
+ */
|
|
|
+ ctor = fun;
|
|
|
+- if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
|
|
++ if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
|
|
|
+ goto bad;
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
|
|
|
+ Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
|
|
+- if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged))
|
|
|
++ if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged)) {
|
|
|
+ goto bad;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
|
|
|
+ (ctor != proto && !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs)))
|
|
|
+ {
|
|
|
+ goto bad;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (defineConstructorProperty) {
|
|
|
+ RootedId id(cx, AtomToId(atom));
|
|
|
+ RootedValue value(cx, ObjectValue(*ctor));
|
|
|
+- if (!DefineDataProperty(cx, obj, id, value, propertyAttrs))
|
|
|
++ if (!DefineDataProperty(cx, obj, id, value, propertyAttrs)) {
|
|
|
+ goto bad;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If this is a standard class, cache its prototype. */
|
|
|
+- if (!cached && key != JSProto_Null)
|
|
|
++ if (!cached && key != JSProto_Null) {
|
|
|
+ SetClassObject(obj, key, ctor, proto);
|
|
|
+-
|
|
|
+- if (ctorp)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (ctorp) {
|
|
|
+ *ctorp = ctor;
|
|
|
++ }
|
|
|
+ return proto;
|
|
|
+
|
|
|
+ bad:
|
|
|
+- if (cached)
|
|
|
++ if (cached) {
|
|
|
+ ClearClassObject(obj, key);
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ NativeObject*
|
|
|
+ js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_,
|
|
|
+ const Class* clasp, Native constructor, unsigned nargs,
|
|
|
+ const JSPropertySpec* ps, const JSFunctionSpec* fs,
|
|
|
+ const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
|
|
|
+ NativeObject** ctorp)
|
|
|
+ {
|
|
|
+ RootedObject protoProto(cx, protoProto_);
|
|
|
+
|
|
|
+ RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
|
|
|
+- if (!atom)
|
|
|
++ if (!atom) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * All instances of the class will inherit properties from the prototype
|
|
|
+ * object we are about to create (in DefineConstructorAndPrototype), which
|
|
|
+ * in turn will inherit from protoProto.
|
|
|
+ *
|
|
|
+ * When initializing a standard class (other than Object), if protoProto is
|
|
|
+ * null, default to Object.prototype. The engine's internal uses of
|
|
|
+ * js::InitClass depend on this nicety.
|
|
|
+ */
|
|
|
+ JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
|
|
+ if (key != JSProto_Null && !protoProto) {
|
|
|
+ protoProto = GlobalObject::getOrCreatePrototype(cx, JSProto_Object);
|
|
|
+- if (!protoProto)
|
|
|
++ if (!protoProto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
|
|
|
+ ps, fs, static_ps, static_fs, ctorp);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSObject::fixupAfterMovingGC()
|
|
|
+@@ -2008,20 +2173,22 @@ JSObject::fixupAfterMovingGC()
|
|
|
+ // For copy-on-write objects that don't own their elements, fix up the
|
|
|
+ // elements pointer if it points to inline elements in the owning object.
|
|
|
+ if (is<NativeObject>()) {
|
|
|
+ NativeObject& obj = as<NativeObject>();
|
|
|
+ if (obj.denseElementsAreCopyOnWrite()) {
|
|
|
+ NativeObject* owner = obj.getElementsHeader()->ownerObject();
|
|
|
+ // Get the new owner pointer but don't call MaybeForwarded as we
|
|
|
+ // don't need to access the object's shape.
|
|
|
+- if (IsForwarded(owner))
|
|
|
++ if (IsForwarded(owner)) {
|
|
|
+ owner = Forwarded(owner);
|
|
|
+- if (owner != &obj && owner->hasFixedElements())
|
|
|
++ }
|
|
|
++ if (owner != &obj && owner->hasFixedElements()) {
|
|
|
+ obj.elements_ = owner->getElementsHeader()->elements();
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(!IsForwarded(obj.getElementsHeader()->ownerObject().get()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ ReshapeForProtoMutation(JSContext* cx, HandleObject obj)
|
|
|
+ {
|
|
|
+@@ -2051,85 +2218,94 @@ ReshapeForProtoMutation(JSContext* cx, H
|
|
|
+ RootedObject pobj(cx, obj);
|
|
|
+
|
|
|
+ while (pobj && pobj->isNative()) {
|
|
|
+ if (pobj->isSingleton()) {
|
|
|
+ // If object was converted to a singleton it should have cleared
|
|
|
+ // any UNCACHEABLE_PROTO flags.
|
|
|
+ MOZ_ASSERT(!pobj->hasUncacheableProto());
|
|
|
+
|
|
|
+- if (!NativeObject::reshapeForProtoMutation(cx, pobj.as<NativeObject>()))
|
|
|
++ if (!NativeObject::reshapeForProtoMutation(cx, pobj.as<NativeObject>())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+- if (!JSObject::setUncacheableProto(cx, pobj))
|
|
|
++ if (!JSObject::setUncacheableProto(cx, pobj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!obj->isDelegate())
|
|
|
++ if (!obj->isDelegate()) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ pobj = pobj->staticPrototype();
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ SetClassAndProto(JSContext* cx, HandleObject obj,
|
|
|
+ const Class* clasp, Handle<js::TaggedProto> proto)
|
|
|
+ {
|
|
|
+ // Regenerate object shape (and possibly prototype shape) to invalidate JIT
|
|
|
+ // code that is affected by a prototype mutation.
|
|
|
+- if (!ReshapeForProtoMutation(cx, obj))
|
|
|
++ if (!ReshapeForProtoMutation(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (proto.isObject()) {
|
|
|
+ RootedObject protoObj(cx, proto.toObject());
|
|
|
+- if (!JSObject::setDelegate(cx, protoObj))
|
|
|
++ if (!JSObject::setDelegate(cx, protoObj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->isSingleton()) {
|
|
|
+ /*
|
|
|
+ * Just splice the prototype, but mark the properties as unknown for
|
|
|
+ * consistent behavior.
|
|
|
+ */
|
|
|
+- if (!JSObject::splicePrototype(cx, obj, clasp, proto))
|
|
|
++ if (!JSObject::splicePrototype(cx, obj, clasp, proto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ MarkObjectGroupUnknownProperties(cx, obj->group());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedObjectGroup oldGroup(cx, obj->group());
|
|
|
+
|
|
|
+ ObjectGroup* newGroup;
|
|
|
+ if (oldGroup->maybeInterpretedFunction()) {
|
|
|
+ // We're changing the group/proto of a scripted function. Create a new
|
|
|
+ // group so we can keep track of the interpreted function for Ion
|
|
|
+ // inlining.
|
|
|
+ MOZ_ASSERT(obj->is<JSFunction>());
|
|
|
+ newGroup = ObjectGroupRealm::makeGroup(cx, oldGroup->realm(), &JSFunction::class_, proto);
|
|
|
+- if (!newGroup)
|
|
|
++ if (!newGroup) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
|
|
|
+ } else {
|
|
|
+ newGroup = ObjectGroup::defaultNewGroup(cx, clasp, proto);
|
|
|
+- if (!newGroup)
|
|
|
++ if (!newGroup) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ obj->setGroup(newGroup);
|
|
|
+
|
|
|
+ // Add the object's property types to the new group.
|
|
|
+ AutoSweepObjectGroup sweep(newGroup);
|
|
|
+ if (!newGroup->unknownProperties(sweep)) {
|
|
|
+- if (obj->isNative())
|
|
|
++ if (obj->isNative()) {
|
|
|
+ AddPropertyTypesAfterProtoChange(cx, &obj->as<NativeObject>(), oldGroup);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MarkObjectGroupUnknownProperties(cx, newGroup);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Type sets containing this object will contain the old group but not the
|
|
|
+ // new group of the object, so we need to treat all such type sets as
|
|
|
+ // unknown.
|
|
|
+ MarkObjectGroupUnknownProperties(cx, oldGroup);
|
|
|
+
|
|
|
+ return true;
|
|
|
+@@ -2139,18 +2315,19 @@ SetClassAndProto(JSContext* cx, HandleOb
|
|
|
+ JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!obj->isSingleton());
|
|
|
+
|
|
|
+ MarkObjectGroupUnknownProperties(cx, obj->group());
|
|
|
+
|
|
|
+ ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->group(), obj->getClass(),
|
|
|
+ obj->taggedProto());
|
|
|
+- if (!group)
|
|
|
++ if (!group) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ obj->group_ = group;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the original Object.prototype from the embedding-provided incumbent
|
|
|
+ * global.
|
|
|
+@@ -2175,23 +2352,25 @@ js::GetObjectFromIncumbentGlobal(JSConte
|
|
|
+ if (!globalObj) {
|
|
|
+ obj.set(nullptr);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ AutoRealm ar(cx, globalObj);
|
|
|
+ obj.set(GlobalObject::getOrCreateObjectPrototype(cx, globalObj));
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // The object might be from a different compartment, so wrap it.
|
|
|
+- if (obj && !cx->compartment()->wrap(cx, obj))
|
|
|
++ if (obj && !cx->compartment()->wrap(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ IsStandardPrototype(JSObject* obj, JSProtoKey key)
|
|
|
+ {
|
|
|
+ Value v = obj->nonCCWGlobal().getPrototype(key);
|
|
|
+@@ -2199,76 +2378,82 @@ IsStandardPrototype(JSObject* obj, JSPro
|
|
|
+ }
|
|
|
+
|
|
|
+ JSProtoKey
|
|
|
+ JS::IdentifyStandardInstance(JSObject* obj)
|
|
|
+ {
|
|
|
+ // Note: The prototype shares its JSClass with instances.
|
|
|
+ MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
|
|
|
+ JSProtoKey key = StandardProtoKeyOrNull(obj);
|
|
|
+- if (key != JSProto_Null && !IsStandardPrototype(obj, key))
|
|
|
++ if (key != JSProto_Null && !IsStandardPrototype(obj, key)) {
|
|
|
+ return key;
|
|
|
++ }
|
|
|
+ return JSProto_Null;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSProtoKey
|
|
|
+ JS::IdentifyStandardPrototype(JSObject* obj)
|
|
|
+ {
|
|
|
+ // Note: The prototype shares its JSClass with instances.
|
|
|
+ MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
|
|
|
+ JSProtoKey key = StandardProtoKeyOrNull(obj);
|
|
|
+- if (key != JSProto_Null && IsStandardPrototype(obj, key))
|
|
|
++ if (key != JSProto_Null && IsStandardPrototype(obj, key)) {
|
|
|
+ return key;
|
|
|
++ }
|
|
|
+ return JSProto_Null;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSProtoKey
|
|
|
+ JS::IdentifyStandardInstanceOrPrototype(JSObject* obj)
|
|
|
+ {
|
|
|
+ return StandardProtoKeyOrNull(obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSProtoKey
|
|
|
+ JS::IdentifyStandardConstructor(JSObject* obj)
|
|
|
+ {
|
|
|
+ // Note that NATIVE_CTOR does not imply that we are a standard constructor,
|
|
|
+ // but the converse is true (at least until we start having self-hosted
|
|
|
+ // constructors for standard classes). This lets us avoid a costly loop for
|
|
|
+ // many functions (which, depending on the call site, may be the common case).
|
|
|
+- if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR))
|
|
|
++ if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR)) {
|
|
|
+ return JSProto_Null;
|
|
|
++ }
|
|
|
+
|
|
|
+ GlobalObject& global = obj->as<JSFunction>().global();
|
|
|
+ for (size_t k = 0; k < JSProto_LIMIT; ++k) {
|
|
|
+ JSProtoKey key = static_cast<JSProtoKey>(k);
|
|
|
+- if (global.getConstructor(key) == ObjectValue(*obj))
|
|
|
++ if (global.getConstructor(key) == ObjectValue(*obj)) {
|
|
|
+ return key;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return JSProto_Null;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
|
|
|
+ MutableHandleObject objp, MutableHandle<PropertyResult> propp)
|
|
|
+ {
|
|
|
+- if (LookupPropertyOp op = obj->getOpsLookupProperty())
|
|
|
++ if (LookupPropertyOp op = obj->getOpsLookupProperty()) {
|
|
|
+ return op(cx, obj, id, objp, propp);
|
|
|
++ }
|
|
|
+ return LookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::LookupName(JSContext* cx, HandlePropertyName name, HandleObject envChain,
|
|
|
+ MutableHandleObject objp, MutableHandleObject pobjp, MutableHandle<PropertyResult> propp)
|
|
|
+ {
|
|
|
+ RootedId id(cx, NameToId(name));
|
|
|
+
|
|
|
+ for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) {
|
|
|
+- if (!LookupProperty(cx, env, id, pobjp, propp))
|
|
|
++ if (!LookupProperty(cx, env, id, pobjp, propp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (propp) {
|
|
|
+ objp.set(env);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ objp.set(nullptr);
|
|
|
+ pobjp.set(nullptr);
|
|
|
+@@ -2280,20 +2465,22 @@ bool
|
|
|
+ js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
|
|
|
+ JSObject** objp, JSObject** pobjp, PropertyResult* propp)
|
|
|
+ {
|
|
|
+ AutoAssertNoException nogc(cx);
|
|
|
+
|
|
|
+ MOZ_ASSERT(!*objp && !*pobjp && !*propp);
|
|
|
+
|
|
|
+ for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
|
|
|
+- if (env->getOpsLookupProperty())
|
|
|
++ if (env->getOpsLookupProperty()) {
|
|
|
+ return false;
|
|
|
+- if (!LookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), NameToId(name), pobjp, propp))
|
|
|
++ }
|
|
|
++ if (!LookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), NameToId(name), pobjp, propp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (*propp) {
|
|
|
+ *objp = env;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -2304,20 +2491,22 @@ js::LookupNameWithGlobalDefault(JSContex
|
|
|
+ {
|
|
|
+ RootedId id(cx, NameToId(name));
|
|
|
+
|
|
|
+ RootedObject pobj(cx);
|
|
|
+ Rooted<PropertyResult> prop(cx);
|
|
|
+
|
|
|
+ RootedObject env(cx, envChain);
|
|
|
+ for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) {
|
|
|
+- if (!LookupProperty(cx, env, id, &pobj, &prop))
|
|
|
++ if (!LookupProperty(cx, env, id, &pobj, &prop)) {
|
|
|
+ return false;
|
|
|
+- if (prop)
|
|
|
++ }
|
|
|
++ if (prop) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ objp.set(env);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject envChain,
|
|
|
+@@ -2325,91 +2514,100 @@ js::LookupNameUnqualified(JSContext* cx,
|
|
|
+ {
|
|
|
+ RootedId id(cx, NameToId(name));
|
|
|
+
|
|
|
+ RootedObject pobj(cx);
|
|
|
+ Rooted<PropertyResult> prop(cx);
|
|
|
+
|
|
|
+ RootedObject env(cx, envChain);
|
|
|
+ for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) {
|
|
|
+- if (!LookupProperty(cx, env, id, &pobj, &prop))
|
|
|
++ if (!LookupProperty(cx, env, id, &pobj, &prop)) {
|
|
|
+ return false;
|
|
|
+- if (prop)
|
|
|
++ }
|
|
|
++ if (prop) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // See note above RuntimeLexicalErrorObject.
|
|
|
+ if (pobj == env) {
|
|
|
+ bool isTDZ = false;
|
|
|
+ if (prop && name != cx->names().dotThis) {
|
|
|
+ // Treat Debugger environments specially for TDZ checks, as they
|
|
|
+ // look like non-native environments but in fact wrap native
|
|
|
+ // environments.
|
|
|
+ if (env->is<DebugEnvironmentProxy>()) {
|
|
|
+ RootedValue v(cx);
|
|
|
+ Rooted<DebugEnvironmentProxy*> envProxy(cx, &env->as<DebugEnvironmentProxy>());
|
|
|
+- if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v))
|
|
|
++ if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ isTDZ = IsUninitializedLexical(v);
|
|
|
+ } else {
|
|
|
+ isTDZ = IsUninitializedLexicalSlot(env, prop);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isTDZ) {
|
|
|
+ env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_UNINITIALIZED_LEXICAL);
|
|
|
+- if (!env)
|
|
|
++ if (!env) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else if (env->is<LexicalEnvironmentObject>() && !prop.shape()->writable()) {
|
|
|
+ // Assigning to a named lambda callee name is a no-op in sloppy mode.
|
|
|
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, &env->as<LexicalEnvironmentObject>());
|
|
|
+ if (lexicalEnv->isExtensible() ||
|
|
|
+ lexicalEnv->scope().kind() != ScopeKind::NamedLambda)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(name != cx->names().dotThis);
|
|
|
+ env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_BAD_CONST_ASSIGN);
|
|
|
+- if (!env)
|
|
|
++ if (!env) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ objp.set(env);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result)
|
|
|
+ {
|
|
|
+- if (obj->is<ProxyObject>())
|
|
|
++ if (obj->is<ProxyObject>()) {
|
|
|
+ return Proxy::hasOwn(cx, obj, id, result);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
|
|
|
+ Rooted<PropertyDescriptor> desc(cx);
|
|
|
+- if (!op(cx, obj, id, &desc))
|
|
|
++ if (!op(cx, obj, id, &desc)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ *result = !!desc.object();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ Rooted<PropertyResult> prop(cx);
|
|
|
+- if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop))
|
|
|
++ if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ *result = prop.isFound();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id, JSObject** objp,
|
|
|
+ PropertyResult* propp)
|
|
|
+ {
|
|
|
+ bool isTypedArrayOutOfRange = false;
|
|
|
+ do {
|
|
|
+- if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange))
|
|
|
++ if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (*propp) {
|
|
|
+ *objp = obj;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isTypedArrayOutOfRange) {
|
|
|
+ *objp = nullptr;
|
|
|
+@@ -2424,50 +2622,53 @@ js::LookupPropertyPure(JSContext* cx, JS
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
|
|
|
+ bool* isTypedArrayOutOfRange /* = nullptr */)
|
|
|
+ {
|
|
|
+ JS::AutoCheckCannotGC nogc;
|
|
|
+- if (isTypedArrayOutOfRange)
|
|
|
++ if (isTypedArrayOutOfRange) {
|
|
|
+ *isTypedArrayOutOfRange = false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (obj->isNative()) {
|
|
|
+ // Search for a native dense element, typed array element, or property.
|
|
|
+
|
|
|
+ if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
|
|
|
+ propp->setDenseOrTypedArrayElement();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->is<TypedArrayObject>()) {
|
|
|
+ uint64_t index;
|
|
|
+ if (IsTypedArrayIndex(id, &index)) {
|
|
|
+ if (index < obj->as<TypedArrayObject>().length()) {
|
|
|
+ propp->setDenseOrTypedArrayElement();
|
|
|
+ } else {
|
|
|
+ propp->setNotFound();
|
|
|
+- if (isTypedArrayOutOfRange)
|
|
|
++ if (isTypedArrayOutOfRange) {
|
|
|
+ *isTypedArrayOutOfRange = true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
|
|
|
+ propp->setNativeProperty(shape);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fail if there's a resolve hook, unless the mayResolve hook tells
|
|
|
+ // us the resolve hook won't define a property with this id.
|
|
|
+- if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
|
|
|
++ if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else if (obj->is<UnboxedPlainObject>()) {
|
|
|
+ if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
|
|
+ propp->setNonNativeProperty();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } else if (obj->is<TypedObject>()) {
|
|
|
+ if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
|
|
|
+ propp->setNonNativeProperty();
|
|
|
+@@ -2481,86 +2682,93 @@ js::LookupOwnPropertyPure(JSContext* cx,
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline bool
|
|
|
+ NativeGetPureInline(NativeObject* pobj, jsid id, PropertyResult prop, Value* vp)
|
|
|
+ {
|
|
|
+ if (prop.isDenseOrTypedArrayElement()) {
|
|
|
+ // For simplicity we ignore the TypedArray with string index case.
|
|
|
+- if (!JSID_IS_INT(id))
|
|
|
++ if (!JSID_IS_INT(id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fail if we have a custom getter.
|
|
|
+ Shape* shape = prop.shape();
|
|
|
+- if (!shape->isDataProperty())
|
|
|
++ if (!shape->isDataProperty()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *vp = pobj->getSlot(shape->slot());
|
|
|
+ MOZ_ASSERT(!vp->isMagic());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline bool
|
|
|
+ UnboxedGetPureInline(JSObject* pobj, jsid id, PropertyResult prop, Value* vp)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(prop.isNonNativeProperty());
|
|
|
+
|
|
|
+ // This might be a TypedObject.
|
|
|
+- if (!pobj->is<UnboxedPlainObject>())
|
|
|
++ if (!pobj->is<UnboxedPlainObject>()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ const UnboxedLayout& layout = pobj->as<UnboxedPlainObject>().layout();
|
|
|
+ if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
|
|
+ *vp = pobj->as<UnboxedPlainObject>().getValue(*property);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Don't bother supporting expandos for now.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp)
|
|
|
+ {
|
|
|
+ JSObject* pobj;
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
|
|
|
++ if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!prop) {
|
|
|
+ vp->setUndefined();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (MOZ_LIKELY(pobj->isNative()))
|
|
|
++ if (MOZ_LIKELY(pobj->isNative())) {
|
|
|
+ return NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp);
|
|
|
++ }
|
|
|
+ return UnboxedGetPureInline(pobj, id, prop, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool* found)
|
|
|
+ {
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupOwnPropertyPure(cx, obj, id, &prop))
|
|
|
++ if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!prop) {
|
|
|
+ *found = false;
|
|
|
+ vp->setUndefined();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ *found = true;
|
|
|
+- if (MOZ_LIKELY(obj->isNative()))
|
|
|
++ if (MOZ_LIKELY(obj->isNative())) {
|
|
|
+ return NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp);
|
|
|
++ }
|
|
|
+ return UnboxedGetPureInline(obj, id, prop, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline bool
|
|
|
+ NativeGetGetterPureInline(PropertyResult prop, JSFunction** fp)
|
|
|
+ {
|
|
|
+ if (!prop.isDenseOrTypedArrayElement() && prop.shape()->hasGetterObject()) {
|
|
|
+ Shape* shape = prop.shape();
|
|
|
+@@ -2576,85 +2784,93 @@ NativeGetGetterPureInline(PropertyResult
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp)
|
|
|
+ {
|
|
|
+ /* Just like GetPropertyPure, but get getter function, without invoking
|
|
|
+ * it. */
|
|
|
+ JSObject* pobj;
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
|
|
|
++ if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!prop) {
|
|
|
+ *fp = nullptr;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp)
|
|
|
+ {
|
|
|
+ JS::AutoCheckCannotGC nogc;
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupOwnPropertyPure(cx, obj, id, &prop))
|
|
|
++ if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!prop) {
|
|
|
+ *fp = nullptr;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native)
|
|
|
+ {
|
|
|
+ JS::AutoCheckCannotGC nogc;
|
|
|
+ *native = nullptr;
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupOwnPropertyPure(cx, obj, id, &prop))
|
|
|
++ if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (!prop || prop.isDenseOrTypedArrayElement() || !prop.shape()->hasGetterObject())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!prop || prop.isDenseOrTypedArrayElement() || !prop.shape()->hasGetterObject()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSObject* getterObj = prop.shape()->getterObject();
|
|
|
+- if (!getterObj->is<JSFunction>())
|
|
|
++ if (!getterObj->is<JSFunction>()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSFunction* getter = &getterObj->as<JSFunction>();
|
|
|
+- if (!getter->isNative())
|
|
|
++ if (!getter->isNative()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ *native = getter->native();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result)
|
|
|
+ {
|
|
|
+ PropertyResult prop;
|
|
|
+- if (!LookupOwnPropertyPure(cx, obj, id, &prop))
|
|
|
++ if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *result = prop && !prop.isDenseOrTypedArrayElement() &&
|
|
|
+ prop.shape()->isDataProperty();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
|
|
|
+ MutableHandleObject protop)
|
|
|
+ {
|
|
|
+- if (obj->is<js::ProxyObject>())
|
|
|
++ if (obj->is<js::ProxyObject>()) {
|
|
|
+ return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop);
|
|
|
++ }
|
|
|
+
|
|
|
+ *isOrdinary = true;
|
|
|
+ protop.set(obj->staticPrototype());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*** ES6 standard internal methods ***************************************************************/
|
|
|
+
|
|
|
+@@ -2667,119 +2883,134 @@ js::SetPrototype(JSContext* cx, HandleOb
|
|
|
+ MOZ_ASSERT(obj->is<ProxyObject>());
|
|
|
+ return Proxy::setPrototype(cx, obj, proto, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
|
|
|
+ * Since the values in question are objects, we can just compare pointers.
|
|
|
+ */
|
|
|
+- if (proto == obj->staticPrototype())
|
|
|
++ if (proto == obj->staticPrototype()) {
|
|
|
+ return result.succeed();
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Disallow mutation of immutable [[Prototype]]s. */
|
|
|
+- if (obj->staticPrototypeIsImmutable())
|
|
|
++ if (obj->staticPrototypeIsImmutable()) {
|
|
|
+ return result.fail(JSMSG_CANT_SET_PROTO);
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
|
|
|
+ */
|
|
|
+ if (obj->is<TypedObject>()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
|
|
|
+ "incompatible TypedObject");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
|
|
|
+ bool extensible;
|
|
|
+- if (!IsExtensible(cx, obj, &extensible))
|
|
|
++ if (!IsExtensible(cx, obj, &extensible)) {
|
|
|
+ return false;
|
|
|
+- if (!extensible)
|
|
|
++ }
|
|
|
++ if (!extensible) {
|
|
|
+ return result.fail(JSMSG_CANT_SET_PROTO);
|
|
|
++ }
|
|
|
+
|
|
|
+ // If this is a global object, resolve the Object class so that its
|
|
|
+ // [[Prototype]] chain is always properly immutable, even in the presence
|
|
|
+ // of lazy standard classes.
|
|
|
+ if (obj->is<GlobalObject>()) {
|
|
|
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
|
|
+- if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object))
|
|
|
++ if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
|
|
|
+ * have to do this comparison on the observable WindowProxy, not on the
|
|
|
+ * possibly-Window object we're setting the proto on.
|
|
|
+ */
|
|
|
+ RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
|
|
|
+ RootedObject obj2(cx, proto);
|
|
|
+ while (obj2) {
|
|
|
+ MOZ_ASSERT(!IsWindow(obj2));
|
|
|
+- if (obj2 == objMaybeWindowProxy)
|
|
|
++ if (obj2 == objMaybeWindowProxy) {
|
|
|
+ return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
|
|
|
++ }
|
|
|
+
|
|
|
+ bool isOrdinary;
|
|
|
+- if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2))
|
|
|
++ if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2)) {
|
|
|
+ return false;
|
|
|
+- if (!isOrdinary)
|
|
|
++ }
|
|
|
++ if (!isOrdinary) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Convert unboxed objects to their native representations before changing
|
|
|
+ // their prototype/group, as they depend on the group for their layout.
|
|
|
+- if (!MaybeConvertUnboxedObjectToNative(cx, obj))
|
|
|
++ if (!MaybeConvertUnboxedObjectToNative(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
|
|
|
+- if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto))
|
|
|
++ if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return result.succeed();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
|
|
|
+ {
|
|
|
+ ObjectOpResult result;
|
|
|
+ return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
|
|
|
+ {
|
|
|
+- if (obj->is<ProxyObject>())
|
|
|
++ if (obj->is<ProxyObject>()) {
|
|
|
+ return js::Proxy::preventExtensions(cx, obj, result);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!obj->nonProxyIsExtensible()) {
|
|
|
+ // If the following assertion fails, there's somewhere else a missing
|
|
|
+ // call to shrinkCapacityToInitializedLength() which needs to be found
|
|
|
+ // and fixed.
|
|
|
+ MOZ_ASSERT_IF(obj->isNative(),
|
|
|
+ obj->as<NativeObject>().getDenseInitializedLength() ==
|
|
|
+ obj->as<NativeObject>().getDenseCapacity());
|
|
|
+
|
|
|
+ return result.succeed();
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!MaybeConvertUnboxedObjectToNative(cx, obj))
|
|
|
++ if (!MaybeConvertUnboxedObjectToNative(cx, obj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (obj->isNative()) {
|
|
|
+ // Force lazy properties to be resolved.
|
|
|
+- if (!ResolveLazyProperties(cx, obj.as<NativeObject>()))
|
|
|
++ if (!ResolveLazyProperties(cx, obj.as<NativeObject>())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Prepare the elements. We have to do this before we mark the object
|
|
|
+ // non-extensible; that's fine because these changes are not observable.
|
|
|
+- if (!ObjectElements::PreventExtensions(cx, &obj->as<NativeObject>()))
|
|
|
++ if (!ObjectElements::PreventExtensions(cx, &obj->as<NativeObject>())) {
|
|
|
+ return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return result.succeed();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::PreventExtensions(JSContext* cx, HandleObject obj)
|
|
|
+ {
|
|
|
+ ObjectOpResult result;
|
|
|
+@@ -2787,18 +3018,19 @@ js::PreventExtensions(JSContext* cx, Han
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
|
|
+ MutableHandle<PropertyDescriptor> desc)
|
|
|
+ {
|
|
|
+ if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
|
|
|
+ bool ok = op(cx, obj, id, desc);
|
|
|
+- if (ok)
|
|
|
++ if (ok) {
|
|
|
+ desc.assertCompleteIfFound();
|
|
|
++ }
|
|
|
+ return ok;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc)
|
|
|
+@@ -2808,18 +3040,19 @@ js::DefineProperty(JSContext* cx, Handle
|
|
|
+ result.checkStrict(cx, obj, id);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
|
|
|
+ ObjectOpResult& result)
|
|
|
+ {
|
|
|
+ desc.assertValid();
|
|
|
+- if (DefinePropertyOp op = obj->getOpsDefineProperty())
|
|
|
++ if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
|
|
|
+ return op(cx, obj, id, desc, result);
|
|
|
++ }
|
|
|
+ return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
|
+ HandleObject getter, HandleObject setter, unsigned attrs,
|
|
|
+ ObjectOpResult& result)
|
|
|
+ {
|
|
|
+@@ -2859,43 +3092,46 @@ js::DefineDataProperty(JSContext* cx, Ha
|
|
|
+ return DefineDataProperty(cx, obj, id, value, attrs, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
|
|
|
+ unsigned attrs, ObjectOpResult& result)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!IndexToId(cx, index, &id))
|
|
|
++ if (!IndexToId(cx, index, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return DefineDataProperty(cx, obj, id, value, attrs, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
|
+ HandleObject getter, HandleObject setter, unsigned attrs)
|
|
|
+ {
|
|
|
+ ObjectOpResult result;
|
|
|
+- if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result))
|
|
|
++ if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (!result) {
|
|
|
+ MOZ_ASSERT(!cx->helperThread());
|
|
|
+ result.reportError(cx, obj, id);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
|
|
|
+ unsigned attrs)
|
|
|
+ {
|
|
|
+ ObjectOpResult result;
|
|
|
+- if (!DefineDataProperty(cx, obj, id, value, attrs, result))
|
|
|
++ if (!DefineDataProperty(cx, obj, id, value, attrs, result)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (!result) {
|
|
|
+ MOZ_ASSERT(!cx->helperThread());
|
|
|
+ result.reportError(cx, obj, id);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2907,59 +3143,65 @@ js::DefineDataProperty(JSContext* cx, Ha
|
|
|
+ return DefineDataProperty(cx, obj, id, value, attrs);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
|
|
|
+ unsigned attrs)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!IndexToId(cx, index, &id))
|
|
|
++ if (!IndexToId(cx, index, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return DefineDataProperty(cx, obj, id, value, attrs);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*** SpiderMonkey nonstandard internal methods ***************************************************/
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::SetImmutablePrototype(JSContext* cx, HandleObject obj, bool* succeeded)
|
|
|
+ {
|
|
|
+ if (obj->hasDynamicPrototype()) {
|
|
|
+ MOZ_ASSERT(!cx->helperThread());
|
|
|
+ return Proxy::setImmutablePrototype(cx, obj, succeeded);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE))
|
|
|
++ if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ *succeeded = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
|
|
+ MutableHandle<PropertyDescriptor> desc)
|
|
|
+ {
|
|
|
+ RootedObject pobj(cx);
|
|
|
+
|
|
|
+ for (pobj = obj; pobj;) {
|
|
|
+ if (pobj->is<ProxyObject>()) {
|
|
|
+ bool ok = Proxy::getPropertyDescriptor(cx, pobj, id, desc);
|
|
|
+- if (ok)
|
|
|
++ if (ok) {
|
|
|
+ desc.assertCompleteIfFound();
|
|
|
++ }
|
|
|
+ return ok;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!GetOwnPropertyDescriptor(cx, pobj, id, desc))
|
|
|
++ if (!GetOwnPropertyDescriptor(cx, pobj, id, desc)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (desc.object())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (desc.object()) {
|
|
|
+ return true;
|
|
|
+-
|
|
|
+- if (!GetPrototype(cx, pobj, &pobj))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!GetPrototype(cx, pobj, &pobj)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!desc.object());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* * */
|
|
|
+
|
|
|
+@@ -2967,37 +3209,41 @@ extern bool
|
|
|
+ PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
|
|
|
+ js::PinningBehavior pin = js::DoNotPinAtom);
|
|
|
+
|
|
|
+ static bool
|
|
|
+ DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags,
|
|
|
+ DefineAsIntrinsic intrinsic)
|
|
|
+ {
|
|
|
+ RootedId id(cx);
|
|
|
+- if (!PropertySpecNameToId(cx, fs->name, &id))
|
|
|
++ if (!PropertySpecNameToId(cx, fs->name, &id)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
|
|
|
+- if (!fun)
|
|
|
++ if (!fun) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (intrinsic == AsIntrinsic)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (intrinsic == AsIntrinsic) {
|
|
|
+ fun->setIsIntrinsic();
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue funVal(cx, ObjectValue(*fun));
|
|
|
+ return DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
|
|
|
+ DefineAsIntrinsic intrinsic)
|
|
|
+ {
|
|
|
+ for (; fs->name; fs++) {
|
|
|
+- if (!DefineFunctionFromSpec(cx, obj, fs, fs->flags, intrinsic))
|
|
|
++ if (!DefineFunctionFromSpec(cx, obj, fs, fs->flags, intrinsic)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*** ToPrimitive *************************************************************/
|
|
|
+
|
|
|
+ /*
|
|
|
+@@ -3006,18 +3252,19 @@ js::DefineFunctions(JSContext* cx, Handl
|
|
|
+ * as |this|, returning the result in *vp.
|
|
|
+ *
|
|
|
+ * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
|
|
|
+ * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
|
|
|
+ */
|
|
|
+ static bool
|
|
|
+ MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+- if (!GetProperty(cx, obj, obj, id, vp))
|
|
|
++ if (!GetProperty(cx, obj, obj, id, vp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (!IsCallable(vp)) {
|
|
|
+ vp.setObject(*obj);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return js::Call(cx, vp, obj, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3025,18 +3272,19 @@ static bool
|
|
|
+ ReportCantConvert(JSContext* cx, unsigned errorNumber, HandleObject obj, JSType hint)
|
|
|
+ {
|
|
|
+ const Class* clasp = obj->getClass();
|
|
|
+
|
|
|
+ // Avoid recursive death when decompiling in ReportValueError.
|
|
|
+ RootedString str(cx);
|
|
|
+ if (hint == JSTYPE_STRING) {
|
|
|
+ str = JS_AtomizeAndPinString(cx, clasp->name);
|
|
|
+- if (!str)
|
|
|
++ if (!str) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ str = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ RootedValue val(cx, ObjectValue(*obj));
|
|
|
+ ReportValueError(cx, errorNumber, JSDVG_SEARCH_STACK, val, str,
|
|
|
+ hint == JSTYPE_UNDEFINED
|
|
|
+ ? "primitive type"
|
|
|
+@@ -3059,26 +3307,30 @@ JS::OrdinaryToPrimitive(JSContext* cx, H
|
|
|
+ if (clasp == &StringObject::class_) {
|
|
|
+ StringObject* nobj = &obj->as<StringObject>();
|
|
|
+ if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) {
|
|
|
+ vp.setString(nobj->unbox());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!MaybeCallMethod(cx, obj, id, vp))
|
|
|
++ if (!MaybeCallMethod(cx, obj, id, vp)) {
|
|
|
+ return false;
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ }
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ id = NameToId(cx->names().valueOf);
|
|
|
+- if (!MaybeCallMethod(cx, obj, id, vp))
|
|
|
++ if (!MaybeCallMethod(cx, obj, id, vp)) {
|
|
|
+ return false;
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ }
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ id = NameToId(cx->names().valueOf);
|
|
|
+
|
|
|
+ /* Optimize new String(...).valueOf(). */
|
|
|
+ if (clasp == &StringObject::class_) {
|
|
|
+ StringObject* nobj = &obj->as<StringObject>();
|
|
|
+ if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) {
|
|
|
+ vp.setString(nobj->unbox());
|
|
|
+@@ -3090,26 +3342,30 @@ JS::OrdinaryToPrimitive(JSContext* cx, H
|
|
|
+ if (clasp == &NumberObject::class_) {
|
|
|
+ NumberObject* nobj = &obj->as<NumberObject>();
|
|
|
+ if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) {
|
|
|
+ vp.setNumber(nobj->unbox());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!MaybeCallMethod(cx, obj, id, vp))
|
|
|
++ if (!MaybeCallMethod(cx, obj, id, vp)) {
|
|
|
+ return false;
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ }
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ id = NameToId(cx->names().toString);
|
|
|
+- if (!MaybeCallMethod(cx, obj, id, vp))
|
|
|
++ if (!MaybeCallMethod(cx, obj, id, vp)) {
|
|
|
+ return false;
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ }
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+@@ -3117,69 +3373,75 @@ js::ToPrimitiveSlow(JSContext* cx, JSTyp
|
|
|
+ // (2015 Mar 17) 7.1.1 ToPrimitive.
|
|
|
+ MOZ_ASSERT(preferredType == JSTYPE_UNDEFINED ||
|
|
|
+ preferredType == JSTYPE_STRING ||
|
|
|
+ preferredType == JSTYPE_NUMBER);
|
|
|
+ RootedObject obj(cx, &vp.toObject());
|
|
|
+
|
|
|
+ // Steps 4-5.
|
|
|
+ RootedValue method(cx);
|
|
|
+- if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, &method))
|
|
|
++ if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, &method)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 6.
|
|
|
+ if (!method.isNullOrUndefined()) {
|
|
|
+ // Step 6 of GetMethod. js::Call() below would do this check and throw a
|
|
|
+ // TypeError anyway, but this produces a better error message.
|
|
|
+- if (!IsCallable(method))
|
|
|
++ if (!IsCallable(method)) {
|
|
|
+ return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj, preferredType);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 1-3, 6.a-b.
|
|
|
+ RootedValue arg0(cx, StringValue(preferredType == JSTYPE_STRING
|
|
|
+ ? cx->names().string
|
|
|
+ : preferredType == JSTYPE_NUMBER
|
|
|
+ ? cx->names().number
|
|
|
+ : cx->names().default_));
|
|
|
+
|
|
|
+- if (!js::Call(cx, method, vp, arg0, vp))
|
|
|
++ if (!js::Call(cx, method, vp, arg0, vp)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 6.c-d.
|
|
|
+- if (vp.isObject())
|
|
|
++ if (vp.isObject()) {
|
|
|
+ return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, preferredType);
|
|
|
++ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return OrdinaryToPrimitive(cx, obj, preferredType, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
|
|
|
+ bool
|
|
|
+ js::ToPropertyKeySlow(JSContext* cx, HandleValue argument, MutableHandleId result)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(argument.isObject());
|
|
|
+
|
|
|
+ // Steps 1-2.
|
|
|
+ RootedValue key(cx, argument);
|
|
|
+- if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key))
|
|
|
++ if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Steps 3-4.
|
|
|
+ return ValueToId<CanGC>(cx, key, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* * */
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj, bool* result)
|
|
|
+ {
|
|
|
+ RootedObject obj2(cx, obj);
|
|
|
+ for (;;) {
|
|
|
+- if (!GetPrototype(cx, obj2, &obj2))
|
|
|
++ if (!GetPrototype(cx, obj2, &obj2)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ if (!obj2) {
|
|
|
+ *result = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (obj2 == protoObj) {
|
|
|
+ *result = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -3188,20 +3450,22 @@ js::IsPrototypeOf(JSContext* cx, HandleO
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ js::PrimitiveToObject(JSContext* cx, const Value& v)
|
|
|
+ {
|
|
|
+ if (v.isString()) {
|
|
|
+ Rooted<JSString*> str(cx, v.toString());
|
|
|
+ return StringObject::create(cx, str);
|
|
|
+ }
|
|
|
+- if (v.isNumber())
|
|
|
++ if (v.isNumber()) {
|
|
|
+ return NumberObject::create(cx, v.toNumber());
|
|
|
+- if (v.isBoolean())
|
|
|
++ }
|
|
|
++ if (v.isBoolean()) {
|
|
|
+ return BooleanObject::create(cx, v.toBoolean());
|
|
|
++ }
|
|
|
+ #ifdef ENABLE_BIGINT
|
|
|
+ if (v.isSymbol()) {
|
|
|
+ RootedSymbol symbol(cx, v.toSymbol());
|
|
|
+ return SymbolObject::create(cx, symbol);
|
|
|
+ }
|
|
|
+ MOZ_ASSERT(v.isBigInt());
|
|
|
+ RootedBigInt bigInt(cx, v.toBigInt());
|
|
|
+ return BigIntObject::create(cx, bigInt);
|
|
|
+@@ -3269,35 +3533,37 @@ js::ToObjectSlowForPropertyAccess(JSCont
|
|
|
+ bool reportScanStack)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!val.isMagic());
|
|
|
+ MOZ_ASSERT(!val.isObject());
|
|
|
+
|
|
|
+ if (val.isNullOrUndefined()) {
|
|
|
+ RootedId key(cx);
|
|
|
+ if (keyValue.isPrimitive()) {
|
|
|
+- if (!ValueToId<CanGC>(cx, keyValue, &key))
|
|
|
++ if (!ValueToId<CanGC>(cx, keyValue, &key)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ ReportIsNullOrUndefinedForPropertyAccess(cx, val, key, reportScanStack);
|
|
|
+ } else {
|
|
|
+ ReportIsNullOrUndefinedForPropertyAccess(cx, val, reportScanStack);
|
|
|
+ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ return PrimitiveToObject(cx, val);
|
|
|
+ }
|
|
|
+
|
|
|
+ Value
|
|
|
+ js::GetThisValue(JSObject* obj)
|
|
|
+ {
|
|
|
+ // Use the WindowProxy if the global is a Window, as Window must never be
|
|
|
+ // exposed to script.
|
|
|
+- if (obj->is<GlobalObject>())
|
|
|
++ if (obj->is<GlobalObject>()) {
|
|
|
+ return ObjectValue(*ToWindowProxyIfWindow(obj));
|
|
|
++ }
|
|
|
+
|
|
|
+ // We should not expose any environments except NSVOs to script. The NSVO is
|
|
|
+ // pretending to be the global object in this case.
|
|
|
+ MOZ_ASSERT(obj->is<NonSyntacticVariablesObject>() || !obj->is<EnvironmentObject>());
|
|
|
+
|
|
|
+ return ObjectValue(*obj);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3345,43 +3611,47 @@ GetObjectSlotNameFunctor::operator()(JS:
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!shape) {
|
|
|
+ do {
|
|
|
+ const char* slotname = nullptr;
|
|
|
+ const char* pattern = nullptr;
|
|
|
+ if (obj->is<GlobalObject>()) {
|
|
|
+ pattern = "CLASS_OBJECT(%s)";
|
|
|
+- if (false)
|
|
|
++ if (false) {
|
|
|
+ ;
|
|
|
++ }
|
|
|
+ #define TEST_SLOT_MATCHES_PROTOTYPE(name,init,clasp) \
|
|
|
+ else if ((JSProto_##name) == slot) { slotname = js_##name##_str; }
|
|
|
+ JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
|
|
|
+ #undef TEST_SLOT_MATCHES_PROTOTYPE
|
|
|
+ } else {
|
|
|
+ pattern = "%s";
|
|
|
+ if (obj->is<EnvironmentObject>()) {
|
|
|
+ if (slot == EnvironmentObject::enclosingEnvironmentSlot()) {
|
|
|
+ slotname = "enclosing_environment";
|
|
|
+ } else if (obj->is<CallObject>()) {
|
|
|
+- if (slot == CallObject::calleeSlot())
|
|
|
++ if (slot == CallObject::calleeSlot()) {
|
|
|
+ slotname = "callee_slot";
|
|
|
++ }
|
|
|
+ } else if (obj->is<WithEnvironmentObject>()) {
|
|
|
+- if (slot == WithEnvironmentObject::objectSlot())
|
|
|
++ if (slot == WithEnvironmentObject::objectSlot()) {
|
|
|
+ slotname = "with_object";
|
|
|
+- else if (slot == WithEnvironmentObject::thisSlot())
|
|
|
++ } else if (slot == WithEnvironmentObject::thisSlot()) {
|
|
|
+ slotname = "with_this";
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (slotname)
|
|
|
++ if (slotname) {
|
|
|
+ snprintf(buf, bufsize, pattern, slotname);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ snprintf(buf, bufsize, "**UNKNOWN SLOT %" PRIu32 "**", slot);
|
|
|
++ }
|
|
|
+ } while (false);
|
|
|
+ } else {
|
|
|
+ jsid propid = shape->propid();
|
|
|
+ if (JSID_IS_INT(propid)) {
|
|
|
+ snprintf(buf, bufsize, "%" PRId32, JSID_TO_INT(propid));
|
|
|
+ } else if (JSID_IS_ATOM(propid)) {
|
|
|
+ PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
|
|
|
+ } else if (JSID_IS_SYMBOL(propid)) {
|
|
|
+@@ -3400,28 +3670,29 @@ GetObjectSlotNameFunctor::operator()(JS:
|
|
|
+ * Routines to print out values during debugging. These are FRIEND_API to help
|
|
|
+ * the debugger find them and to support temporarily hacking js::Dump* calls
|
|
|
+ * into other code.
|
|
|
+ */
|
|
|
+
|
|
|
+ static void
|
|
|
+ dumpValue(const Value& v, js::GenericPrinter& out)
|
|
|
+ {
|
|
|
+- if (v.isNull())
|
|
|
++ if (v.isNull()) {
|
|
|
+ out.put("null");
|
|
|
+- else if (v.isUndefined())
|
|
|
++ } else if (v.isUndefined()) {
|
|
|
+ out.put("undefined");
|
|
|
+- else if (v.isInt32())
|
|
|
++ } else if (v.isInt32()) {
|
|
|
+ out.printf("%d", v.toInt32());
|
|
|
+- else if (v.isDouble())
|
|
|
++ } else if (v.isDouble()) {
|
|
|
+ out.printf("%g", v.toDouble());
|
|
|
+- else if (v.isString())
|
|
|
++ } else if (v.isString()) {
|
|
|
+ v.toString()->dumpNoNewline(out);
|
|
|
+- else if (v.isSymbol())
|
|
|
++ } else if (v.isSymbol()) {
|
|
|
+ v.toSymbol()->dump(out);
|
|
|
++ }
|
|
|
+ else if (v.isObject() && v.toObject().is<JSFunction>()) {
|
|
|
+ JSFunction* fun = &v.toObject().as<JSFunction>();
|
|
|
+ if (fun->displayAtom()) {
|
|
|
+ out.put("<function ");
|
|
|
+ EscapedStringPrinter(out, fun->displayAtom(), 0);
|
|
|
+ } else {
|
|
|
+ out.put("<unnamed function");
|
|
|
+ }
|
|
|
+@@ -3434,20 +3705,21 @@ dumpValue(const Value& v, js::GenericPri
|
|
|
+ } else if (v.isObject()) {
|
|
|
+ JSObject* obj = &v.toObject();
|
|
|
+ const Class* clasp = obj->getClass();
|
|
|
+ out.printf("<%s%s at %p>",
|
|
|
+ clasp->name,
|
|
|
+ (clasp == &PlainObject::class_) ? "" : " object",
|
|
|
+ (void*) obj);
|
|
|
+ } else if (v.isBoolean()) {
|
|
|
+- if (v.toBoolean())
|
|
|
++ if (v.toBoolean()) {
|
|
|
+ out.put("true");
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ out.put("false");
|
|
|
++ }
|
|
|
+ } else if (v.isMagic()) {
|
|
|
+ out.put("<invalid");
|
|
|
+ switch (v.whyMagic()) {
|
|
|
+ case JS_ELEMENTS_HOLE: out.put(" elements hole"); break;
|
|
|
+ case JS_NO_ITER_VALUE: out.put(" no iter value"); break;
|
|
|
+ case JS_GENERATOR_CLOSING: out.put(" generator closing"); break;
|
|
|
+ case JS_OPTIMIZED_OUT: out.put(" optimized out"); break;
|
|
|
+ default: out.put(" ?!"); break;
|
|
|
+@@ -3488,49 +3760,53 @@ js::DumpId(jsid id, js::GenericPrinter&
|
|
|
+ dumpValue(IdToValue(id), out);
|
|
|
+ out.putChar('\n');
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ DumpProperty(const NativeObject* obj, Shape& shape, js::GenericPrinter& out)
|
|
|
+ {
|
|
|
+ jsid id = shape.propid();
|
|
|
+- if (JSID_IS_ATOM(id))
|
|
|
++ if (JSID_IS_ATOM(id)) {
|
|
|
+ JSID_TO_ATOM(id)->dumpCharsNoNewline(out);
|
|
|
+- else if (JSID_IS_INT(id))
|
|
|
++ } else if (JSID_IS_INT(id)) {
|
|
|
+ out.printf("%d", JSID_TO_INT(id));
|
|
|
+- else if (JSID_IS_SYMBOL(id))
|
|
|
++ } else if (JSID_IS_SYMBOL(id)) {
|
|
|
+ JSID_TO_SYMBOL(id)->dump(out);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ out.printf("id %p", reinterpret_cast<void*>(JSID_BITS(id)));
|
|
|
++ }
|
|
|
+
|
|
|
+ if (shape.isDataProperty()) {
|
|
|
+ out.printf(": ");
|
|
|
+ dumpValue(obj->getSlot(shape.maybeSlot()), out);
|
|
|
+ }
|
|
|
+
|
|
|
+ out.printf(" (shape %p", (void*) &shape);
|
|
|
+
|
|
|
+ uint8_t attrs = shape.attributes();
|
|
|
+ if (attrs & JSPROP_ENUMERATE) out.put(" enumerate");
|
|
|
+ if (attrs & JSPROP_READONLY) out.put(" readonly");
|
|
|
+ if (attrs & JSPROP_PERMANENT) out.put(" permanent");
|
|
|
+
|
|
|
+- if (shape.hasGetterValue())
|
|
|
++ if (shape.hasGetterValue()) {
|
|
|
+ out.printf(" getterValue %p", shape.getterObject());
|
|
|
+- else if (!shape.hasDefaultGetter())
|
|
|
++ } else if (!shape.hasDefaultGetter()) {
|
|
|
+ out.printf(" getterOp %p", JS_FUNC_TO_DATA_PTR(void*, shape.getterOp()));
|
|
|
+-
|
|
|
+- if (shape.hasSetterValue())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (shape.hasSetterValue()) {
|
|
|
+ out.printf(" setterValue %p", shape.setterObject());
|
|
|
+- else if (!shape.hasDefaultSetter())
|
|
|
++ } else if (!shape.hasDefaultSetter()) {
|
|
|
+ out.printf(" setterOp %p", JS_FUNC_TO_DATA_PTR(void*, shape.setterOp()));
|
|
|
+-
|
|
|
+- if (shape.isDataProperty())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (shape.isDataProperty()) {
|
|
|
+ out.printf(" slot %d", shape.maybeSlot());
|
|
|
++ }
|
|
|
+
|
|
|
+ out.printf(")\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSObject::uninlinedIsProxy() const
|
|
|
+ {
|
|
|
+ return is<ProxyObject>();
|
|
|
+@@ -3570,45 +3846,52 @@ JSObject::dump(js::GenericPrinter& out)
|
|
|
+ if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) out.put(" not_extensible");
|
|
|
+ if (obj->maybeHasInterestingSymbolProperty()) out.put(" maybe_has_interesting_symbol");
|
|
|
+ if (obj->isBoundFunction()) out.put(" bound_function");
|
|
|
+ if (obj->isQualifiedVarObj()) out.put(" varobj");
|
|
|
+ if (obj->isUnqualifiedVarObj()) out.put(" unqualified_varobj");
|
|
|
+ if (obj->isIteratedSingleton()) out.put(" iterated_singleton");
|
|
|
+ if (obj->isNewGroupUnknown()) out.put(" new_type_unknown");
|
|
|
+ if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto");
|
|
|
+- if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable())
|
|
|
++ if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) {
|
|
|
+ out.put(" immutable_prototype");
|
|
|
++ }
|
|
|
+
|
|
|
+ const NativeObject* nobj = obj->isNative() ? &obj->as<NativeObject>() : nullptr;
|
|
|
+ if (nobj) {
|
|
|
+- if (nobj->inDictionaryMode())
|
|
|
++ if (nobj->inDictionaryMode()) {
|
|
|
+ out.put(" inDictionaryMode");
|
|
|
+- if (nobj->hasShapeTable())
|
|
|
++ }
|
|
|
++ if (nobj->hasShapeTable()) {
|
|
|
+ out.put(" hasShapeTable");
|
|
|
+- if (nobj->hadElementsAccess())
|
|
|
++ }
|
|
|
++ if (nobj->hadElementsAccess()) {
|
|
|
+ out.put(" had_elements_access");
|
|
|
+- if (nobj->isIndexed())
|
|
|
++ }
|
|
|
++ if (nobj->isIndexed()) {
|
|
|
+ out.put(" indexed");
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ out.put(" not_native\n");
|
|
|
+ }
|
|
|
+ out.putChar('\n');
|
|
|
+
|
|
|
+ out.put(" proto ");
|
|
|
+ TaggedProto proto = obj->taggedProto();
|
|
|
+- if (proto.isDynamic())
|
|
|
++ if (proto.isDynamic()) {
|
|
|
+ out.put("<dynamic>");
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ dumpValue(ObjectOrNullValue(proto.toObjectOrNull()), out);
|
|
|
++ }
|
|
|
+ out.putChar('\n');
|
|
|
+
|
|
|
+ if (nobj) {
|
|
|
+- if (clasp->flags & JSCLASS_HAS_PRIVATE)
|
|
|
++ if (clasp->flags & JSCLASS_HAS_PRIVATE) {
|
|
|
+ out.printf(" private %p\n", nobj->getPrivate());
|
|
|
++ }
|
|
|
+
|
|
|
+ uint32_t reserved = JSCLASS_RESERVED_SLOTS(clasp);
|
|
|
+ if (reserved) {
|
|
|
+ out.printf(" reserved slots:\n");
|
|
|
+ for (uint32_t i = 0; i < reserved; i++) {
|
|
|
+ out.printf(" %3d ", i);
|
|
|
+ out.put(": ");
|
|
|
+ dumpValue(nobj->getSlot(i), out);
|
|
|
+@@ -3678,31 +3961,33 @@ js::DumpInterpreterFrame(JSContext* cx,
|
|
|
+ /* This should only called during live debugging. */
|
|
|
+ ScriptFrameIter i(cx);
|
|
|
+ if (!start) {
|
|
|
+ if (i.done()) {
|
|
|
+ out.printf("no stack for cx = %p\n", (void*) cx);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+- while (!i.done() && !i.isJSJit() && i.interpFrame() != start)
|
|
|
++ while (!i.done() && !i.isJSJit() && i.interpFrame() != start) {
|
|
|
+ ++i;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (i.done()) {
|
|
|
+ out.printf("fp = %p not found in cx = %p\n",
|
|
|
+ (void*)start, (void*)cx);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (; !i.done(); ++i) {
|
|
|
+- if (i.isJSJit())
|
|
|
++ if (i.isJSJit()) {
|
|
|
+ out.put("JIT frame\n");
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ out.printf("InterpreterFrame at %p\n", (void*) i.interpFrame());
|
|
|
++ }
|
|
|
+
|
|
|
+ if (i.isFunctionFrame()) {
|
|
|
+ out.put("callee fun: ");
|
|
|
+ RootedValue v(cx);
|
|
|
+ JSObject* fun = i.callee(cx);
|
|
|
+ v.setObject(*fun);
|
|
|
+ dumpValue(v, out);
|
|
|
+ } else {
|
|
|
+@@ -3713,31 +3998,35 @@ js::DumpInterpreterFrame(JSContext* cx,
|
|
|
+ out.printf("file %s line %u\n",
|
|
|
+ i.script()->filename(), i.script()->lineno());
|
|
|
+
|
|
|
+ if (jsbytecode* pc = i.pc()) {
|
|
|
+ out.printf(" pc = %p\n", pc);
|
|
|
+ out.printf(" current op: %s\n", CodeName[*pc]);
|
|
|
+ MaybeDumpScope(i.script()->lookupScope(pc), out);
|
|
|
+ }
|
|
|
+- if (i.isFunctionFrame())
|
|
|
++ if (i.isFunctionFrame()) {
|
|
|
+ MaybeDumpValue("this", i.thisArgument(cx), out);
|
|
|
++ }
|
|
|
+ if (!i.isJSJit()) {
|
|
|
+ out.put(" rval: ");
|
|
|
+ dumpValue(i.interpFrame()->returnValue(), out);
|
|
|
+ out.putChar('\n');
|
|
|
+ }
|
|
|
+
|
|
|
+ out.put(" flags:");
|
|
|
+- if (i.isConstructing())
|
|
|
++ if (i.isConstructing()) {
|
|
|
+ out.put(" constructing");
|
|
|
+- if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame())
|
|
|
++ }
|
|
|
++ if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) {
|
|
|
+ out.put(" debugger eval");
|
|
|
+- if (i.isEvalFrame())
|
|
|
++ }
|
|
|
++ if (i.isEvalFrame()) {
|
|
|
+ out.put(" eval");
|
|
|
++ }
|
|
|
+ out.putChar('\n');
|
|
|
+
|
|
|
+ out.printf(" envChain: (JSObject*) %p\n", (void*) i.environmentChain(cx));
|
|
|
+
|
|
|
+ out.putChar('\n');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3809,71 +4098,77 @@ JSObject::allocKindForTenure(const js::N
|
|
|
+
|
|
|
+ MOZ_ASSERT(IsInsideNursery(this));
|
|
|
+
|
|
|
+ if (is<ArrayObject>()) {
|
|
|
+ const ArrayObject& aobj = as<ArrayObject>();
|
|
|
+ MOZ_ASSERT(aobj.numFixedSlots() == 0);
|
|
|
+
|
|
|
+ /* Use minimal size object if we are just going to copy the pointer. */
|
|
|
+- if (!nursery.isInside(aobj.getElementsHeader()))
|
|
|
++ if (!nursery.isInside(aobj.getElementsHeader())) {
|
|
|
+ return gc::AllocKind::OBJECT0_BACKGROUND;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t nelements = aobj.getDenseCapacity();
|
|
|
+ return GetBackgroundAllocKind(GetGCArrayKind(nelements));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Unboxed plain objects are sized according to the data they store.
|
|
|
+ if (is<UnboxedPlainObject>()) {
|
|
|
+ size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
|
|
|
+ return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (is<JSFunction>())
|
|
|
++ if (is<JSFunction>()) {
|
|
|
+ return as<JSFunction>().getAllocKind();
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Typed arrays in the nursery may have a lazily allocated buffer, make
|
|
|
+ * sure there is room for the array's fixed data when moving the array.
|
|
|
+ */
|
|
|
+ if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
|
|
|
+ size_t nbytes = as<TypedArrayObject>().byteLength();
|
|
|
+- if (as<TypedArrayObject>().hasInlineElements())
|
|
|
++ if (as<TypedArrayObject>().hasInlineElements()) {
|
|
|
+ return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
|
|
|
++ }
|
|
|
+ return GetGCObjectKind(getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Proxies that are CrossCompartmentWrappers may be nursery allocated.
|
|
|
+- if (IsProxy(this))
|
|
|
++ if (IsProxy(this)) {
|
|
|
+ return as<ProxyObject>().allocKindForTenure();
|
|
|
++ }
|
|
|
+
|
|
|
+ // Inlined typed objects are followed by their data, so make sure we copy
|
|
|
+ // it all over to the new object.
|
|
|
+ if (is<InlineTypedObject>()) {
|
|
|
+ // Figure out the size of this object, from the prototype's TypeDescr.
|
|
|
+ // The objects we are traversing here are all tenured, so we don't need
|
|
|
+ // to check forwarding pointers.
|
|
|
+ TypeDescr& descr = as<InlineTypedObject>().typeDescr();
|
|
|
+ MOZ_ASSERT(!IsInsideNursery(&descr));
|
|
|
+ return InlineTypedObject::allocKindForTypeDescriptor(&descr);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Outline typed objects use the minimum allocation kind.
|
|
|
+- if (is<OutlineTypedObject>())
|
|
|
++ if (is<OutlineTypedObject>()) {
|
|
|
+ return gc::AllocKind::OBJECT0;
|
|
|
++ }
|
|
|
+
|
|
|
+ // All nursery allocatable non-native objects are handled above.
|
|
|
+ return as<NativeObject>().allocKindForTenure();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info)
|
|
|
+ {
|
|
|
+- if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
|
|
|
++ if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots()) {
|
|
|
+ info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
|
|
|
+ js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
|
|
|
+ if (!elements->isCopyOnWrite() || elements->ownerObject() == this) {
|
|
|
+ void* allocatedElements = as<NativeObject>().getUnshiftedElementsHeader();
|
|
|
+ info->objectsMallocHeapElementsNormal += mallocSizeOf(allocatedElements);
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -3934,49 +4229,53 @@ JSObject::sizeOfIncludingThisInNursery()
|
|
|
+ if (is<NativeObject>()) {
|
|
|
+ const NativeObject& native = as<NativeObject>();
|
|
|
+
|
|
|
+ size += native.numFixedSlots() * sizeof(Value);
|
|
|
+ size += native.numDynamicSlots() * sizeof(Value);
|
|
|
+
|
|
|
+ if (native.hasDynamicElements()) {
|
|
|
+ js::ObjectElements& elements = *native.getElementsHeader();
|
|
|
+- if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
|
|
|
++ if (!elements.isCopyOnWrite() || elements.ownerObject() == this) {
|
|
|
+ size += (elements.capacity + elements.numShiftedElements()) * sizeof(HeapSlot);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (is<ArgumentsObject>())
|
|
|
++ if (is<ArgumentsObject>()) {
|
|
|
+ size += as<ArgumentsObject>().sizeOfData();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ JS::ubi::Node::Size
|
|
|
+ JS::ubi::Concrete<JSObject>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
|
|
+ {
|
|
|
+ JSObject& obj = get();
|
|
|
+
|
|
|
+- if (!obj.isTenured())
|
|
|
++ if (!obj.isTenured()) {
|
|
|
+ return obj.sizeOfIncludingThisInNursery();
|
|
|
++ }
|
|
|
+
|
|
|
+ JS::ClassInfo info;
|
|
|
+ obj.addSizeOfExcludingThis(mallocSizeOf, &info);
|
|
|
+ return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
|
|
|
+ }
|
|
|
+
|
|
|
+ const char16_t JS::ubi::Concrete<JSObject>::concreteTypeName[] = u"JSObject";
|
|
|
+
|
|
|
+ void
|
|
|
+ JSObject::traceChildren(JSTracer* trc)
|
|
|
+ {
|
|
|
+ TraceEdge(trc, &group_, "group");
|
|
|
+
|
|
|
+- if (is<ShapedObject>())
|
|
|
++ if (is<ShapedObject>()) {
|
|
|
+ as<ShapedObject>().traceShape(trc);
|
|
|
++ }
|
|
|
+
|
|
|
+ const Class* clasp = group_->clasp();
|
|
|
+ if (clasp->isNative()) {
|
|
|
+ NativeObject* nobj = &as<NativeObject>();
|
|
|
+
|
|
|
+ {
|
|
|
+ GetObjectSlotNameFunctor func(nobj);
|
|
|
+ JS::AutoTracingDetails ctx(trc, func);
|
|
|
+@@ -4005,47 +4304,51 @@ JSObject::traceChildren(JSTracer* trc)
|
|
|
+ nobj->getDenseInitializedLength(),
|
|
|
+ static_cast<HeapSlot*>(nobj->getDenseElementsAllowCopyOnWrite()),
|
|
|
+ "objectElements");
|
|
|
+ } while (false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Call the trace hook at the end so that during a moving GC the trace hook
|
|
|
+ // will see updated fields and slots.
|
|
|
+- if (clasp->hasTrace())
|
|
|
++ if (clasp->hasTrace()) {
|
|
|
+ clasp->doTrace(trc, this);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static JSAtom*
|
|
|
+ displayAtomFromObjectGroup(ObjectGroup& group)
|
|
|
+ {
|
|
|
+ AutoSweepObjectGroup sweep(&group);
|
|
|
+ TypeNewScript* script = group.newScript(sweep);
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return script->function()->displayAtom();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSObject::constructorDisplayAtom(JSContext* cx, js::HandleObject obj, js::MutableHandleAtom name)
|
|
|
+ {
|
|
|
+ ObjectGroup *g = JSObject::getGroup(cx, obj);
|
|
|
+- if (!g)
|
|
|
++ if (!g) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ name.set(displayAtomFromObjectGroup(*g));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSAtom*
|
|
|
+ JSObject::maybeConstructorDisplayAtom() const
|
|
|
+ {
|
|
|
+- if (hasLazyGroup())
|
|
|
++ if (hasLazyGroup()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return displayAtomFromObjectGroup(*group());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ES 2016 7.3.20.
|
|
|
+ MOZ_MUST_USE JSObject*
|
|
|
+ js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
|
|
|
+ bool (*isDefaultSpecies)(JSContext*, JSFunction*))
|
|
|
+ {
|
|
|
+@@ -4066,99 +4369,108 @@ js::SpeciesConstructor(JSContext* cx, Ha
|
|
|
+ if (GetGetterPure(cx, defaultCtor, speciesId, &getter) && getter &&
|
|
|
+ isDefaultSpecies(cx, getter))
|
|
|
+ {
|
|
|
+ return defaultCtor;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 2.
|
|
|
+- if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor))
|
|
|
++ if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 3.
|
|
|
+- if (ctor.isUndefined())
|
|
|
++ if (ctor.isUndefined()) {
|
|
|
+ return defaultCtor;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 4.
|
|
|
+ if (!ctor.isObject()) {
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
|
|
+ "object's 'constructor' property");
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 5.
|
|
|
+ RootedObject ctorObj(cx, &ctor.toObject());
|
|
|
+ RootedValue s(cx);
|
|
|
+ RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
|
|
|
+- if (!GetProperty(cx, ctorObj, ctor, speciesId, &s))
|
|
|
++ if (!GetProperty(cx, ctorObj, ctor, speciesId, &s)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 6.
|
|
|
+- if (s.isNullOrUndefined())
|
|
|
++ if (s.isNullOrUndefined()) {
|
|
|
+ return defaultCtor;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 7.
|
|
|
+- if (IsConstructor(s))
|
|
|
++ if (IsConstructor(s)) {
|
|
|
+ return &s.toObject();
|
|
|
++ }
|
|
|
+
|
|
|
+ // Step 8.
|
|
|
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR,
|
|
|
+ "[Symbol.species] property of object's constructor");
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_MUST_USE JSObject*
|
|
|
+ js::SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
|
|
|
+ bool (*isDefaultSpecies)(JSContext*, JSFunction*))
|
|
|
+ {
|
|
|
+ RootedObject defaultCtor(cx, GlobalObject::getOrCreateConstructor(cx, ctorKey));
|
|
|
+- if (!defaultCtor)
|
|
|
++ if (!defaultCtor) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+- if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
|
|
|
++ if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
|
|
|
+ return Proxy::boxedValue_unbox(cx, obj, vp);
|
|
|
+-
|
|
|
+- if (obj->is<BooleanObject>())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (obj->is<BooleanObject>()) {
|
|
|
+ vp.setBoolean(obj->as<BooleanObject>().unbox());
|
|
|
+- else if (obj->is<NumberObject>())
|
|
|
++ } else if (obj->is<NumberObject>()) {
|
|
|
+ vp.setNumber(obj->as<NumberObject>().unbox());
|
|
|
+- else if (obj->is<StringObject>())
|
|
|
++ } else if (obj->is<StringObject>()) {
|
|
|
+ vp.setString(obj->as<StringObject>().unbox());
|
|
|
+- else if (obj->is<DateObject>())
|
|
|
++ } else if (obj->is<DateObject>()) {
|
|
|
+ vp.set(obj->as<DateObject>().UTCTime());
|
|
|
+- else if (obj->is<SymbolObject>())
|
|
|
++ } else if (obj->is<SymbolObject>()) {
|
|
|
+ vp.setSymbol(obj->as<SymbolObject>().unbox());
|
|
|
+ #ifdef ENABLE_BIGINT
|
|
|
+- else if (obj->is<BigIntObject>())
|
|
|
++ } else if (obj->is<BigIntObject>()) {
|
|
|
+ vp.setBigInt(obj->as<BigIntObject>().unbox());
|
|
|
+ #endif
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ vp.setUndefined();
|
|
|
++ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ /* static */ void
|
|
|
+ JSObject::debugCheckNewObject(ObjectGroup* group, Shape* shape, js::gc::AllocKind allocKind,
|
|
|
+ js::gc::InitialHeap heap)
|
|
|
+ {
|
|
|
+ const js::Class* clasp = group->clasp();
|
|
|
+ MOZ_ASSERT(clasp != &ArrayObject::class_);
|
|
|
+
|
|
|
+- if (shape)
|
|
|
++ if (shape) {
|
|
|
+ MOZ_ASSERT(clasp == shape->getObjectClass());
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_ASSERT(clasp == &UnboxedPlainObject::class_);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!ClassCanHaveFixedData(clasp)) {
|
|
|
+ MOZ_ASSERT(shape);
|
|
|
+ MOZ_ASSERT(gc::GetGCKindSlots(allocKind, clasp) == shape->numFixedSlots());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Classes with a finalizer must specify whether instances will be finalized
|
|
|
+ // on the main thread or in the background, except proxies whose behaviour
|
|
|
+diff --git a/js/src/vm/JSObject.h b/js/src/vm/JSObject.h
|
|
|
+--- a/js/src/vm/JSObject.h
|
|
|
++++ b/js/src/vm/JSObject.h
|
|
|
+@@ -606,49 +606,53 @@ struct JSObject_Slots2 : JSObject { void
|
|
|
+ struct JSObject_Slots4 : JSObject { void* data[2]; js::Value fslots[4]; };
|
|
|
+ struct JSObject_Slots8 : JSObject { void* data[2]; js::Value fslots[8]; };
|
|
|
+ struct JSObject_Slots12 : JSObject { void* data[2]; js::Value fslots[12]; };
|
|
|
+ struct JSObject_Slots16 : JSObject { void* data[2]; js::Value fslots[16]; };
|
|
|
+
|
|
|
+ /* static */ MOZ_ALWAYS_INLINE void
|
|
|
+ JSObject::readBarrier(JSObject* obj)
|
|
|
+ {
|
|
|
+- if (obj && obj->isTenured())
|
|
|
++ if (obj && obj->isTenured()) {
|
|
|
+ obj->asTenured().readBarrier(&obj->asTenured());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ MOZ_ALWAYS_INLINE void
|
|
|
+ JSObject::writeBarrierPre(JSObject* obj)
|
|
|
+ {
|
|
|
+- if (obj && obj->isTenured())
|
|
|
++ if (obj && obj->isTenured()) {
|
|
|
+ obj->asTenured().writeBarrierPre(&obj->asTenured());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ MOZ_ALWAYS_INLINE void
|
|
|
+ JSObject::writeBarrierPost(void* cellp, JSObject* prev, JSObject* next)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(cellp);
|
|
|
+
|
|
|
+ // If the target needs an entry, add it.
|
|
|
+ js::gc::StoreBuffer* buffer;
|
|
|
+ if (next && (buffer = next->storeBuffer())) {
|
|
|
+ // If we know that the prev has already inserted an entry, we can skip
|
|
|
+ // doing the lookup to add the new entry. Note that we cannot safely
|
|
|
+ // assert the presence of the entry because it may have been added
|
|
|
+ // via a different store buffer.
|
|
|
+- if (prev && prev->storeBuffer())
|
|
|
++ if (prev && prev->storeBuffer()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+ buffer->putCell(static_cast<js::gc::Cell**>(cellp));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove the prev entry if the new value does not need it. There will only
|
|
|
+ // be a prev entry if the prev value was in the nursery.
|
|
|
+- if (prev && (buffer = prev->storeBuffer()))
|
|
|
++ if (prev && (buffer = prev->storeBuffer())) {
|
|
|
+ buffer->unputCell(static_cast<js::gc::Cell**>(cellp));
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ /*** Standard internal methods ********************************************************************
|
|
|
+ *
|
|
|
+ * The functions below are the fundamental operations on objects. See the
|
|
|
+ * comment about "Standard internal methods" in jsapi.h.
|
|
|
+@@ -966,26 +970,28 @@ DefineFunctions(JSContext* cx, HandleObj
|
|
|
+
|
|
|
+ /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
|
|
|
+ extern bool
|
|
|
+ ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ ToPrimitive(JSContext* cx, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ return ToPrimitiveSlow(cx, JSTYPE_UNDEFINED, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
|
|
+ {
|
|
|
+- if (vp.isPrimitive())
|
|
|
++ if (vp.isPrimitive()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ return ToPrimitiveSlow(cx, preferredType, vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * toString support. (This isn't called GetClassName because there's a macro in
|
|
|
+ * <windows.h> with that name.)
|
|
|
+ */
|
|
|
+ MOZ_ALWAYS_INLINE const char*
|
|
|
+@@ -1019,20 +1025,22 @@ inline gc::InitialHeap
|
|
|
+ GetInitialHeap(NewObjectKind newKind, const Class* clasp)
|
|
|
+ {
|
|
|
+ if (newKind == NurseryAllocatedProxy) {
|
|
|
+ MOZ_ASSERT(clasp->isProxy());
|
|
|
+ MOZ_ASSERT(clasp->hasFinalize());
|
|
|
+ MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
|
|
|
+ return gc::DefaultHeap;
|
|
|
+ }
|
|
|
+- if (newKind != GenericObject)
|
|
|
++ if (newKind != GenericObject) {
|
|
|
+ return gc::TenuredHeap;
|
|
|
+- if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
|
|
|
++ }
|
|
|
++ if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp)) {
|
|
|
+ return gc::TenuredHeap;
|
|
|
++ }
|
|
|
+ return gc::DefaultHeap;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ NewObjectWithTaggedProtoIsCachable(JSContext* cx, Handle<TaggedProto> proto,
|
|
|
+ NewObjectKind newKind, const Class* clasp);
|
|
|
+
|
|
|
+ // ES6 9.1.15 GetPrototypeFromConstructor.
|
|
|
+@@ -1190,50 +1198,54 @@ PrimitiveToObject(JSContext* cx, const V
|
|
|
+ } /* namespace js */
|
|
|
+
|
|
|
+ namespace js {
|
|
|
+
|
|
|
+ /* For converting stack values to objects. */
|
|
|
+ MOZ_ALWAYS_INLINE JSObject*
|
|
|
+ ToObjectFromStack(JSContext* cx, HandleValue vp)
|
|
|
+ {
|
|
|
+- if (vp.isObject())
|
|
|
++ if (vp.isObject()) {
|
|
|
+ return &vp.toObject();
|
|
|
++ }
|
|
|
+ return js::ToObjectSlow(cx, vp, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSObject*
|
|
|
+ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleId key,
|
|
|
+ bool reportScanStack);
|
|
|
+ JSObject*
|
|
|
+ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandlePropertyName key,
|
|
|
+ bool reportScanStack);
|
|
|
+ JSObject*
|
|
|
+ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleValue keyValue,
|
|
|
+ bool reportScanStack);
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE JSObject*
|
|
|
+ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleId key)
|
|
|
+ {
|
|
|
+- if (vp.isObject())
|
|
|
++ if (vp.isObject()) {
|
|
|
+ return &vp.toObject();
|
|
|
++ }
|
|
|
+ return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
|
|
|
+ }
|
|
|
+ MOZ_ALWAYS_INLINE JSObject*
|
|
|
+ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandlePropertyName key)
|
|
|
+ {
|
|
|
+- if (vp.isObject())
|
|
|
++ if (vp.isObject()) {
|
|
|
+ return &vp.toObject();
|
|
|
++ }
|
|
|
+ return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
|
|
|
+ }
|
|
|
+ MOZ_ALWAYS_INLINE JSObject*
|
|
|
+ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleValue key)
|
|
|
+ {
|
|
|
+- if (vp.isObject())
|
|
|
++ if (vp.isObject()) {
|
|
|
+ return &vp.toObject();
|
|
|
++ }
|
|
|
+ return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ template<XDRMode mode>
|
|
|
+ XDRResult
|
|
|
+ XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
|
|
|
+
|
|
|
+ /*
|
|
|
+@@ -1241,51 +1253,54 @@ XDRObjectLiteral(XDRState<mode>* xdr, Mu
|
|
|
+ * Using NotNullObject is usually less code.
|
|
|
+ */
|
|
|
+ extern void
|
|
|
+ ReportNotObject(JSContext* cx, HandleValue v);
|
|
|
+
|
|
|
+ inline JSObject*
|
|
|
+ NonNullObject(JSContext* cx, HandleValue v)
|
|
|
+ {
|
|
|
+- if (v.isObject())
|
|
|
++ if (v.isObject()) {
|
|
|
+ return &v.toObject();
|
|
|
++ }
|
|
|
+ ReportNotObject(cx, v);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Report a TypeError: "N-th argument of FUN must be an object, got VALUE".
|
|
|
+ * Using NotNullObjectArg is usually less code.
|
|
|
+ */
|
|
|
+ extern void
|
|
|
+ ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v);
|
|
|
+
|
|
|
+ inline JSObject*
|
|
|
+ NonNullObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
|
|
|
+ {
|
|
|
+- if (v.isObject())
|
|
|
++ if (v.isObject()) {
|
|
|
+ return &v.toObject();
|
|
|
++ }
|
|
|
+ ReportNotObjectArg(cx, nth, fun, v);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Report a TypeError: "SOMETHING must be an object, got VALUE".
|
|
|
+ * Using NotNullObjectWithName is usually less code.
|
|
|
+ */
|
|
|
+ extern void
|
|
|
+ ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v);
|
|
|
+
|
|
|
+ inline JSObject*
|
|
|
+ NonNullObjectWithName(JSContext* cx, const char* name, HandleValue v)
|
|
|
+ {
|
|
|
+- if (v.isObject())
|
|
|
++ if (v.isObject()) {
|
|
|
+ return &v.toObject();
|
|
|
++ }
|
|
|
+ ReportNotObjectWithName(cx, name, v);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ extern bool
|
|
|
+ GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
|
|
|
+ MutableHandleObject objp);
|
|
|
+@@ -1327,17 +1342,18 @@ SpeciesConstructor(JSContext* cx, Handle
|
|
|
+ extern bool
|
|
|
+ GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);
|
|
|
+
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ inline bool
|
|
|
+ IsObjectValueInCompartment(const Value& v, JS::Compartment* comp)
|
|
|
+ {
|
|
|
+- if (!v.isObject())
|
|
|
++ if (!v.isObject()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ return v.toObject().compartment() == comp;
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ } /* namespace js */
|
|
|
+
|
|
|
+ #endif /* vm_JSObject_h */
|
|
|
+diff --git a/js/src/vm/JSScript-inl.h b/js/src/vm/JSScript-inl.h
|
|
|
+--- a/js/src/vm/JSScript-inl.h
|
|
|
++++ b/js/src/vm/JSScript-inl.h
|
|
|
+@@ -75,44 +75,47 @@ ScriptAndCounts::ScriptAndCounts(ScriptA
|
|
|
+ void
|
|
|
+ SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
|
|
|
+ HandleScript script, JSObject* argsobj);
|
|
|
+
|
|
|
+ /* static */ inline JSFunction*
|
|
|
+ LazyScript::functionDelazifying(JSContext* cx, Handle<LazyScript*> script)
|
|
|
+ {
|
|
|
+ RootedFunction fun(cx, script->function_);
|
|
|
+- if (script->function_ && !JSFunction::getOrCreateScript(cx, fun))
|
|
|
++ if (script->function_ && !JSFunction::getOrCreateScript(cx, fun)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return script->function_;
|
|
|
+ }
|
|
|
+
|
|
|
+ } // namespace js
|
|
|
+
|
|
|
+ inline JSFunction*
|
|
|
+ JSScript::functionDelazifying() const
|
|
|
+ {
|
|
|
+ JSFunction* fun = function();
|
|
|
+ if (fun && fun->isInterpretedLazy()) {
|
|
|
+ fun->setUnlazifiedScript(const_cast<JSScript*>(this));
|
|
|
+ // If this script has a LazyScript, make sure the LazyScript has a
|
|
|
+ // reference to the script when delazifying its canonical function.
|
|
|
+- if (lazyScript && !lazyScript->maybeScript())
|
|
|
++ if (lazyScript && !lazyScript->maybeScript()) {
|
|
|
+ lazyScript->initScript(const_cast<JSScript*>(this));
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return fun;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSScript::ensureNonLazyCanonicalFunction()
|
|
|
+ {
|
|
|
+ // Infallibly delazify the canonical script.
|
|
|
+ JSFunction* fun = function();
|
|
|
+- if (fun && fun->isInterpretedLazy())
|
|
|
++ if (fun && fun->isInterpretedLazy()) {
|
|
|
+ functionDelazifying();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSFunction*
|
|
|
+ JSScript::getFunction(size_t index)
|
|
|
+ {
|
|
|
+ JSObject* obj = getObject(index);
|
|
|
+ MOZ_RELEASE_ASSERT(obj->is<JSFunction>(), "Script object is not JSFunction");
|
|
|
+ JSFunction* fun = &obj->as<JSFunction>();
|
|
|
+@@ -163,48 +166,52 @@ JSScript::maybeNamedLambdaScope() const
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::Shape*
|
|
|
+ JSScript::initialEnvironmentShape() const
|
|
|
+ {
|
|
|
+ js::Scope* scope = bodyScope();
|
|
|
+ if (scope->is<js::FunctionScope>()) {
|
|
|
+- if (js::Shape* envShape = scope->environmentShape())
|
|
|
++ if (js::Shape* envShape = scope->environmentShape()) {
|
|
|
+ return envShape;
|
|
|
+- if (js::Scope* namedLambdaScope = maybeNamedLambdaScope())
|
|
|
++ }
|
|
|
++ if (js::Scope* namedLambdaScope = maybeNamedLambdaScope()) {
|
|
|
+ return namedLambdaScope->environmentShape();
|
|
|
++ }
|
|
|
+ } else if (scope->is<js::EvalScope>()) {
|
|
|
+ return scope->environmentShape();
|
|
|
+ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSPrincipals*
|
|
|
+ JSScript::principals()
|
|
|
+ {
|
|
|
+ return realm()->principals();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ JSScript::setBaselineScript(JSRuntime* rt, js::jit::BaselineScript* baselineScript)
|
|
|
+ {
|
|
|
+- if (hasBaselineScript())
|
|
|
++ if (hasBaselineScript()) {
|
|
|
+ js::jit::BaselineScript::writeBarrierPre(zone(), baseline);
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(!ion || ion == ION_DISABLED_SCRIPT);
|
|
|
+ baseline = baselineScript;
|
|
|
+ resetWarmUpResetCounter();
|
|
|
+ updateJitCodeRaw(rt);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSScript::ensureHasAnalyzedArgsUsage(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (analyzedArgsUsage())
|
|
|
++ if (analyzedArgsUsage()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ return js::jit::AnalyzeArgumentsUsage(cx, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool
|
|
|
+ JSScript::isDebuggee() const
|
|
|
+ {
|
|
|
+ return realm_->debuggerObservesAllExecution() || bitFields_.hasDebugScript_;
|
|
|
+ }
|
|
|
+diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
|
|
|
+--- a/js/src/vm/JSScript.cpp
|
|
|
++++ b/js/src/vm/JSScript.cpp
|
|
|
+@@ -120,71 +120,84 @@ js::XDRScriptConst(XDRState<mode>* xdr,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeEnum32(&tag));
|
|
|
+
|
|
|
+ switch (tag) {
|
|
|
+ case SCRIPT_INT: {
|
|
|
+ uint32_t i;
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ i = uint32_t(vp.toInt32());
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&i));
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(Int32Value(int32_t(i)));
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SCRIPT_DOUBLE: {
|
|
|
+ double d;
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ d = vp.toDouble();
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeDouble(&d));
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(DoubleValue(d));
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SCRIPT_ATOM: {
|
|
|
+ RootedAtom atom(cx);
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ atom = &vp.toString()->asAtom();
|
|
|
++ }
|
|
|
+ MOZ_TRY(XDRAtom(xdr, &atom));
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(StringValue(atom));
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SCRIPT_TRUE:
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(BooleanValue(true));
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ case SCRIPT_FALSE:
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(BooleanValue(false));
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ case SCRIPT_NULL:
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(NullValue());
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ case SCRIPT_OBJECT: {
|
|
|
+ RootedObject obj(cx);
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ obj = &vp.toObject();
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_TRY(XDRObjectLiteral(xdr, &obj));
|
|
|
+
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.setObject(*obj);
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SCRIPT_VOID:
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.set(UndefinedValue());
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ case SCRIPT_HOLE:
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vp.setMagic(JS_ELEMENTS_HOLE);
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // Fail in debug, but only soft-fail in release
|
|
|
+ MOZ_ASSERT(false, "Bad XDR value kind");
|
|
|
+ return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
|
|
|
+ }
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+@@ -206,23 +219,25 @@ XDRLazyClosedOverBindings(XDRState<mode>
|
|
|
+ uint8_t endOfScopeSentinel;
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ atom = lazy->closedOverBindings()[i];
|
|
|
+ endOfScopeSentinel = !atom;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeUint8(&endOfScopeSentinel));
|
|
|
+
|
|
|
+- if (endOfScopeSentinel)
|
|
|
++ if (endOfScopeSentinel) {
|
|
|
+ atom = nullptr;
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_TRY(XDRAtom(xdr, &atom));
|
|
|
+-
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ lazy->closedOverBindings()[i] = atom;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Code the missing part needed to re-create a LazyScript from a JSScript.
|
|
|
+ template<XDRMode mode>
|
|
|
+ static XDRResult
|
|
|
+@@ -259,18 +274,19 @@ XDRRelazificationInfo(XDRState<mode>* xd
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeUint64(&packedFields));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ RootedScriptSourceObject sourceObject(cx, &script->scriptSourceUnwrap());
|
|
|
+ lazy.set(LazyScript::CreateForXDR(cx, fun, script, enclosingScope, sourceObject,
|
|
|
+ packedFields, sourceStart, sourceEnd, toStringStart,
|
|
|
+ lineno, column));
|
|
|
+- if (!lazy)
|
|
|
++ if (!lazy) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ lazy->setToStringEnd(toStringEnd);
|
|
|
+
|
|
|
+ // As opposed to XDRLazyScript, we need to restore the runtime bits
|
|
|
+ // of the script, as we are trying to match the fact this function
|
|
|
+ // has already been parsed and that it would need to be re-lazified.
|
|
|
+ lazy->initRuntimeFields(packedFields);
|
|
|
+ }
|
|
|
+@@ -287,18 +303,19 @@ XDRRelazificationInfo(XDRState<mode>* xd
|
|
|
+
|
|
|
+ static inline uint32_t
|
|
|
+ FindScopeIndex(JSScript* script, Scope& scope)
|
|
|
+ {
|
|
|
+ ScopeArray* scopes = script->scopes();
|
|
|
+ GCPtrScope* vector = scopes->vector;
|
|
|
+ unsigned length = scopes->length;
|
|
|
+ for (uint32_t i = 0; i < length; ++i) {
|
|
|
+- if (vector[i] == &scope)
|
|
|
++ if (vector[i] == &scope) {
|
|
|
+ return i;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_CRASH("Scope not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ enum XDRClassKind {
|
|
|
+ CK_RegexpObject,
|
|
|
+ CK_JSFunction,
|
|
|
+@@ -368,96 +385,126 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ if (!realm->creationOptions().cloneSingletons() ||
|
|
|
+ !realm->behaviors().getSingletonsAsTemplates())
|
|
|
+ {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Failure_RunOnceNotSupported);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ length = script->length();
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&length));
|
|
|
+
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ prologueLength = script->mainOffset();
|
|
|
+ lineno = script->lineno();
|
|
|
+ column = script->column();
|
|
|
+ nfixed = script->nfixed();
|
|
|
+ nslots = script->nslots();
|
|
|
+
|
|
|
+ bodyScopeIndex = script->bodyScopeIndex();
|
|
|
+ natoms = script->natoms();
|
|
|
+
|
|
|
+ nsrcnotes = script->numNotes();
|
|
|
+
|
|
|
+- if (script->hasConsts())
|
|
|
++ if (script->hasConsts()) {
|
|
|
+ nconsts = script->consts()->length;
|
|
|
+- if (script->hasObjects())
|
|
|
++ }
|
|
|
++ if (script->hasObjects()) {
|
|
|
+ nobjects = script->objects()->length;
|
|
|
++ }
|
|
|
+ nscopes = script->scopes()->length;
|
|
|
+- if (script->hasTrynotes())
|
|
|
++ if (script->hasTrynotes()) {
|
|
|
+ ntrynotes = script->trynotes()->length;
|
|
|
+- if (script->hasScopeNotes())
|
|
|
++ }
|
|
|
++ if (script->hasScopeNotes()) {
|
|
|
+ nscopenotes = script->scopeNotes()->length;
|
|
|
+- if (script->hasYieldAndAwaitOffsets())
|
|
|
++ }
|
|
|
++ if (script->hasYieldAndAwaitOffsets()) {
|
|
|
+ nyieldoffsets = script->yieldAndAwaitOffsets().length();
|
|
|
++ }
|
|
|
+
|
|
|
+ nTypeSets = script->nTypeSets();
|
|
|
+ funLength = script->funLength();
|
|
|
+
|
|
|
+- if (script->noScriptRval())
|
|
|
++ if (script->noScriptRval()) {
|
|
|
+ scriptBits |= (1 << NoScriptRval);
|
|
|
+- if (script->strict())
|
|
|
++ }
|
|
|
++ if (script->strict()) {
|
|
|
+ scriptBits |= (1 << Strict);
|
|
|
+- if (script->explicitUseStrict())
|
|
|
++ }
|
|
|
++ if (script->explicitUseStrict()) {
|
|
|
+ scriptBits |= (1 << ExplicitUseStrict);
|
|
|
+- if (script->selfHosted())
|
|
|
++ }
|
|
|
++ if (script->selfHosted()) {
|
|
|
+ scriptBits |= (1 << SelfHosted);
|
|
|
+- if (script->bindingsAccessedDynamically())
|
|
|
++ }
|
|
|
++ if (script->bindingsAccessedDynamically()) {
|
|
|
+ scriptBits |= (1 << ContainsDynamicNameAccess);
|
|
|
+- if (script->funHasExtensibleScope())
|
|
|
++ }
|
|
|
++ if (script->funHasExtensibleScope()) {
|
|
|
+ scriptBits |= (1 << FunHasExtensibleScope);
|
|
|
+- if (script->funHasAnyAliasedFormal())
|
|
|
++ }
|
|
|
++ if (script->funHasAnyAliasedFormal()) {
|
|
|
+ scriptBits |= (1 << FunHasAnyAliasedFormal);
|
|
|
+- if (script->argumentsHasVarBinding())
|
|
|
++ }
|
|
|
++ if (script->argumentsHasVarBinding()) {
|
|
|
+ scriptBits |= (1 << ArgumentsHasVarBinding);
|
|
|
+- if (script->analyzedArgsUsage() && script->needsArgsObj())
|
|
|
++ }
|
|
|
++ if (script->analyzedArgsUsage() && script->needsArgsObj()) {
|
|
|
+ scriptBits |= (1 << NeedsArgsObj);
|
|
|
+- if (script->hasMappedArgsObj())
|
|
|
++ }
|
|
|
++ if (script->hasMappedArgsObj()) {
|
|
|
+ scriptBits |= (1 << HasMappedArgsObj);
|
|
|
+- if (script->functionHasThisBinding())
|
|
|
++ }
|
|
|
++ if (script->functionHasThisBinding()) {
|
|
|
+ scriptBits |= (1 << FunctionHasThisBinding);
|
|
|
+- if (script->functionHasExtraBodyVarScope())
|
|
|
++ }
|
|
|
++ if (script->functionHasExtraBodyVarScope()) {
|
|
|
+ scriptBits |= (1 << FunctionHasExtraBodyVarScope);
|
|
|
++ }
|
|
|
+ MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
|
|
|
+- if (!sourceObjectArg)
|
|
|
++ if (!sourceObjectArg) {
|
|
|
+ scriptBits |= (1 << OwnSource);
|
|
|
+- if (script->isGenerator())
|
|
|
++ }
|
|
|
++ if (script->isGenerator()) {
|
|
|
+ scriptBits |= (1 << IsGenerator);
|
|
|
+- if (script->isAsync())
|
|
|
++ }
|
|
|
++ if (script->isAsync()) {
|
|
|
+ scriptBits |= (1 << IsAsync);
|
|
|
+- if (script->hasRest())
|
|
|
++ }
|
|
|
++ if (script->hasRest()) {
|
|
|
+ scriptBits |= (1 << HasRest);
|
|
|
+- if (script->hasSingletons())
|
|
|
++ }
|
|
|
++ if (script->hasSingletons()) {
|
|
|
+ scriptBits |= (1 << HasSingleton);
|
|
|
+- if (script->treatAsRunOnce())
|
|
|
++ }
|
|
|
++ if (script->treatAsRunOnce()) {
|
|
|
+ scriptBits |= (1 << TreatAsRunOnce);
|
|
|
+- if (script->isRelazifiable())
|
|
|
++ }
|
|
|
++ if (script->isRelazifiable()) {
|
|
|
+ scriptBits |= (1 << HasLazyScript);
|
|
|
+- if (script->hasNonSyntacticScope())
|
|
|
++ }
|
|
|
++ if (script->hasNonSyntacticScope()) {
|
|
|
+ scriptBits |= (1 << HasNonSyntacticScope);
|
|
|
+- if (script->hasInnerFunctions())
|
|
|
++ }
|
|
|
++ if (script->hasInnerFunctions()) {
|
|
|
+ scriptBits |= (1 << HasInnerFunctions);
|
|
|
+- if (script->needsHomeObject())
|
|
|
++ }
|
|
|
++ if (script->needsHomeObject()) {
|
|
|
+ scriptBits |= (1 << NeedsHomeObject);
|
|
|
+- if (script->isDerivedClassConstructor())
|
|
|
++ }
|
|
|
++ if (script->isDerivedClassConstructor()) {
|
|
|
+ scriptBits |= (1 << IsDerivedClassConstructor);
|
|
|
+- if (script->isDefaultClassConstructor())
|
|
|
++ }
|
|
|
++ if (script->isDefaultClassConstructor()) {
|
|
|
+ scriptBits |= (1 << IsDefaultClassConstructor);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeUint32(&prologueLength));
|
|
|
+
|
|
|
+ // To fuse allocations, we need lengths of all embedded arrays early.
|
|
|
+ MOZ_TRY(xdr->codeUint32(&natoms));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&nsrcnotes));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&nconsts));
|
|
|
+@@ -489,50 +536,55 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ } else {
|
|
|
+ options.emplace(xdr->cx());
|
|
|
+ (*options).setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
|
|
+ .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scriptBits & (1 << OwnSource)) {
|
|
|
+ ScriptSource* ss = cx->new_<ScriptSource>();
|
|
|
+- if (!ss)
|
|
|
++ if (!ss) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ ScriptSourceHolder ssHolder(ss);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We use this CompileOptions only to initialize the
|
|
|
+ * ScriptSourceObject. Most CompileOptions fields aren't used by
|
|
|
+ * ScriptSourceObject, and those that are (element; elementAttributeName)
|
|
|
+ * aren't preserved by XDR. So this can be simple.
|
|
|
+ */
|
|
|
+- if (!ss->initFromOptions(cx, *options))
|
|
|
++ if (!ss->initFromOptions(cx, *options)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ sourceObject = ScriptSourceObject::create(cx, ss);
|
|
|
+- if (!sourceObject)
|
|
|
++ if (!sourceObject) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (xdr->hasScriptSourceObjectOut()) {
|
|
|
+ // When the ScriptSourceObjectOut is provided by ParseTask, it
|
|
|
+ // is stored in a location which is traced by the GC.
|
|
|
+ *xdr->scriptSourceObjectOut() = sourceObject;
|
|
|
+ } else if (!ScriptSourceObject::initFromOptions(cx, sourceObject, *options)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Set the script in its function now so that inner scripts to be
|
|
|
+ // decoded may iterate the static scope chain.
|
|
|
+- if (fun)
|
|
|
++ if (fun) {
|
|
|
+ fun->initScript(script);
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ // When encoding, we do not mutate any of the JSScript or LazyScript, so
|
|
|
+ // we can safely unwrap it here.
|
|
|
+ sourceObject = &script->scriptSourceUnwrap();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ if (!JSScript::partiallyInit(cx, script, nscopes, nconsts, nobjects, ntrynotes,
|
|
|
+@@ -545,63 +597,84 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ script->mainOffset_ = prologueLength;
|
|
|
+ script->funLength_ = funLength;
|
|
|
+
|
|
|
+ MOZ_ASSERT(nTypeSets <= UINT16_MAX);
|
|
|
+ script->nTypeSets_ = uint16_t(nTypeSets);
|
|
|
+
|
|
|
+ scriptp.set(script);
|
|
|
+
|
|
|
+- if (scriptBits & (1 << Strict))
|
|
|
++ if (scriptBits & (1 << Strict)) {
|
|
|
+ script->bitFields_.strict_ = true;
|
|
|
+- if (scriptBits & (1 << ExplicitUseStrict))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << ExplicitUseStrict)) {
|
|
|
+ script->bitFields_.explicitUseStrict_ = true;
|
|
|
+- if (scriptBits & (1 << ContainsDynamicNameAccess))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << ContainsDynamicNameAccess)) {
|
|
|
+ script->bitFields_.bindingsAccessedDynamically_ = true;
|
|
|
+- if (scriptBits & (1 << FunHasExtensibleScope))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << FunHasExtensibleScope)) {
|
|
|
+ script->bitFields_.funHasExtensibleScope_ = true;
|
|
|
+- if (scriptBits & (1 << FunHasAnyAliasedFormal))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
|
|
|
+ script->bitFields_.funHasAnyAliasedFormal_ = true;
|
|
|
+- if (scriptBits & (1 << ArgumentsHasVarBinding))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << ArgumentsHasVarBinding)) {
|
|
|
+ script->setArgumentsHasVarBinding();
|
|
|
+- if (scriptBits & (1 << NeedsArgsObj))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << NeedsArgsObj)) {
|
|
|
+ script->setNeedsArgsObj(true);
|
|
|
+- if (scriptBits & (1 << HasMappedArgsObj))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << HasMappedArgsObj)) {
|
|
|
+ script->bitFields_.hasMappedArgsObj_ = true;
|
|
|
+- if (scriptBits & (1 << FunctionHasThisBinding))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << FunctionHasThisBinding)) {
|
|
|
+ script->bitFields_.functionHasThisBinding_ = true;
|
|
|
+- if (scriptBits & (1 << FunctionHasExtraBodyVarScope))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
|
|
|
+ script->bitFields_.functionHasExtraBodyVarScope_ = true;
|
|
|
+- if (scriptBits & (1 << HasSingleton))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << HasSingleton)) {
|
|
|
+ script->bitFields_.hasSingletons_ = true;
|
|
|
+- if (scriptBits & (1 << TreatAsRunOnce))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << TreatAsRunOnce)) {
|
|
|
+ script->bitFields_.treatAsRunOnce_ = true;
|
|
|
+- if (scriptBits & (1 << HasNonSyntacticScope))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << HasNonSyntacticScope)) {
|
|
|
+ script->bitFields_.hasNonSyntacticScope_ = true;
|
|
|
+- if (scriptBits & (1 << HasInnerFunctions))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << HasInnerFunctions)) {
|
|
|
+ script->bitFields_.hasInnerFunctions_ = true;
|
|
|
+- if (scriptBits & (1 << NeedsHomeObject))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << NeedsHomeObject)) {
|
|
|
+ script->bitFields_.needsHomeObject_ = true;
|
|
|
+- if (scriptBits & (1 << IsDerivedClassConstructor))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << IsDerivedClassConstructor)) {
|
|
|
+ script->bitFields_.isDerivedClassConstructor_ = true;
|
|
|
+- if (scriptBits & (1 << IsDefaultClassConstructor))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << IsDefaultClassConstructor)) {
|
|
|
+ script->bitFields_.isDefaultClassConstructor_ = true;
|
|
|
+- if (scriptBits & (1 << IsGenerator))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << IsGenerator)) {
|
|
|
+ script->setGeneratorKind(GeneratorKind::Generator);
|
|
|
+- if (scriptBits & (1 << IsAsync))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << IsAsync)) {
|
|
|
+ script->setAsyncKind(FunctionAsyncKind::AsyncFunction);
|
|
|
+- if (scriptBits & (1 << HasRest))
|
|
|
++ }
|
|
|
++ if (scriptBits & (1 << HasRest)) {
|
|
|
+ script->setHasRest();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
|
|
|
+ JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
|
|
|
+
|
|
|
+- if (scriptBits & (1 << OwnSource))
|
|
|
++ if (scriptBits & (1 << OwnSource)) {
|
|
|
+ MOZ_TRY(sourceObject->source()->performXDR<mode>(xdr));
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&script->sourceStart_));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&script->sourceEnd_));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&script->toStringStart_));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&script->toStringEnd_));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&lineno));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&column));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&nfixed));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&nslots));
|
|
|
+@@ -611,23 +684,25 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ script->lineno_ = lineno;
|
|
|
+ script->column_ = column;
|
|
|
+ script->nfixed_ = nfixed;
|
|
|
+ script->nslots_ = nslots;
|
|
|
+ script->bodyScopeIndex_ = bodyScopeIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+- if (!script->createScriptData(cx, length, nsrcnotes, natoms))
|
|
|
++ if (!script->createScriptData(cx, length, nsrcnotes, natoms)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ auto scriptDataGuard = mozilla::MakeScopeExit([&] {
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ script->freeScriptData();
|
|
|
++ }
|
|
|
+ });
|
|
|
+
|
|
|
+ jsbytecode* code = script->code();
|
|
|
+ MOZ_TRY(xdr->codeBytes(code, length));
|
|
|
+ MOZ_TRY(xdr->codeBytes(code + length, nsrcnotes));
|
|
|
+
|
|
|
+ for (i = 0; i != natoms; ++i) {
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+@@ -637,29 +712,32 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ } else {
|
|
|
+ RootedAtom tmp(cx, script->atoms()[i]);
|
|
|
+ MOZ_TRY(XDRAtom(xdr, &tmp));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ scriptDataGuard.release();
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+- if (!script->shareScriptData(cx))
|
|
|
++ if (!script->shareScriptData(cx)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nconsts) {
|
|
|
+ GCPtrValue* vector = script->consts()->vector;
|
|
|
+ RootedValue val(cx);
|
|
|
+ for (i = 0; i != nconsts; ++i) {
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ val = vector[i];
|
|
|
++ }
|
|
|
+ MOZ_TRY(XDRScriptConst(xdr, &val));
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vector[i].init(val);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(nscopes != 0);
|
|
|
+ GCPtrScope* vector = script->scopes()->vector;
|
|
|
+ RootedScope scope(cx);
|
|
|
+ RootedScope enclosing(cx);
|
|
|
+@@ -710,18 +788,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ case ScopeKind::Catch:
|
|
|
+ case ScopeKind::NamedLambda:
|
|
|
+ case ScopeKind::StrictNamedLambda:
|
|
|
+ MOZ_TRY(LexicalScope::XDR(xdr, scopeKind, enclosing, &scope));
|
|
|
+ break;
|
|
|
+ case ScopeKind::With:
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ scope = WithScope::create(cx, enclosing);
|
|
|
+- if (!scope)
|
|
|
++ if (!scope) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case ScopeKind::Eval:
|
|
|
+ case ScopeKind::StrictEval:
|
|
|
+ MOZ_TRY(EvalScope::XDR(xdr, scopeKind, enclosing, &scope));
|
|
|
+ break;
|
|
|
+ case ScopeKind::Global:
|
|
|
+ case ScopeKind::NonSyntactic:
|
|
|
+@@ -735,18 +814,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ MOZ_CRASH("wasm functions cannot be nested in JSScripts");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // Fail in debug, but only soft-fail in release
|
|
|
+ MOZ_ASSERT(false, "Bad XDR scope kind");
|
|
|
+ return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ vector[i].init(scope);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify marker to detect data corruption after decoding scope data. A
|
|
|
+ // mismatch here indicates we will almost certainly crash in release.
|
|
|
+ MOZ_TRY(xdr->codeMarker(0x48922BAB));
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+@@ -755,36 +835,39 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+ * after the enclosing block has been XDR'd.
|
|
|
+ */
|
|
|
+ for (i = 0; i != nobjects; ++i) {
|
|
|
+ GCPtrObject* objp = &script->objects()->vector[i];
|
|
|
+ XDRClassKind classk;
|
|
|
+
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ JSObject* obj = *objp;
|
|
|
+- if (obj->is<RegExpObject>())
|
|
|
++ if (obj->is<RegExpObject>()) {
|
|
|
+ classk = CK_RegexpObject;
|
|
|
+- else if (obj->is<JSFunction>())
|
|
|
++ } else if (obj->is<JSFunction>()) {
|
|
|
+ classk = CK_JSFunction;
|
|
|
+- else if (obj->is<PlainObject>() || obj->is<ArrayObject>())
|
|
|
++ } else if (obj->is<PlainObject>() || obj->is<ArrayObject>()) {
|
|
|
+ classk = CK_JSObject;
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_CRASH("Cannot encode this class of object.");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_TRY(xdr->codeEnum32(&classk));
|
|
|
+
|
|
|
+ switch (classk) {
|
|
|
+ case CK_RegexpObject: {
|
|
|
+ Rooted<RegExpObject*> regexp(cx);
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ regexp = &(*objp)->as<RegExpObject>();
|
|
|
++ }
|
|
|
+ MOZ_TRY(XDRScriptRegExpObject(xdr, ®exp));
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ *objp = regexp;
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case CK_JSFunction: {
|
|
|
+ /* Code the nested function's enclosing scope. */
|
|
|
+ uint32_t funEnclosingScopeIndex = 0;
|
|
|
+ RootedScope funEnclosingScope(cx);
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+@@ -806,18 +889,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ MOZ_ASSERT(funEnclosingScopeIndex < script->scopes()->length);
|
|
|
+ funEnclosingScope = script->scopes()->vector[funEnclosingScopeIndex];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Code nested function and script.
|
|
|
+ RootedFunction tmp(cx);
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ tmp = &(*objp)->as<JSFunction>();
|
|
|
++ }
|
|
|
+ MOZ_TRY(XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp));
|
|
|
+ *objp = tmp;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case CK_JSObject: {
|
|
|
+ /* Code object literal. */
|
|
|
+ RootedObject tmp(cx, *objp);
|
|
|
+@@ -861,31 +945,34 @@ js::XDRScript(XDRState<mode>* xdr, Handl
|
|
|
+
|
|
|
+ for (i = 0; i < nyieldoffsets; ++i) {
|
|
|
+ uint32_t* offset = &script->yieldAndAwaitOffsets()[i];
|
|
|
+ MOZ_TRY(xdr->codeUint32(offset));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scriptBits & (1 << HasLazyScript)) {
|
|
|
+ Rooted<LazyScript*> lazy(cx);
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ lazy = script->maybeLazyScript();
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_TRY(XDRRelazificationInfo(xdr, fun, script, scriptEnclosingScope, &lazy));
|
|
|
+
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ script->setLazyScript(lazy);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ scriptp.set(script);
|
|
|
+
|
|
|
+ /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
|
|
|
+- if (!fun && !cx->helperThread())
|
|
|
++ if (!fun && !cx->helperThread()) {
|
|
|
+ Debugger::onNewScript(cx, script);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ template XDRResult
|
|
|
+ js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSourceObject, HandleFunction,
|
|
|
+ MutableHandleScript);
|
|
|
+@@ -936,41 +1023,44 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
|
|
|
+ MOZ_TRY(xdr->codeUint32(&lineno));
|
|
|
+ MOZ_TRY(xdr->codeUint32(&column));
|
|
|
+ MOZ_TRY(xdr->codeUint64(&packedFields));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ lazy.set(LazyScript::CreateForXDR(cx, fun, nullptr, enclosingScope, sourceObject,
|
|
|
+ packedFields, sourceStart, sourceEnd, toStringStart,
|
|
|
+ lineno, column));
|
|
|
+- if (!lazy)
|
|
|
++ if (!lazy) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ lazy->setToStringEnd(toStringEnd);
|
|
|
+ fun->initLazyScript(lazy);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Code closed-over bindings.
|
|
|
+ MOZ_TRY(XDRLazyClosedOverBindings(xdr, lazy));
|
|
|
+
|
|
|
+ // Code inner functions.
|
|
|
+ {
|
|
|
+ RootedFunction func(cx);
|
|
|
+ GCPtrFunction* innerFunctions = lazy->innerFunctions();
|
|
|
+ size_t numInnerFunctions = lazy->numInnerFunctions();
|
|
|
+ for (size_t i = 0; i < numInnerFunctions; i++) {
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ func = innerFunctions[i];
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, sourceObject, &func));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ innerFunctions[i] = func;
|
|
|
+- if (innerFunctions[i]->isInterpretedLazy())
|
|
|
++ if (innerFunctions[i]->isInterpretedLazy()) {
|
|
|
+ innerFunctions[i]->lazyScript()->setEnclosingLazyScript(lazy);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ template XDRResult
|
|
|
+@@ -1045,24 +1135,26 @@ JSScript::initScriptCounts(JSContext* cx
|
|
|
+
|
|
|
+ // Initialize all PCCounts counters to 0.
|
|
|
+ ScriptCounts::PCCountsVector base;
|
|
|
+ if (!base.reserve(jumpTargets.length())) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+- for (size_t i = 0; i < jumpTargets.length(); i++)
|
|
|
++ for (size_t i = 0; i < jumpTargets.length(); i++) {
|
|
|
+ base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
|
|
|
++ }
|
|
|
+
|
|
|
+ // Create realm's scriptCountsMap if necessary.
|
|
|
+ if (!realm()->scriptCountsMap) {
|
|
|
+ auto map = cx->make_unique<ScriptCountsMap>();
|
|
|
+- if (!map)
|
|
|
++ if (!map) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ realm()->scriptCountsMap = std::move(map);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Allocate the ScriptCounts.
|
|
|
+ UniqueScriptCounts sc = cx->make_unique<ScriptCounts>(std::move(base));
|
|
|
+ if (!sc) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+@@ -1076,18 +1168,19 @@ JSScript::initScriptCounts(JSContext* cx
|
|
|
+ }
|
|
|
+
|
|
|
+ // safe to set this; we can't fail after this point.
|
|
|
+ bitFields_.hasScriptCounts_ = true;
|
|
|
+
|
|
|
+ // Enable interrupts in any interpreter frames running on this script. This
|
|
|
+ // is used to let the interpreter increment the PCCounts, if present.
|
|
|
+ for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
|
|
+- if (iter->isInterpreter())
|
|
|
++ if (iter->isInterpreter()) {
|
|
|
+ iter->asInterpreter()->enableInterruptsIfRunning(this);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline ScriptCountsMap::Ptr
|
|
|
+ GetScriptCountsMapEntry(JSScript* script)
|
|
|
+ {
|
|
|
+@@ -1118,93 +1211,104 @@ JSScript::getScriptName()
|
|
|
+ auto p = GetScriptNameMapEntry(this);
|
|
|
+ return p->value().get();
|
|
|
+ }
|
|
|
+
|
|
|
+ js::PCCounts*
|
|
|
+ ScriptCounts::maybeGetPCCounts(size_t offset) {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
|
|
|
+- if (elem == pcCounts_.end() || elem->pcOffset() != offset)
|
|
|
++ if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+
|
|
|
+ const js::PCCounts*
|
|
|
+ ScriptCounts::maybeGetPCCounts(size_t offset) const {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
|
|
|
+- if (elem == pcCounts_.end() || elem->pcOffset() != offset)
|
|
|
++ if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+
|
|
|
+ js::PCCounts*
|
|
|
+ ScriptCounts::getImmediatePrecedingPCCounts(size_t offset)
|
|
|
+ {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
|
|
|
+- if (elem == pcCounts_.end())
|
|
|
++ if (elem == pcCounts_.end()) {
|
|
|
+ return &pcCounts_.back();
|
|
|
+- if (elem->pcOffset() == offset)
|
|
|
++ }
|
|
|
++ if (elem->pcOffset() == offset) {
|
|
|
+ return elem;
|
|
|
+- if (elem != pcCounts_.begin())
|
|
|
++ }
|
|
|
++ if (elem != pcCounts_.begin()) {
|
|
|
+ return elem - 1;
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ const js::PCCounts*
|
|
|
+ ScriptCounts::maybeGetThrowCounts(size_t offset) const {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
|
|
|
+- if (elem == throwCounts_.end() || elem->pcOffset() != offset)
|
|
|
++ if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+
|
|
|
+ const js::PCCounts*
|
|
|
+ ScriptCounts::getImmediatePrecedingThrowCounts(size_t offset) const
|
|
|
+ {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
|
|
|
+ if (elem == throwCounts_.end()) {
|
|
|
+- if (throwCounts_.begin() == throwCounts_.end())
|
|
|
++ if (throwCounts_.begin() == throwCounts_.end()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return &throwCounts_.back();
|
|
|
+ }
|
|
|
+- if (elem->pcOffset() == offset)
|
|
|
++ if (elem->pcOffset() == offset) {
|
|
|
+ return elem;
|
|
|
+- if (elem != throwCounts_.begin())
|
|
|
++ }
|
|
|
++ if (elem != throwCounts_.begin()) {
|
|
|
+ return elem - 1;
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ js::PCCounts*
|
|
|
+ ScriptCounts::getThrowCounts(size_t offset) {
|
|
|
+ PCCounts searched = PCCounts(offset);
|
|
|
+ PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
|
|
|
+- if (elem == throwCounts_.end() || elem->pcOffset() != offset)
|
|
|
++ if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
|
|
|
+ elem = throwCounts_.insert(elem, searched);
|
|
|
++ }
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t
|
|
|
+ ScriptCounts::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
|
|
|
+ return mallocSizeOf(this) +
|
|
|
+ pcCounts_.sizeOfExcludingThis(mallocSizeOf) +
|
|
|
+ throwCounts_.sizeOfExcludingThis(mallocSizeOf) +
|
|
|
+ ionCounts_->sizeOfIncludingThis(mallocSizeOf);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
|
|
|
+- if (hasIonScript())
|
|
|
++ if (hasIonScript()) {
|
|
|
+ js::jit::IonScript::writeBarrierPre(zone(), ion);
|
|
|
++ }
|
|
|
+ ion = ionScript;
|
|
|
+ MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
|
|
|
+ updateJitCodeRaw(rt);
|
|
|
+ }
|
|
|
+
|
|
|
+ js::PCCounts*
|
|
|
+ JSScript::maybeGetPCCounts(jsbytecode* pc) {
|
|
|
+ MOZ_ASSERT(containsPC(pc));
|
|
|
+@@ -1222,59 +1326,67 @@ JSScript::getThrowCounts(jsbytecode* pc)
|
|
|
+ MOZ_ASSERT(containsPC(pc));
|
|
|
+ return getScriptCounts().getThrowCounts(pcToOffset(pc));
|
|
|
+ }
|
|
|
+
|
|
|
+ uint64_t
|
|
|
+ JSScript::getHitCount(jsbytecode* pc)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(containsPC(pc));
|
|
|
+- if (pc < main())
|
|
|
++ if (pc < main()) {
|
|
|
+ pc = main();
|
|
|
++ }
|
|
|
+
|
|
|
+ ScriptCounts& sc = getScriptCounts();
|
|
|
+ size_t targetOffset = pcToOffset(pc);
|
|
|
+ const js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(targetOffset);
|
|
|
+- if (!baseCount)
|
|
|
++ if (!baseCount) {
|
|
|
+ return 0;
|
|
|
+- if (baseCount->pcOffset() == targetOffset)
|
|
|
++ }
|
|
|
++ if (baseCount->pcOffset() == targetOffset) {
|
|
|
+ return baseCount->numExec();
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
|
|
|
+ uint64_t count = baseCount->numExec();
|
|
|
+ do {
|
|
|
+ const js::PCCounts* throwCount = sc.getImmediatePrecedingThrowCounts(targetOffset);
|
|
|
+- if (!throwCount)
|
|
|
++ if (!throwCount) {
|
|
|
+ return count;
|
|
|
+- if (throwCount->pcOffset() <= baseCount->pcOffset())
|
|
|
++ }
|
|
|
++ if (throwCount->pcOffset() <= baseCount->pcOffset()) {
|
|
|
+ return count;
|
|
|
++ }
|
|
|
+ count -= throwCount->numExec();
|
|
|
+ targetOffset = throwCount->pcOffset() - 1;
|
|
|
+ } while (true);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::incHitCount(jsbytecode* pc)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(containsPC(pc));
|
|
|
+- if (pc < main())
|
|
|
++ if (pc < main()) {
|
|
|
+ pc = main();
|
|
|
++ }
|
|
|
+
|
|
|
+ ScriptCounts& sc = getScriptCounts();
|
|
|
+ js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(pcToOffset(pc));
|
|
|
+- if (!baseCount)
|
|
|
++ if (!baseCount) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+ baseCount->numExec()++;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
|
|
|
+ {
|
|
|
+ ScriptCounts& sc = getScriptCounts();
|
|
|
+- if (sc.ionCounts_)
|
|
|
++ if (sc.ionCounts_) {
|
|
|
+ ionCounts->setPrevious(sc.ionCounts_);
|
|
|
++ }
|
|
|
+ sc.ionCounts_ = ionCounts;
|
|
|
+ }
|
|
|
+
|
|
|
+ jit::IonScriptCounts*
|
|
|
+ JSScript::getIonCounts()
|
|
|
+ {
|
|
|
+ return getScriptCounts().ionCounts_;
|
|
|
+ }
|
|
|
+@@ -1308,18 +1420,19 @@ JSScript::destroyScriptName()
|
|
|
+ {
|
|
|
+ auto p = GetScriptNameMapEntry(this);
|
|
|
+ realm()->scriptNameMap->remove(p);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::hasScriptName()
|
|
|
+ {
|
|
|
+- if (!realm()->scriptNameMap)
|
|
|
++ if (!realm()->scriptNameMap) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ auto p = realm()->scriptNameMap->lookup(this);
|
|
|
+ return p.found();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
|
|
|
+ {
|
|
|
+@@ -1349,18 +1462,19 @@ const Class ScriptSourceObject::class_ =
|
|
|
+ JSCLASS_FOREGROUND_FINALIZE,
|
|
|
+ &ScriptSourceObjectClassOps
|
|
|
+ };
|
|
|
+
|
|
|
+ ScriptSourceObject*
|
|
|
+ ScriptSourceObject::create(JSContext* cx, ScriptSource* source)
|
|
|
+ {
|
|
|
+ RootedScriptSourceObject sourceObject(cx, NewObjectWithGivenProto<ScriptSourceObject>(cx, nullptr));
|
|
|
+- if (!sourceObject)
|
|
|
++ if (!sourceObject) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ source->incref(); // The matching decref is in ScriptSourceObject::finalize.
|
|
|
+ sourceObject->initReservedSlot(SOURCE_SLOT, PrivateValue(source));
|
|
|
+
|
|
|
+ // The remaining slots should eventually be populated by a call to
|
|
|
+ // initFromOptions. Poison them until that point.
|
|
|
+ sourceObject->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
|
|
|
+ sourceObject->initReservedSlot(ELEMENT_PROPERTY_SLOT, MagicValue(JS_GENERIC_MAGIC));
|
|
|
+@@ -1375,18 +1489,19 @@ ScriptSourceObject::initFromOptions(JSCo
|
|
|
+ {
|
|
|
+ cx->releaseCheck(source);
|
|
|
+ MOZ_ASSERT(source->getReservedSlot(ELEMENT_SLOT).isMagic(JS_GENERIC_MAGIC));
|
|
|
+ MOZ_ASSERT(source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC));
|
|
|
+ MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC));
|
|
|
+
|
|
|
+ RootedObject element(cx, options.element());
|
|
|
+ RootedString elementAttributeName(cx, options.elementAttributeName());
|
|
|
+- if (!initElementProperties(cx, source, element, elementAttributeName))
|
|
|
++ if (!initElementProperties(cx, source, element, elementAttributeName)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // There is no equivalent of cross-compartment wrappers for scripts. If the
|
|
|
+ // introduction script and ScriptSourceObject are in different compartments,
|
|
|
+ // we would be creating a cross-compartment script reference, which is
|
|
|
+ // forbidden. In that case, simply don't bother to retain the introduction
|
|
|
+ // script.
|
|
|
+ Value introductionScript = UndefinedValue();
|
|
|
+ if (options.introductionScript() &&
|
|
|
+@@ -1399,46 +1514,53 @@ ScriptSourceObject::initFromOptions(JSCo
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ ScriptSourceObject::initElementProperties(JSContext* cx, HandleScriptSourceObject source,
|
|
|
+ HandleObject element, HandleString elementAttrName)
|
|
|
+ {
|
|
|
+ RootedValue elementValue(cx, ObjectOrNullValue(element));
|
|
|
+- if (!cx->compartment()->wrap(cx, &elementValue))
|
|
|
++ if (!cx->compartment()->wrap(cx, &elementValue)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ RootedValue nameValue(cx);
|
|
|
+- if (elementAttrName)
|
|
|
++ if (elementAttrName) {
|
|
|
+ nameValue = StringValue(elementAttrName);
|
|
|
+- if (!cx->compartment()->wrap(cx, &nameValue))
|
|
|
++ }
|
|
|
++ if (!cx->compartment()->wrap(cx, &nameValue)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ source->setReservedSlot(ELEMENT_SLOT, elementValue);
|
|
|
+ source->setReservedSlot(ELEMENT_PROPERTY_SLOT, nameValue);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!ss->hasSourceData());
|
|
|
+ *worked = false;
|
|
|
+- if (!cx->runtime()->sourceHook.ref() || !ss->sourceRetrievable())
|
|
|
++ if (!cx->runtime()->sourceHook.ref() || !ss->sourceRetrievable()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ char16_t* src = nullptr;
|
|
|
+ size_t length;
|
|
|
+- if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length))
|
|
|
++ if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length)) {
|
|
|
+ return false;
|
|
|
+- if (!src)
|
|
|
++ }
|
|
|
++ if (!src) {
|
|
|
+ return true;
|
|
|
+- if (!ss->setSource(cx, UniqueTwoByteChars(src), length))
|
|
|
++ }
|
|
|
++ if (!ss->setSource(cx, UniqueTwoByteChars(src), length)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ *worked = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ JSFlatString*
|
|
|
+ JSScript::sourceData(JSContext* cx, HandleScript script)
|
|
|
+ {
|
|
|
+@@ -1509,51 +1631,55 @@ UncompressedSourceCache::releaseEntry(Au
|
|
|
+ MOZ_ASSERT(holder_ == &holder);
|
|
|
+ holder_ = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char16_t*
|
|
|
+ UncompressedSourceCache::lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& holder)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!holder_);
|
|
|
+- if (!map_)
|
|
|
++ if (!map_) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ if (Map::Ptr p = map_->lookup(ssc)) {
|
|
|
+ holdEntry(holder, ssc);
|
|
|
+ return p->value().get();
|
|
|
+ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ UncompressedSourceCache::put(const ScriptSourceChunk& ssc, UniqueTwoByteChars str,
|
|
|
+ AutoHoldEntry& holder)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!holder_);
|
|
|
+
|
|
|
+ if (!map_) {
|
|
|
+ UniquePtr<Map> map = MakeUnique<Map>();
|
|
|
+- if (!map)
|
|
|
++ if (!map) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ map_ = std::move(map);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!map_->put(ssc, std::move(str)))
|
|
|
++ if (!map_->put(ssc, std::move(str))) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ holdEntry(holder, ssc);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ UncompressedSourceCache::purge()
|
|
|
+ {
|
|
|
+- if (!map_)
|
|
|
++ if (!map_) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
|
|
|
+ if (holder_ && r.front().key() == holder_->sourceChunk()) {
|
|
|
+ holder_->deferDelete(std::move(r.front().value()));
|
|
|
+ holder_ = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1561,31 +1687,33 @@ UncompressedSourceCache::purge()
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t
|
|
|
+ UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
|
|
+ {
|
|
|
+ size_t n = 0;
|
|
|
+ if (map_ && !map_->empty()) {
|
|
|
+ n += map_->shallowSizeOfIncludingThis(mallocSizeOf);
|
|
|
+- for (Map::Range r = map_->all(); !r.empty(); r.popFront())
|
|
|
++ for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
|
|
|
+ n += mallocSizeOf(r.front().value().get());
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char16_t*
|
|
|
+ ScriptSource::chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
|
|
|
+ size_t chunk)
|
|
|
+ {
|
|
|
+ const Compressed& c = data.as<Compressed>();
|
|
|
+
|
|
|
+ ScriptSourceChunk ssc(this, chunk);
|
|
|
+- if (const char16_t* decompressed = cx->caches().uncompressedSourceCache.lookup(ssc, holder))
|
|
|
++ if (const char16_t* decompressed = cx->caches().uncompressedSourceCache.lookup(ssc, holder)) {
|
|
|
+ return decompressed;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t totalLengthInBytes = length() * sizeof(char16_t);
|
|
|
+ size_t chunkBytes = Compressor::chunkSize(totalLengthInBytes, chunk);
|
|
|
+
|
|
|
+ MOZ_ASSERT((chunkBytes % sizeof(char16_t)) == 0);
|
|
|
+ const size_t lengthWithNull = (chunkBytes / sizeof(char16_t)) + 1;
|
|
|
+ UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
|
|
|
+ if (!decompressed) {
|
|
|
+@@ -1627,26 +1755,28 @@ ScriptSource::PinnedChars::PinnedChars(J
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ScriptSource::PinnedChars::~PinnedChars()
|
|
|
+ {
|
|
|
+ if (chars_) {
|
|
|
+ MOZ_ASSERT(*stack_ == this);
|
|
|
+ *stack_ = prev_;
|
|
|
+- if (!prev_)
|
|
|
++ if (!prev_) {
|
|
|
+ source_->movePendingCompressedSource();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ ScriptSource::movePendingCompressedSource()
|
|
|
+ {
|
|
|
+- if (!pendingCompressed_)
|
|
|
++ if (!pendingCompressed_) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
|
|
|
+ MOZ_ASSERT_IF(data.is<Uncompressed>(),
|
|
|
+ data.as<Uncompressed>().string.length() ==
|
|
|
+ pendingCompressed_->uncompressedLength);
|
|
|
+
|
|
|
+ data = SourceType(Compressed(std::move(pendingCompressed_->raw),
|
|
|
+ pendingCompressed_->uncompressedLength));
|
|
|
+@@ -1656,41 +1786,44 @@ ScriptSource::movePendingCompressedSourc
|
|
|
+ const char16_t*
|
|
|
+ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
|
|
|
+ size_t begin, size_t len)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(begin + len <= length());
|
|
|
+
|
|
|
+ if (data.is<Uncompressed>()) {
|
|
|
+ const char16_t* chars = data.as<Uncompressed>().string.chars();
|
|
|
+- if (!chars)
|
|
|
++ if (!chars) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return chars + begin;
|
|
|
+ }
|
|
|
+
|
|
|
+- if (data.is<Missing>())
|
|
|
++ if (data.is<Missing>()) {
|
|
|
+ MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(data.is<Compressed>());
|
|
|
+
|
|
|
+ // Determine which chunk(s) we are interested in, and the offsets within
|
|
|
+ // these chunks.
|
|
|
+ size_t firstChunk, lastChunk;
|
|
|
+ size_t firstChunkOffset, lastChunkOffset;
|
|
|
+ MOZ_ASSERT(len > 0);
|
|
|
+ Compressor::toChunkOffset(begin * sizeof(char16_t), &firstChunk, &firstChunkOffset);
|
|
|
+ Compressor::toChunkOffset((begin + len - 1) * sizeof(char16_t), &lastChunk, &lastChunkOffset);
|
|
|
+
|
|
|
+ MOZ_ASSERT(firstChunkOffset % sizeof(char16_t) == 0);
|
|
|
+ size_t firstChar = firstChunkOffset / sizeof(char16_t);
|
|
|
+
|
|
|
+ if (firstChunk == lastChunk) {
|
|
|
+ const char16_t* chars = chunkChars(cx, holder, firstChunk);
|
|
|
+- if (!chars)
|
|
|
++ if (!chars) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return chars + firstChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ // We need multiple chunks. Allocate a (null-terminated) buffer to hold
|
|
|
+ // |len| chars and copy uncompressed chars from the chunks into it. We use
|
|
|
+ // chunkChars() so we benefit from chunk caching by UncompressedSourceCache.
|
|
|
+
|
|
|
+ MOZ_ASSERT(firstChunk < lastChunk);
|
|
|
+@@ -1703,18 +1836,19 @@ ScriptSource::chars(JSContext* cx, Uncom
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t totalLengthInBytes = length() * sizeof(char16_t);
|
|
|
+ char16_t* cursor = decompressed.get();
|
|
|
+
|
|
|
+ for (size_t i = firstChunk; i <= lastChunk; i++) {
|
|
|
+ UncompressedSourceCache::AutoHoldEntry chunkHolder;
|
|
|
+ const char16_t* chars = chunkChars(cx, chunkHolder, i);
|
|
|
+- if (!chars)
|
|
|
++ if (!chars) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t numChars = Compressor::chunkSize(totalLengthInBytes, i) / sizeof(char16_t);
|
|
|
+ if (i == firstChunk) {
|
|
|
+ MOZ_ASSERT(firstChar < numChars);
|
|
|
+ chars += firstChar;
|
|
|
+ numChars -= firstChar;
|
|
|
+ } else if (i == lastChunk) {
|
|
|
+ size_t numCharsNew = lastChunkOffset / sizeof(char16_t) + 1;
|
|
|
+@@ -1736,44 +1870,48 @@ ScriptSource::chars(JSContext* cx, Uncom
|
|
|
+
|
|
|
+ JSFlatString*
|
|
|
+ ScriptSource::substring(JSContext* cx, size_t start, size_t stop)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(start <= stop);
|
|
|
+ size_t len = stop - start;
|
|
|
+ UncompressedSourceCache::AutoHoldEntry holder;
|
|
|
+ PinnedChars chars(cx, this, holder, start, len);
|
|
|
+- if (!chars.get())
|
|
|
++ if (!chars.get()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return NewStringCopyN<CanGC>(cx, chars.get(), len);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFlatString*
|
|
|
+ ScriptSource::substringDontDeflate(JSContext* cx, size_t start, size_t stop)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(start <= stop);
|
|
|
+ size_t len = stop - start;
|
|
|
+ UncompressedSourceCache::AutoHoldEntry holder;
|
|
|
+ PinnedChars chars(cx, this, holder, start, len);
|
|
|
+- if (!chars.get())
|
|
|
++ if (!chars.get()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return NewStringCopyNDontDeflate<CanGC>(cx, chars.get(), len);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, size_t stop)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(start <= stop);
|
|
|
+ size_t len = stop - start;
|
|
|
+ UncompressedSourceCache::AutoHoldEntry holder;
|
|
|
+ PinnedChars chars(cx, this, holder, start, len);
|
|
|
+- if (!chars.get())
|
|
|
++ if (!chars.get()) {
|
|
|
+ return false;
|
|
|
+- if (len > SourceDeflateLimit && !buf.ensureTwoByteChars())
|
|
|
++ }
|
|
|
++ if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return buf.append(chars.get(), len);
|
|
|
+ }
|
|
|
+
|
|
|
+ JSFlatString*
|
|
|
+ ScriptSource::functionBodyString(JSContext* cx)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(isFunctionBody());
|
|
|
+
|
|
|
+@@ -1800,46 +1938,49 @@ ScriptSource::setSource(SharedImmutableT
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(data.is<Missing>());
|
|
|
+ data = SourceType(Uncompressed(std::move(string)));
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::tryCompressOffThread(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (!data.is<Uncompressed>())
|
|
|
++ if (!data.is<Uncompressed>()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // There are several cases where source compression is not a good idea:
|
|
|
+ // - If the script is tiny, then compression will save little or no space.
|
|
|
+ // - If there is only one core, then compression will contend with JS
|
|
|
+ // execution (which hurts benchmarketing).
|
|
|
+ //
|
|
|
+ // Otherwise, enqueue a compression task to be processed when a major
|
|
|
+ // GC is requested.
|
|
|
+
|
|
|
+ bool canCompressOffThread =
|
|
|
+ HelperThreadState().cpuCount > 1 &&
|
|
|
+ HelperThreadState().threadCount >= 2 &&
|
|
|
+ CanUseExtraThreads();
|
|
|
+ const size_t TINY_SCRIPT = 256;
|
|
|
+- if (TINY_SCRIPT > length() || !canCompressOffThread)
|
|
|
++ if (TINY_SCRIPT > length() || !canCompressOffThread) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // The SourceCompressionTask needs to record the major GC number for
|
|
|
+ // scheduling. If we're parsing off thread, this number is not safe to
|
|
|
+ // access.
|
|
|
+ //
|
|
|
+ // When parsing on the main thread, the attempts made to compress off
|
|
|
+ // thread in BytecodeCompiler will succeed.
|
|
|
+ //
|
|
|
+ // When parsing off-thread, the above attempts will fail and the attempt
|
|
|
+ // made in ParseTask::finish will succeed.
|
|
|
+- if (!CurrentThreadCanAccessRuntime(cx->runtime()))
|
|
|
++ if (!CurrentThreadCanAccessRuntime(cx->runtime())) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Heap allocate the task. It will be freed upon compression
|
|
|
+ // completing in AttachFinishedCompressedSources.
|
|
|
+ auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), this);
|
|
|
+ if (!task) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+@@ -1862,20 +2003,21 @@ ScriptSource::setCompressedSource(JSCont
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ ScriptSource::setCompressedSource(SharedImmutableString&& raw, size_t uncompressedLength)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
|
|
|
+ MOZ_ASSERT_IF(data.is<Uncompressed>(),
|
|
|
+ data.as<Uncompressed>().string.length() == uncompressedLength);
|
|
|
+- if (pinnedCharsStack_)
|
|
|
++ if (pinnedCharsStack_) {
|
|
|
+ pendingCompressed_ = mozilla::Some(Compressed(std::move(raw), uncompressedLength));
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ data = SourceType(Compressed(std::move(raw), uncompressedLength));
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::setSourceCopy(JSContext* cx, SourceBufferHolder& srcBuf)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!hasSourceData());
|
|
|
+
|
|
|
+ JSRuntime* runtime = cx->zone()->runtimeFromAnyThread();
|
|
|
+@@ -1893,91 +2035,99 @@ ScriptSource::setSourceCopy(JSContext* c
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static MOZ_MUST_USE bool
|
|
|
+ reallocUniquePtr(UniqueChars& unique, size_t size)
|
|
|
+ {
|
|
|
+ auto newPtr = static_cast<char*>(js_realloc(unique.get(), size));
|
|
|
+- if (!newPtr)
|
|
|
++ if (!newPtr) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Since the realloc succeeded, unique is now holding a freed pointer.
|
|
|
+ mozilla::Unused << unique.release();
|
|
|
+ unique.reset(newPtr);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ SourceCompressionTask::work()
|
|
|
+ {
|
|
|
+- if (shouldCancel())
|
|
|
++ if (shouldCancel()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ ScriptSource* source = sourceHolder_.get();
|
|
|
+ MOZ_ASSERT(source->data.is<ScriptSource::Uncompressed>());
|
|
|
+
|
|
|
+ // Try to keep the maximum memory usage down by only allocating half the
|
|
|
+ // size of the string, first.
|
|
|
+ size_t inputBytes = source->length() * sizeof(char16_t);
|
|
|
+ size_t firstSize = inputBytes / 2;
|
|
|
+ UniqueChars compressed(js_pod_malloc<char>(firstSize));
|
|
|
+- if (!compressed)
|
|
|
++ if (!compressed) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ const char16_t* chars = source->data.as<ScriptSource::Uncompressed>().string.chars();
|
|
|
+ Compressor comp(reinterpret_cast<const unsigned char*>(chars),
|
|
|
+ inputBytes);
|
|
|
+- if (!comp.init())
|
|
|
++ if (!comp.init()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()), firstSize);
|
|
|
+ bool cont = true;
|
|
|
+ bool reallocated = false;
|
|
|
+ while (cont) {
|
|
|
+- if (shouldCancel())
|
|
|
++ if (shouldCancel()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ switch (comp.compressMore()) {
|
|
|
+ case Compressor::CONTINUE:
|
|
|
+ break;
|
|
|
+ case Compressor::MOREOUTPUT: {
|
|
|
+ if (reallocated) {
|
|
|
+ // The compressed string is longer than the original string.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The compressed output is greater than half the size of the
|
|
|
+ // original string. Reallocate to the full size.
|
|
|
+- if (!reallocUniquePtr(compressed, inputBytes))
|
|
|
++ if (!reallocUniquePtr(compressed, inputBytes)) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()), inputBytes);
|
|
|
+ reallocated = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case Compressor::DONE:
|
|
|
+ cont = false;
|
|
|
+ break;
|
|
|
+ case Compressor::OOM:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t totalBytes = comp.totalBytesNeeded();
|
|
|
+
|
|
|
+ // Shrink the buffer to the size of the compressed data.
|
|
|
+- if (!reallocUniquePtr(compressed, totalBytes))
|
|
|
++ if (!reallocUniquePtr(compressed, totalBytes)) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ comp.finish(compressed.get(), totalBytes);
|
|
|
+
|
|
|
+- if (shouldCancel())
|
|
|
++ if (shouldCancel()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ auto& strings = runtime_->sharedImmutableStrings();
|
|
|
+ resultString_ = strings.getOrCreate(std::move(compressed), totalBytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ SourceCompressionTask::complete()
|
|
|
+ {
|
|
|
+@@ -1996,18 +2146,19 @@ ScriptSource::addSizeOfIncludingThis(moz
|
|
|
+ mallocSizeOf(introducerFilename_.get());
|
|
|
+ info->numScripts++;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::xdrEncodeTopLevel(JSContext* cx, HandleScript script)
|
|
|
+ {
|
|
|
+ // Encoding failures are reported by the xdrFinalizeEncoder function.
|
|
|
+- if (containsAsmJS())
|
|
|
++ if (containsAsmJS()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx);
|
|
|
+ if (!xdrEncoder_) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(hasEncoder());
|
|
|
+@@ -2015,18 +2166,19 @@ ScriptSource::xdrEncodeTopLevel(JSContex
|
|
|
+ xdrEncoder_.reset(nullptr);
|
|
|
+ });
|
|
|
+
|
|
|
+ RootedScript s(cx, script);
|
|
|
+ XDRResult res = xdrEncoder_->codeScript(&s);
|
|
|
+ if (res.isErr()) {
|
|
|
+ // On encoding failure, let failureCase destroy encoder and return true
|
|
|
+ // to avoid failing any currently executing script.
|
|
|
+- if (res.unwrapErr() & JS::TranscodeResult_Failure)
|
|
|
++ if (res.unwrapErr() & JS::TranscodeResult_Failure) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ failureCase.release();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2040,30 +2192,32 @@ ScriptSource::xdrEncodeFunction(JSContex
|
|
|
+ xdrEncoder_.reset(nullptr);
|
|
|
+ });
|
|
|
+
|
|
|
+ RootedFunction f(cx, fun);
|
|
|
+ XDRResult res = xdrEncoder_->codeFunction(&f, sourceObject);
|
|
|
+ if (res.isErr()) {
|
|
|
+ // On encoding failure, let failureCase destroy encoder and return true
|
|
|
+ // to avoid failing any currently executing script.
|
|
|
+- if (res.unwrapErr() & JS::TranscodeResult_Failure)
|
|
|
++ if (res.unwrapErr() & JS::TranscodeResult_Failure) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ failureCase.release();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::xdrFinalizeEncoder(JS::TranscodeBuffer& buffer)
|
|
|
+ {
|
|
|
+- if (!hasEncoder())
|
|
|
++ if (!hasEncoder()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ auto cleanup = mozilla::MakeScopeExit([&] {
|
|
|
+ xdrEncoder_.reset(nullptr);
|
|
|
+ });
|
|
|
+
|
|
|
+ XDRResult res = xdrEncoder_->linearize(buffer);
|
|
|
+ return res.isOk();
|
|
|
+ }
|
|
|
+@@ -2108,41 +2262,45 @@ ScriptSource::performXDR(XDRState<mode>*
|
|
|
+ MOZ_TRY(xdr->codeUint8(&hasSource));
|
|
|
+
|
|
|
+ uint8_t retrievable = sourceRetrievable_;
|
|
|
+ MOZ_TRY(xdr->codeUint8(&retrievable));
|
|
|
+ sourceRetrievable_ = retrievable;
|
|
|
+
|
|
|
+ if (hasSource && !sourceRetrievable_) {
|
|
|
+ uint32_t len = 0;
|
|
|
+- if (mode == XDR_ENCODE)
|
|
|
++ if (mode == XDR_ENCODE) {
|
|
|
+ len = length();
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&len));
|
|
|
+
|
|
|
+ uint32_t compressedLength;
|
|
|
+ if (mode == XDR_ENCODE) {
|
|
|
+ CompressedLengthMatcher m;
|
|
|
+ compressedLength = data.match(m);
|
|
|
+ }
|
|
|
+ MOZ_TRY(xdr->codeUint32(&compressedLength));
|
|
|
+
|
|
|
+ size_t byteLen = compressedLength ? compressedLength : (len * sizeof(char16_t));
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ auto bytes = xdr->cx()->template make_pod_array<char>(Max<size_t>(byteLen, 1));
|
|
|
+- if (!bytes)
|
|
|
++ if (!bytes) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ MOZ_TRY(xdr->codeBytes(bytes.get(), byteLen));
|
|
|
+
|
|
|
+ if (compressedLength) {
|
|
|
+- if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len))
|
|
|
++ if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ UniqueTwoByteChars source(reinterpret_cast<char16_t*>(bytes.release()));
|
|
|
+- if (!setSource(xdr->cx(), std::move(source), len))
|
|
|
++ if (!setSource(xdr->cx(), std::move(source), len)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ RawDataMatcher rdm;
|
|
|
+ void* p = data.match(rdm);
|
|
|
+ MOZ_TRY(xdr->codeBytes(p, byteLen));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2150,67 +2308,73 @@ ScriptSource::performXDR(XDRState<mode>*
|
|
|
+ MOZ_TRY(xdr->codeUint8(&haveSourceMap));
|
|
|
+
|
|
|
+ if (haveSourceMap) {
|
|
|
+ uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_.get());
|
|
|
+ MOZ_TRY(xdr->codeUint32(&sourceMapURLLen));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ sourceMapURL_ = xdr->cx()->template make_pod_array<char16_t>(sourceMapURLLen + 1);
|
|
|
+- if (!sourceMapURL_)
|
|
|
++ if (!sourceMapURL_) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ auto guard = mozilla::MakeScopeExit([&] {
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ sourceMapURL_ = nullptr;
|
|
|
++ }
|
|
|
+ });
|
|
|
+ MOZ_TRY(xdr->codeChars(sourceMapURL_.get(), sourceMapURLLen));
|
|
|
+ guard.release();
|
|
|
+ sourceMapURL_[sourceMapURLLen] = '\0';
|
|
|
+ }
|
|
|
+
|
|
|
+ uint8_t haveDisplayURL = hasDisplayURL();
|
|
|
+ MOZ_TRY(xdr->codeUint8(&haveDisplayURL));
|
|
|
+
|
|
|
+ if (haveDisplayURL) {
|
|
|
+ uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_.get());
|
|
|
+ MOZ_TRY(xdr->codeUint32(&displayURLLen));
|
|
|
+
|
|
|
+ if (mode == XDR_DECODE) {
|
|
|
+ displayURL_ = xdr->cx()->template make_pod_array<char16_t>(displayURLLen + 1);
|
|
|
+- if (!displayURL_)
|
|
|
++ if (!displayURL_) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ auto guard = mozilla::MakeScopeExit([&] {
|
|
|
+- if (mode == XDR_DECODE)
|
|
|
++ if (mode == XDR_DECODE) {
|
|
|
+ displayURL_ = nullptr;
|
|
|
++ }
|
|
|
+ });
|
|
|
+ MOZ_TRY(xdr->codeChars(displayURL_.get(), displayURLLen));
|
|
|
+ guard.release();
|
|
|
+ displayURL_[displayURLLen] = '\0';
|
|
|
+ }
|
|
|
+
|
|
|
+ uint8_t haveFilename = !!filename_;
|
|
|
+ MOZ_TRY(xdr->codeUint8(&haveFilename));
|
|
|
+
|
|
|
+ if (haveFilename) {
|
|
|
+ const char* fn = filename();
|
|
|
+ MOZ_TRY(xdr->codeCString(&fn));
|
|
|
+ // Note: If the decoder has an option, then the filename is defined by
|
|
|
+ // the CompileOption from the document.
|
|
|
+ MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
|
|
|
+- if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
|
|
|
++ if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn)) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Note the content of sources decoded when recording or replaying.
|
|
|
+ if (mode == XDR_DECODE && hasSourceData() && mozilla::recordreplay::IsRecordingOrReplaying()) {
|
|
|
+ UncompressedSourceCache::AutoHoldEntry holder;
|
|
|
+ ScriptSource::PinnedChars chars(xdr->cx(), this, holder, 0, length());
|
|
|
+- if (!chars.get())
|
|
|
++ if (!chars.get()) {
|
|
|
+ return xdr->fail(JS::TranscodeResult_Throw);
|
|
|
++ }
|
|
|
+ mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
|
|
|
+ chars.get(), length());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2235,18 +2399,19 @@ js::FormatIntroducedFilename(JSContext*
|
|
|
+ size_t introducerLen = strlen(introducer);
|
|
|
+ size_t len = filenameLen +
|
|
|
+ 6 /* == strlen(" line ") */ +
|
|
|
+ linenoLen +
|
|
|
+ 3 /* == strlen(" > ") */ +
|
|
|
+ introducerLen +
|
|
|
+ 1 /* \0 */;
|
|
|
+ char* formatted = cx->pod_malloc<char>(len);
|
|
|
+- if (!formatted)
|
|
|
++ if (!formatted) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ mozilla::DebugOnly<size_t> checkLen = snprintf(formatted, len, "%s line %s > %s",
|
|
|
+ filename, linenoBuf, introducer);
|
|
|
+ MOZ_ASSERT(checkLen == len - 1);
|
|
|
+
|
|
|
+ return formatted;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2263,28 +2428,31 @@ ScriptSource::initFromOptions(JSContext*
|
|
|
+ setIntroductionOffset(options.introductionOffset);
|
|
|
+ parameterListEnd_ = parameterListEnd.isSome() ? parameterListEnd.value() : 0;
|
|
|
+
|
|
|
+ if (options.hasIntroductionInfo) {
|
|
|
+ MOZ_ASSERT(options.introductionType != nullptr);
|
|
|
+ const char* filename = options.filename() ? options.filename() : "<unknown>";
|
|
|
+ char* formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno,
|
|
|
+ options.introductionType);
|
|
|
+- if (!formatted)
|
|
|
++ if (!formatted) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ filename_.reset(formatted);
|
|
|
+ } else if (options.filename()) {
|
|
|
+- if (!setFilename(cx, options.filename()))
|
|
|
++ if (!setFilename(cx, options.filename())) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (options.introducerFilename()) {
|
|
|
+ introducerFilename_ = DuplicateString(cx, options.introducerFilename());
|
|
|
+- if (!introducerFilename_)
|
|
|
++ if (!introducerFilename_) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::setFilename(JSContext* cx, const char* filename)
|
|
|
+ {
|
|
|
+@@ -2304,31 +2472,33 @@ ScriptSource::setDisplayURL(JSContext* c
|
|
|
+ GetErrorMessage, nullptr,
|
|
|
+ JSMSG_ALREADY_HAS_PRAGMA, filename_.get(),
|
|
|
+ "//# sourceURL"))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ size_t len = js_strlen(displayURL) + 1;
|
|
|
+- if (len == 1)
|
|
|
++ if (len == 1) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ displayURL_ = DuplicateString(cx, displayURL);
|
|
|
+ return displayURL_ != nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ ScriptSource::setSourceMapURL(JSContext* cx, const char16_t* sourceMapURL)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(sourceMapURL);
|
|
|
+
|
|
|
+ size_t len = js_strlen(sourceMapURL) + 1;
|
|
|
+- if (len == 1)
|
|
|
++ if (len == 1) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ sourceMapURL_ = DuplicateString(cx, sourceMapURL);
|
|
|
+ return sourceMapURL_ != nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * [SMDOC] JSScript data layout (shared)
|
|
|
+ *
|
|
|
+@@ -2367,18 +2537,19 @@ js::SharedScriptData::new_(JSContext* cx
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Call constructors to initialize the storage that will be accessed as a
|
|
|
+ * GCPtrAtom array via atoms().
|
|
|
+ */
|
|
|
+ static_assert(offsetof(SharedScriptData, data_) % sizeof(GCPtrAtom) == 0,
|
|
|
+ "atoms must have GCPtrAtom alignment");
|
|
|
+ GCPtrAtom* atoms = entry->atoms();
|
|
|
+- for (unsigned i = 0; i < natoms; ++i)
|
|
|
++ for (unsigned i = 0; i < natoms; ++i) {
|
|
|
+ new (&atoms[i]) GCPtrAtom();
|
|
|
++ }
|
|
|
+
|
|
|
+ // Sanity check the dataLength() computation
|
|
|
+ MOZ_ASSERT(entry->dataLength() == dataLength);
|
|
|
+
|
|
|
+ return entry;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline
|
|
|
+@@ -2396,18 +2567,19 @@ js::ScriptBytecodeHasher::Lookup::~Looku
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::createScriptData(JSContext* cx, uint32_t codeLength, uint32_t srcnotesLength,
|
|
|
+ uint32_t natoms)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!scriptData());
|
|
|
+ SharedScriptData* ssd = SharedScriptData::new_(cx, codeLength, srcnotesLength, natoms);
|
|
|
+- if (!ssd)
|
|
|
++ if (!ssd) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ setScriptData(ssd);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::freeScriptData()
|
|
|
+ {
|
|
|
+@@ -2597,26 +2769,31 @@ JS_STATIC_ASSERT((NoPaddingBetweenEntrie
|
|
|
+ static inline size_t
|
|
|
+ ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects,
|
|
|
+ uint32_t ntrynotes, uint32_t nscopenotes, uint32_t nyieldoffsets)
|
|
|
+ {
|
|
|
+ size_t size = 0;
|
|
|
+
|
|
|
+ MOZ_ASSERT(nscopes != 0);
|
|
|
+ size += sizeof(ScopeArray) + nscopes * sizeof(Scope*);
|
|
|
+- if (nconsts != 0)
|
|
|
++ if (nconsts != 0) {
|
|
|
+ size += sizeof(ConstArray) + nconsts * sizeof(Value);
|
|
|
+- if (nobjects != 0)
|
|
|
++ }
|
|
|
++ if (nobjects != 0) {
|
|
|
+ size += sizeof(ObjectArray) + nobjects * sizeof(NativeObject*);
|
|
|
+- if (ntrynotes != 0)
|
|
|
++ }
|
|
|
++ if (ntrynotes != 0) {
|
|
|
+ size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
|
|
|
+- if (nscopenotes != 0)
|
|
|
++ }
|
|
|
++ if (nscopenotes != 0) {
|
|
|
+ size += sizeof(ScopeNoteArray) + nscopenotes * sizeof(ScopeNote);
|
|
|
+- if (nyieldoffsets != 0)
|
|
|
++ }
|
|
|
++ if (nyieldoffsets != 0) {
|
|
|
+ size += sizeof(YieldAndAwaitOffsetArray) + nyieldoffsets * sizeof(uint32_t);
|
|
|
++ }
|
|
|
+
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSScript::JSScript(JS::Realm* realm, uint8_t* stubEntry, const ReadOnlyCompileOptions& options,
|
|
|
+ HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
|
|
|
+ uint32_t toStringStart, uint32_t toStringEnd)
|
|
|
+ :
|
|
|
+@@ -2652,18 +2829,19 @@ JSScript::JSScript(JS::Realm* realm, uin
|
|
|
+
|
|
|
+ /* static */ JSScript*
|
|
|
+ JSScript::createInitialized(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|
|
+ HandleObject sourceObject,
|
|
|
+ uint32_t bufStart, uint32_t bufEnd,
|
|
|
+ uint32_t toStringStart, uint32_t toStringEnd)
|
|
|
+ {
|
|
|
+ void* script = Allocate<JSScript>(cx);
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ uint8_t* stubEntry =
|
|
|
+ #ifndef JS_CODEGEN_NONE
|
|
|
+ cx->runtime()->jitRuntime()->interpreterStub().value
|
|
|
+ #else
|
|
|
+ nullptr
|
|
|
+ #endif
|
|
|
+ ;
|
|
|
+@@ -2674,40 +2852,44 @@ JSScript::createInitialized(JSContext* c
|
|
|
+
|
|
|
+ /* static */ JSScript*
|
|
|
+ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|
|
+ HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
|
|
|
+ uint32_t toStringStart, uint32_t toStringEnd)
|
|
|
+ {
|
|
|
+ RootedScript script(cx, createInitialized(cx, options, sourceObject, bufStart, bufEnd,
|
|
|
+ toStringStart, toStringEnd));
|
|
|
+- if (!script)
|
|
|
++ if (!script) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (cx->runtime()->lcovOutput().isEnabled()) {
|
|
|
+- if (!script->initScriptName(cx))
|
|
|
++ if (!script->initScriptName(cx)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return script;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::initScriptName(JSContext* cx)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!hasScriptName());
|
|
|
+
|
|
|
+- if (!filename())
|
|
|
++ if (!filename()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Create realm's scriptNameMap if necessary.
|
|
|
+ if (!realm()->scriptNameMap) {
|
|
|
+ auto map = cx->make_unique<ScriptNameMap>();
|
|
|
+- if (!map)
|
|
|
++ if (!map) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ realm()->scriptNameMap = std::move(map);
|
|
|
+ }
|
|
|
+
|
|
|
+ UniqueChars name = DuplicateString(filename());
|
|
|
+ if (!name) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+@@ -2720,38 +2902,41 @@ JSScript::initScriptName(JSContext* cx)
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline uint8_t*
|
|
|
+ AllocScriptData(JSContext* cx, size_t size)
|
|
|
+ {
|
|
|
+- if (!size)
|
|
|
++ if (!size) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ uint8_t* data = cx->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
|
|
|
+- if (!data)
|
|
|
++ if (!data) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ MOZ_ASSERT(size_t(data) % sizeof(Value) == 0);
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSScript::partiallyInit(JSContext* cx, HandleScript script, uint32_t nscopes,
|
|
|
+ uint32_t nconsts, uint32_t nobjects, uint32_t ntrynotes,
|
|
|
+ uint32_t nscopenotes, uint32_t nyieldoffsets)
|
|
|
+ {
|
|
|
+ cx->check(script);
|
|
|
+
|
|
|
+ size_t size = ScriptDataSize(nscopes, nconsts, nobjects, ntrynotes,
|
|
|
+ nscopenotes, nyieldoffsets);
|
|
|
+ script->data = AllocScriptData(cx, size);
|
|
|
+- if (size && !script->data)
|
|
|
++ if (size && !script->data) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ script->dataSize_ = size;
|
|
|
+
|
|
|
+ uint8_t* cursor = script->data;
|
|
|
+
|
|
|
+ // There must always be at least 1 scope, the body scope.
|
|
|
+ MOZ_ASSERT(nscopes != 0);
|
|
|
+ cursor += sizeof(ScopeArray);
|
|
|
+@@ -2846,25 +3031,27 @@ JSScript::initFunctionPrototype(JSContex
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ script->nTypeSets_ = 0;
|
|
|
+
|
|
|
+ RootedScope enclosing(cx, &cx->global()->emptyGlobalScope());
|
|
|
+ Scope* functionProtoScope = FunctionScope::create(cx, nullptr, false, false, functionProto,
|
|
|
+ enclosing);
|
|
|
+- if (!functionProtoScope)
|
|
|
++ if (!functionProtoScope) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ script->scopes()->vector[0].init(functionProtoScope);
|
|
|
+
|
|
|
+ uint32_t codeLength = 1;
|
|
|
+ uint32_t srcNotesLength = 1;
|
|
|
+ uint32_t numAtoms = 0;
|
|
|
+- if (!script->createScriptData(cx, codeLength, srcNotesLength, numAtoms))
|
|
|
++ if (!script->createScriptData(cx, codeLength, srcNotesLength, numAtoms)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ jsbytecode* code = script->code();
|
|
|
+ code[0] = JSOP_RETRVAL;
|
|
|
+ code[1] = SRC_NULL;
|
|
|
+ return script->shareScriptData(cx);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+@@ -2877,47 +3064,51 @@ InitAtomMap(frontend::AtomIndexMap& indi
|
|
|
+ atoms[index].init(atom);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ void
|
|
|
+ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox)
|
|
|
+ {
|
|
|
+ JSFunction* fun = funbox->function();
|
|
|
+- if (fun->isInterpretedLazy())
|
|
|
++ if (fun->isInterpretedLazy()) {
|
|
|
+ fun->setUnlazifiedScript(script);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun->setScript(script);
|
|
|
++ }
|
|
|
+
|
|
|
+ script->bitFields_.funHasExtensibleScope_ = funbox->hasExtensibleScope();
|
|
|
+ script->bitFields_.needsHomeObject_ = funbox->needsHomeObject();
|
|
|
+ script->bitFields_.isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
|
|
|
+
|
|
|
+ if (funbox->argumentsHasLocalBinding()) {
|
|
|
+ script->setArgumentsHasVarBinding();
|
|
|
+- if (funbox->definitelyNeedsArgsObj())
|
|
|
++ if (funbox->definitelyNeedsArgsObj()) {
|
|
|
+ script->setNeedsArgsObj(true);
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
|
|
|
+ }
|
|
|
+ script->bitFields_.hasMappedArgsObj_ = funbox->hasMappedArgsObj();
|
|
|
+
|
|
|
+ script->bitFields_.functionHasThisBinding_ = funbox->hasThisBinding();
|
|
|
+ script->bitFields_.functionHasExtraBodyVarScope_ = funbox->hasExtraBodyVarScope();
|
|
|
+
|
|
|
+ script->funLength_ = funbox->length;
|
|
|
+
|
|
|
+ script->setGeneratorKind(funbox->generatorKind());
|
|
|
+ script->setAsyncKind(funbox->asyncKind());
|
|
|
+- if (funbox->hasRest())
|
|
|
++ if (funbox->hasRest()) {
|
|
|
+ script->setHasRest();
|
|
|
++ }
|
|
|
+
|
|
|
+ PositionalFormalParameterIter fi(script);
|
|
|
+- while (fi && !fi.closedOver())
|
|
|
++ while (fi && !fi.closedOver()) {
|
|
|
+ fi++;
|
|
|
++ }
|
|
|
+ script->bitFields_.funHasAnyAliasedFormal_ = !!fi;
|
|
|
+
|
|
|
+ script->setHasInnerFunctions(funbox->hasInnerFunctions());
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ void
|
|
|
+ JSScript::initFromModuleContext(HandleScript script)
|
|
|
+ {
|
|
|
+@@ -2945,82 +3136,92 @@ JSScript::fullyInitFromEmitter(JSContext
|
|
|
+ if (nslots > UINT32_MAX) {
|
|
|
+ bce->reportError(nullptr, JSMSG_NEED_DIET, js_script_str);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t mainLength = bce->offset();
|
|
|
+ uint32_t prologueLength = bce->prologueOffset();
|
|
|
+ uint32_t nsrcnotes;
|
|
|
+- if (!bce->finishTakingSrcNotes(&nsrcnotes))
|
|
|
++ if (!bce->finishTakingSrcNotes(&nsrcnotes)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ uint32_t natoms = bce->atomIndices->count();
|
|
|
+ if (!partiallyInit(cx, script,
|
|
|
+ bce->scopeList.length(), bce->numberList.length(), bce->objectList.length,
|
|
|
+ bce->tryNoteList.length(), bce->scopeNoteList.length(),
|
|
|
+ bce->yieldAndAwaitOffsetList.length()))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(script->mainOffset() == 0);
|
|
|
+ script->mainOffset_ = prologueLength;
|
|
|
+ script->nTypeSets_ = bce->typesetCount;
|
|
|
+ script->lineno_ = bce->firstLine;
|
|
|
+
|
|
|
+- if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms))
|
|
|
++ if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Any fallible operation after JSScript::createScriptData should reset
|
|
|
+ // JSScript.scriptData_, in order to treat this script as uncompleted,
|
|
|
+ // in JSScript::isUncompleted.
|
|
|
+ // JSScript::shareScriptData resets it before returning false.
|
|
|
+
|
|
|
+ jsbytecode* code = script->code();
|
|
|
+ PodCopy<jsbytecode>(code, bce->prologue.code.begin(), prologueLength);
|
|
|
+ PodCopy<jsbytecode>(code + prologueLength, bce->main.code.begin(), mainLength);
|
|
|
+ bce->copySrcNotes((jssrcnote*)(code + script->length()), nsrcnotes);
|
|
|
+ InitAtomMap(*bce->atomIndices, script->atoms());
|
|
|
+
|
|
|
+- if (!script->shareScriptData(cx))
|
|
|
++ if (!script->shareScriptData(cx)) {
|
|
|
+ return false;
|
|
|
+-
|
|
|
+- if (bce->numberList.length() != 0)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (bce->numberList.length() != 0) {
|
|
|
+ bce->numberList.finish(script->consts());
|
|
|
+- if (bce->objectList.length != 0)
|
|
|
++ }
|
|
|
++ if (bce->objectList.length != 0) {
|
|
|
+ bce->objectList.finish(script->objects());
|
|
|
+- if (bce->scopeList.length() != 0)
|
|
|
++ }
|
|
|
++ if (bce->scopeList.length() != 0) {
|
|
|
+ bce->scopeList.finish(script->scopes());
|
|
|
+- if (bce->tryNoteList.length() != 0)
|
|
|
++ }
|
|
|
++ if (bce->tryNoteList.length() != 0) {
|
|
|
+ bce->tryNoteList.finish(script->trynotes());
|
|
|
+- if (bce->scopeNoteList.length() != 0)
|
|
|
++ }
|
|
|
++ if (bce->scopeNoteList.length() != 0) {
|
|
|
+ bce->scopeNoteList.finish(script->scopeNotes(), prologueLength);
|
|
|
++ }
|
|
|
+ script->bitFields_.strict_ = bce->sc->strict();
|
|
|
+ script->bitFields_.explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
|
|
|
+ script->bitFields_.bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
|
|
|
+ script->bitFields_.hasSingletons_ = bce->hasSingletons;
|
|
|
+
|
|
|
+ script->nfixed_ = bce->maxFixedSlots;
|
|
|
+ script->nslots_ = nslots;
|
|
|
+ script->bodyScopeIndex_ = bce->bodyScopeIndex;
|
|
|
+ script->bitFields_.hasNonSyntacticScope_ =
|
|
|
+ bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic);
|
|
|
+
|
|
|
+ // There shouldn't be any fallible operation after initFromFunctionBox,
|
|
|
+ // JSFunction::hasUncompletedScript relies on the fact that the existence
|
|
|
+ // of the pointer to JSScript means the pointed JSScript is complete.
|
|
|
+- if (bce->sc->isFunctionBox())
|
|
|
++ if (bce->sc->isFunctionBox()) {
|
|
|
+ initFromFunctionBox(script, bce->sc->asFunctionBox());
|
|
|
+- else if (bce->sc->isModuleContext())
|
|
|
++ } else if (bce->sc->isModuleContext()) {
|
|
|
+ initFromModuleContext(script);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Copy yield offsets last, as the generator kind is set in
|
|
|
+ // initFromFunctionBox.
|
|
|
+- if (bce->yieldAndAwaitOffsetList.length() != 0)
|
|
|
++ if (bce->yieldAndAwaitOffsetList.length() != 0) {
|
|
|
+ bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength);
|
|
|
++ }
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ script->assertValidJumpTargets();
|
|
|
+ #endif
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3071,18 +3272,19 @@ JSScript::assertValidJumpTargets() const
|
|
|
+
|
|
|
+ // Check catch/finally blocks as jump targets.
|
|
|
+ if (hasTrynotes()) {
|
|
|
+ JSTryNote* tn = trynotes()->vector;
|
|
|
+ JSTryNote* tnlimit = tn + trynotes()->length;
|
|
|
+ for (; tn < tnlimit; tn++) {
|
|
|
+ jsbytecode* tryStart = mainEntry + tn->start;
|
|
|
+ jsbytecode* tryPc = tryStart - 1;
|
|
|
+- if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY)
|
|
|
++ if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(JSOp(*tryPc) == JSOP_TRY);
|
|
|
+ jsbytecode* tryTarget = tryStart + tn->length;
|
|
|
+ MOZ_ASSERT(mainEntry <= tryTarget && tryTarget < end);
|
|
|
+ MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*tryTarget)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -3125,31 +3327,33 @@ JSScript::finalize(FreeOp* fop)
|
|
|
+ MOZ_ASSERT_IF(hasScriptName(), fop->runtime()->lcovOutput().isEnabled());
|
|
|
+ if (fop->runtime()->lcovOutput().isEnabled() && hasScriptName()) {
|
|
|
+ realm()->lcovOutput.collectCodeCoverageInfo(realm(), this, getScriptName());
|
|
|
+ destroyScriptName();
|
|
|
+ }
|
|
|
+
|
|
|
+ fop->runtime()->geckoProfiler().onScriptFinalized(this);
|
|
|
+
|
|
|
+- if (types_)
|
|
|
++ if (types_) {
|
|
|
+ types_->destroy();
|
|
|
++ }
|
|
|
+
|
|
|
+ jit::DestroyJitScripts(fop, this);
|
|
|
+
|
|
|
+ destroyScriptCounts();
|
|
|
+ destroyDebugScript(fop);
|
|
|
+
|
|
|
+ if (data) {
|
|
|
+ JS_POISON(data, 0xdb, computedSizeOfData(), MemCheckKind::MakeNoAccess);
|
|
|
+ fop->free_(data);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (scriptData_)
|
|
|
++ if (scriptData_) {
|
|
|
+ scriptData_->decRefCount();
|
|
|
++ }
|
|
|
+
|
|
|
+ // In most cases, our LazyScript's script pointer will reference this
|
|
|
+ // script, and thus be nulled out by normal weakref processing. However, if
|
|
|
+ // we unlazified the LazyScript during incremental sweeping, it will have a
|
|
|
+ // completely different JSScript.
|
|
|
+ MOZ_ASSERT_IF(lazyScript && !IsAboutToBeFinalizedUnbarriered(&lazyScript),
|
|
|
+ !lazyScript->hasScript() || lazyScript->maybeScriptUnbarriered() != this);
|
|
|
+ }
|
|
|
+@@ -3162,18 +3366,19 @@ GSNCache::purge()
|
|
|
+ code = nullptr;
|
|
|
+ map.clearAndCompact();
|
|
|
+ }
|
|
|
+
|
|
|
+ jssrcnote*
|
|
|
+ js::GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc)
|
|
|
+ {
|
|
|
+ size_t target = pc - script->code();
|
|
|
+- if (target >= script->length())
|
|
|
++ if (target >= script->length()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (cache.code == script->code()) {
|
|
|
+ GSNCache::Map::Ptr p = cache.map.lookup(pc);
|
|
|
+ return p ? p->value() : nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t offset = 0;
|
|
|
+ jssrcnote* result;
|
|
|
+@@ -3187,31 +3392,33 @@ js::GetSrcNote(GSNCache& cache, JSScript
|
|
|
+ result = sn;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) {
|
|
|
+ unsigned nsrcnotes = 0;
|
|
|
+ for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
|
|
+- if (SN_IS_GETTABLE(sn))
|
|
|
++ if (SN_IS_GETTABLE(sn)) {
|
|
|
+ ++nsrcnotes;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (cache.code) {
|
|
|
+ cache.map.clear();
|
|
|
+ cache.code = nullptr;
|
|
|
+ }
|
|
|
+ if (cache.map.reserve(nsrcnotes)) {
|
|
|
+ pc = script->code();
|
|
|
+ for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
|
|
|
+ sn = SN_NEXT(sn))
|
|
|
+ {
|
|
|
+ pc += SN_DELTA(sn);
|
|
|
+- if (SN_IS_GETTABLE(sn))
|
|
|
++ if (SN_IS_GETTABLE(sn)) {
|
|
|
+ cache.map.putNewInfallible(pc, sn);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ cache.code = script->code();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3232,98 +3439,105 @@ js::PCToLineNumber(unsigned startLine, j
|
|
|
+ * Walk through source notes accumulating their deltas, keeping track of
|
|
|
+ * line-number notes, until we pass the note for pc's offset within
|
|
|
+ * script->code.
|
|
|
+ */
|
|
|
+ ptrdiff_t offset = 0;
|
|
|
+ ptrdiff_t target = pc - code;
|
|
|
+ for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
|
|
+ offset += SN_DELTA(sn);
|
|
|
+- if (offset > target)
|
|
|
++ if (offset > target) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+
|
|
|
+ SrcNoteType type = SN_TYPE(sn);
|
|
|
+ if (type == SRC_SETLINE) {
|
|
|
+ lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
|
|
|
+ column = 0;
|
|
|
+ } else if (type == SRC_NEWLINE) {
|
|
|
+ lineno++;
|
|
|
+ column = 0;
|
|
|
+ } else if (type == SRC_COLSPAN) {
|
|
|
+ ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span));
|
|
|
+ MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
|
|
|
+ column += colspan;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (columnp)
|
|
|
++ if (columnp) {
|
|
|
+ *columnp = column;
|
|
|
++ }
|
|
|
+
|
|
|
+ return lineno;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned
|
|
|
+ js::PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp)
|
|
|
+ {
|
|
|
+ /* Cope with InterpreterFrame.pc value prior to entering Interpret. */
|
|
|
+- if (!pc)
|
|
|
++ if (!pc) {
|
|
|
+ return 0;
|
|
|
++ }
|
|
|
+
|
|
|
+ return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp);
|
|
|
+ }
|
|
|
+
|
|
|
+ jsbytecode*
|
|
|
+ js::LineNumberToPC(JSScript* script, unsigned target)
|
|
|
+ {
|
|
|
+ ptrdiff_t offset = 0;
|
|
|
+ ptrdiff_t best = -1;
|
|
|
+ unsigned lineno = script->lineno();
|
|
|
+ unsigned bestdiff = SN_MAX_OFFSET;
|
|
|
+ for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
|
|
+ /*
|
|
|
+ * Exact-match only if offset is not in the prologue; otherwise use
|
|
|
+ * nearest greater-or-equal line number match.
|
|
|
+ */
|
|
|
+- if (lineno == target && offset >= ptrdiff_t(script->mainOffset()))
|
|
|
++ if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) {
|
|
|
+ goto out;
|
|
|
++ }
|
|
|
+ if (lineno >= target) {
|
|
|
+ unsigned diff = lineno - target;
|
|
|
+ if (diff < bestdiff) {
|
|
|
+ bestdiff = diff;
|
|
|
+ best = offset;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset += SN_DELTA(sn);
|
|
|
+ SrcNoteType type = SN_TYPE(sn);
|
|
|
+ if (type == SRC_SETLINE) {
|
|
|
+ lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
|
|
|
+ } else if (type == SRC_NEWLINE) {
|
|
|
+ lineno++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+- if (best >= 0)
|
|
|
++ if (best >= 0) {
|
|
|
+ offset = best;
|
|
|
++ }
|
|
|
+ out:
|
|
|
+ return script->offsetToPC(offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ JS_FRIEND_API(unsigned)
|
|
|
+ js::GetScriptLineExtent(JSScript* script)
|
|
|
+ {
|
|
|
+ unsigned lineno = script->lineno();
|
|
|
+ unsigned maxLineNo = lineno;
|
|
|
+ for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
|
|
+ SrcNoteType type = SN_TYPE(sn);
|
|
|
+- if (type == SRC_SETLINE)
|
|
|
++ if (type == SRC_SETLINE) {
|
|
|
+ lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
|
|
|
+- else if (type == SRC_NEWLINE)
|
|
|
++ } else if (type == SRC_NEWLINE) {
|
|
|
+ lineno++;
|
|
|
+-
|
|
|
+- if (maxLineNo < lineno)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (maxLineNo < lineno) {
|
|
|
+ maxLineNo = lineno;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1 + maxLineNo - script->lineno();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::DescribeScriptedCallerForDirectEval(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|
|
+ const char** file, unsigned* linenop, uint32_t* pcOffset,
|
|
|
+@@ -3391,47 +3605,53 @@ Rebase(JSScript* dst, JSScript* src, T*
|
|
|
+
|
|
|
+ static JSObject*
|
|
|
+ CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun)
|
|
|
+ {
|
|
|
+ /* NB: Keep this in sync with XDRInterpretedFunction. */
|
|
|
+ RootedObject cloneProto(cx);
|
|
|
+ if (srcFun->isGenerator() || srcFun->isAsync()) {
|
|
|
+ cloneProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
|
|
|
+- if (!cloneProto)
|
|
|
++ if (!cloneProto) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gc::AllocKind allocKind = srcFun->getAllocKind();
|
|
|
+ uint16_t flags = srcFun->flags();
|
|
|
+ if (srcFun->isSelfHostedBuiltin()) {
|
|
|
+ // Functions in the self-hosting compartment are only extended in
|
|
|
+ // debug mode. For top-level functions, FUNCTION_EXTENDED gets used by
|
|
|
+ // the cloning algorithm. Do the same for inner functions here.
|
|
|
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
|
|
+ flags |= JSFunction::Flags::EXTENDED;
|
|
|
+ }
|
|
|
+ RootedAtom atom(cx, srcFun->displayAtom());
|
|
|
+- if (atom)
|
|
|
++ if (atom) {
|
|
|
+ cx->markAtom(atom);
|
|
|
++ }
|
|
|
+ RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, srcFun->nargs(),
|
|
|
+ JSFunction::Flags(flags), nullptr, atom,
|
|
|
+ cloneProto, allocKind, TenuredObject));
|
|
|
+- if (!clone)
|
|
|
++ if (!clone) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSScript::AutoDelazify srcScript(cx, srcFun);
|
|
|
+- if (!srcScript)
|
|
|
++ if (!srcScript) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
|
|
|
+- if (!cloneScript)
|
|
|
++ if (!cloneScript) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- if (!JSFunction::setTypeForScriptedFunction(cx, clone))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!JSFunction::setTypeForScriptedFunction(cx, clone)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
|
|
+ MutableHandle<GCVector<Scope*>> scopes)
|
|
|
+ {
|
|
|
+@@ -3451,36 +3671,38 @@ js::detail::CopyScript(JSContext* cx, Ha
|
|
|
+ uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
|
|
|
+ uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
|
|
|
+ uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
|
|
|
+
|
|
|
+ /* Script data */
|
|
|
+
|
|
|
+ size_t size = src->dataSize();
|
|
|
+ UniquePtr<uint8_t, JS::FreePolicy> data(AllocScriptData(cx, size));
|
|
|
+- if (size && !data)
|
|
|
++ if (size && !data) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Scopes */
|
|
|
+
|
|
|
+ // The passed in scopes vector contains body scopes that needed to be
|
|
|
+ // cloned especially, depending on whether the script is a function or
|
|
|
+ // global scope. Starting at scopes.length() means we only deal with
|
|
|
+ // intra-body scopes.
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(nscopes != 0);
|
|
|
+ MOZ_ASSERT(src->bodyScopeIndex() + 1 == scopes.length());
|
|
|
+ GCPtrScope* vector = src->scopes()->vector;
|
|
|
+ RootedScope original(cx);
|
|
|
+ RootedScope clone(cx);
|
|
|
+ for (uint32_t i = scopes.length(); i < nscopes; i++) {
|
|
|
+ original = vector[i];
|
|
|
+ clone = Scope::clone(cx, original, scopes[FindScopeIndex(src, *original->enclosing())]);
|
|
|
+- if (!clone || !scopes.append(clone))
|
|
|
++ if (!clone || !scopes.append(clone)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Objects */
|
|
|
+
|
|
|
+ AutoObjectVector objects(cx);
|
|
|
+ if (nobjects != 0) {
|
|
|
+ GCPtrObject* vector = src->objects()->vector;
|
|
|
+@@ -3498,59 +3720,64 @@ js::detail::CopyScript(JSContext* cx, Ha
|
|
|
+ MOZ_ASSERT(innerFun->isAsmJSNative());
|
|
|
+ JS_ReportErrorASCII(cx, "AsmJS modules do not yet support cloning.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ clone = innerFun;
|
|
|
+ } else {
|
|
|
+ if (innerFun->isInterpretedLazy()) {
|
|
|
+ AutoRealm ar(cx, innerFun);
|
|
|
+- if (!JSFunction::getOrCreateScript(cx, innerFun))
|
|
|
++ if (!JSFunction::getOrCreateScript(cx, innerFun)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Scope* enclosing = innerFun->nonLazyScript()->enclosingScope();
|
|
|
+ RootedScope enclosingClone(cx, scopes[FindScopeIndex(src, *enclosing)]);
|
|
|
+ clone = CloneInnerInterpretedFunction(cx, enclosingClone, innerFun);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
|
|
|
+ }
|
|
|
+
|
|
|
+- if (!clone || !objects.append(clone))
|
|
|
++ if (!clone || !objects.append(clone)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This assignment must occur before all the Rebase calls. */
|
|
|
+ dst->data = data.release();
|
|
|
+ dst->dataSize_ = size;
|
|
|
+ MOZ_ASSERT(bool(dst->data) == bool(src->data));
|
|
|
+- if (dst->data)
|
|
|
++ if (dst->data) {
|
|
|
+ memcpy(dst->data, src->data, size);
|
|
|
++ }
|
|
|
+
|
|
|
+ if (cx->zone() != src->zoneFromAnyThread()) {
|
|
|
+- for (size_t i = 0; i < src->scriptData()->natoms(); i++)
|
|
|
++ for (size_t i = 0; i < src->scriptData()->natoms(); i++) {
|
|
|
+ cx->markAtom(src->scriptData()->atoms()[i]);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Script filenames, bytecodes and atoms are runtime-wide. */
|
|
|
+ dst->setScriptData(src->scriptData());
|
|
|
+
|
|
|
+ dst->lineno_ = src->lineno();
|
|
|
+ dst->mainOffset_ = src->mainOffset();
|
|
|
+ dst->nfixed_ = src->nfixed();
|
|
|
+ dst->nslots_ = src->nslots();
|
|
|
+ dst->bodyScopeIndex_ = src->bodyScopeIndex_;
|
|
|
+ dst->funLength_ = src->funLength();
|
|
|
+ dst->nTypeSets_ = src->nTypeSets();
|
|
|
+ if (src->argumentsHasVarBinding()) {
|
|
|
+ dst->setArgumentsHasVarBinding();
|
|
|
+- if (src->analyzedArgsUsage())
|
|
|
++ if (src->analyzedArgsUsage()) {
|
|
|
+ dst->setNeedsArgsObj(src->needsArgsObj());
|
|
|
++ }
|
|
|
+ }
|
|
|
+ dst->bitFields_.hasMappedArgsObj_ = src->hasMappedArgsObj();
|
|
|
+ dst->bitFields_.functionHasThisBinding_ = src->functionHasThisBinding();
|
|
|
+ dst->bitFields_.functionHasExtraBodyVarScope_ = src->functionHasExtraBodyVarScope();
|
|
|
+ dst->cloneHasArray(src);
|
|
|
+ dst->bitFields_.strict_ = src->strict();
|
|
|
+ dst->bitFields_.explicitUseStrict_ = src->explicitUseStrict();
|
|
|
+ dst->bitFields_.hasNonSyntacticScope_ = scopes[0]->hasOnChain(ScopeKind::NonSyntactic);
|
|
|
+@@ -3566,35 +3793,40 @@ js::detail::CopyScript(JSContext* cx, Ha
|
|
|
+ dst->bitFields_.isDefaultClassConstructor_ = src->isDefaultClassConstructor();
|
|
|
+ dst->bitFields_.isAsync_ = src->bitFields_.isAsync_;
|
|
|
+ dst->bitFields_.hasRest_ = src->bitFields_.hasRest_;
|
|
|
+ dst->bitFields_.hideScriptFromDebugger_ = src->bitFields_.hideScriptFromDebugger_;
|
|
|
+
|
|
|
+ if (nconsts != 0) {
|
|
|
+ GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
|
|
|
+ dst->consts()->vector = vector;
|
|
|
+- for (unsigned i = 0; i < nconsts; ++i)
|
|
|
++ for (unsigned i = 0; i < nconsts; ++i) {
|
|
|
+ MOZ_ASSERT_IF(vector[i].isGCThing(), vector[i].toString()->isAtom());
|
|
|
++ }
|
|
|
+ }
|
|
|
+ if (nobjects != 0) {
|
|
|
+ GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objects()->vector);
|
|
|
+ dst->objects()->vector = vector;
|
|
|
+- for (unsigned i = 0; i < nobjects; ++i)
|
|
|
++ for (unsigned i = 0; i < nobjects; ++i) {
|
|
|
+ vector[i].init(&objects[i]->as<NativeObject>());
|
|
|
++ }
|
|
|
+ }
|
|
|
+ {
|
|
|
+ GCPtrScope* vector = Rebase<GCPtrScope>(dst, src, src->scopes()->vector);
|
|
|
+ dst->scopes()->vector = vector;
|
|
|
+- for (uint32_t i = 0; i < nscopes; ++i)
|
|
|
++ for (uint32_t i = 0; i < nscopes; ++i) {
|
|
|
+ vector[i].init(scopes[i]);
|
|
|
+- }
|
|
|
+- if (ntrynotes != 0)
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (ntrynotes != 0) {
|
|
|
+ dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
|
|
|
+- if (nscopenotes != 0)
|
|
|
++ }
|
|
|
++ if (nscopenotes != 0) {
|
|
|
+ dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector);
|
|
|
++ }
|
|
|
+ if (nyieldoffsets != 0) {
|
|
|
+ dst->yieldAndAwaitOffsets().vector_ =
|
|
|
+ Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3608,25 +3840,27 @@ CreateEmptyScriptForClone(JSContext* cx,
|
|
|
+ */
|
|
|
+ RootedObject sourceObject(cx);
|
|
|
+ if (src->realm()->isSelfHostingRealm()) {
|
|
|
+ if (!cx->realm()->selfHostingScriptSource) {
|
|
|
+ CompileOptions options(cx);
|
|
|
+ FillSelfHostingCompileOptions(options);
|
|
|
+
|
|
|
+ ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
|
|
|
+- if (!obj)
|
|
|
++ if (!obj) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ cx->realm()->selfHostingScriptSource.set(obj);
|
|
|
+ }
|
|
|
+ sourceObject = cx->realm()->selfHostingScriptSource;
|
|
|
+ } else {
|
|
|
+ sourceObject = src->sourceObject();
|
|
|
+- if (!cx->compartment()->wrap(cx, &sourceObject))
|
|
|
++ if (!cx->compartment()->wrap(cx, &sourceObject)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ CompileOptions options(cx);
|
|
|
+ options.setMutedErrors(src->mutedErrors())
|
|
|
+ .setSelfHostingMode(src->selfHosted())
|
|
|
+ .setNoScriptRval(src->noScriptRval());
|
|
|
+
|
|
|
+ return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
|
|
|
+@@ -3634,79 +3868,86 @@ CreateEmptyScriptForClone(JSContext* cx,
|
|
|
+ }
|
|
|
+
|
|
|
+ JSScript*
|
|
|
+ js::CloneGlobalScript(JSContext* cx, ScopeKind scopeKind, HandleScript src)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
|
|
|
+
|
|
|
+ RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
|
|
|
+- if (!dst)
|
|
|
++ if (!dst) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(src->bodyScopeIndex() == 0);
|
|
|
+ Rooted<GCVector<Scope*>> scopes(cx, GCVector<Scope*>(cx));
|
|
|
+ Rooted<GlobalScope*> original(cx, &src->bodyScope()->as<GlobalScope>());
|
|
|
+ GlobalScope* clone = GlobalScope::clone(cx, original, scopeKind);
|
|
|
+- if (!clone || !scopes.append(clone))
|
|
|
++ if (!clone || !scopes.append(clone)) {
|
|
|
+ return nullptr;
|
|
|
+-
|
|
|
+- if (!detail::CopyScript(cx, src, dst, &scopes))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!detail::CopyScript(cx, src, dst, &scopes)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return dst;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSScript*
|
|
|
+ js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction fun,
|
|
|
+ HandleScript src)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(fun->isInterpreted());
|
|
|
+ MOZ_ASSERT(!fun->hasScript() || fun->hasUncompletedScript());
|
|
|
+
|
|
|
+ RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
|
|
|
+- if (!dst)
|
|
|
++ if (!dst) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Clone the non-intra-body scopes.
|
|
|
+ Rooted<GCVector<Scope*>> scopes(cx, GCVector<Scope*>(cx));
|
|
|
+ RootedScope original(cx);
|
|
|
+ RootedScope enclosingClone(cx);
|
|
|
+ for (uint32_t i = 0; i <= src->bodyScopeIndex(); i++) {
|
|
|
+ original = src->getScope(i);
|
|
|
+
|
|
|
+ if (i == 0) {
|
|
|
+ enclosingClone = enclosingScope;
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(src->getScope(i - 1) == original->enclosing());
|
|
|
+ enclosingClone = scopes[i - 1];
|
|
|
+ }
|
|
|
+
|
|
|
+ Scope* clone;
|
|
|
+- if (original->is<FunctionScope>())
|
|
|
++ if (original->is<FunctionScope>()) {
|
|
|
+ clone = FunctionScope::clone(cx, original.as<FunctionScope>(), fun, enclosingClone);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ clone = Scope::clone(cx, original, enclosingClone);
|
|
|
+-
|
|
|
+- if (!clone || !scopes.append(clone))
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!clone || !scopes.append(clone)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Save flags in case we need to undo the early mutations.
|
|
|
+ const int preservedFlags = fun->flags();
|
|
|
+ if (!detail::CopyScript(cx, src, dst, &scopes)) {
|
|
|
+ fun->setFlags(preservedFlags);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Finally set the script after all the fallible operations.
|
|
|
+- if (fun->isInterpretedLazy())
|
|
|
++ if (fun->isInterpretedLazy()) {
|
|
|
+ fun->setUnlazifiedScript(dst);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ fun->initScript(dst);
|
|
|
++ }
|
|
|
+
|
|
|
+ return dst;
|
|
|
+ }
|
|
|
+
|
|
|
+ DebugScript*
|
|
|
+ JSScript::debugScript()
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(bitFields_.hasDebugScript_);
|
|
|
+@@ -3746,29 +3987,32 @@ JSScript::destroyDebugScript(FreeOp* fop
|
|
|
+ #endif
|
|
|
+ fop->free_(releaseDebugScript());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::ensureHasDebugScript(JSContext* cx)
|
|
|
+ {
|
|
|
+- if (bitFields_.hasDebugScript_)
|
|
|
++ if (bitFields_.hasDebugScript_) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
|
|
|
+ UniqueDebugScript debug(reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
|
|
|
+- if (!debug)
|
|
|
++ if (!debug) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ /* Create realm's debugScriptMap if necessary. */
|
|
|
+ if (!realm()->debugScriptMap) {
|
|
|
+ auto map = cx->make_unique<DebugScriptMap>();
|
|
|
+- if (!map)
|
|
|
++ if (!map) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ realm()->debugScriptMap = std::move(map);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!realm()->debugScriptMap->putNew(this, std::move(debug))) {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+@@ -3776,49 +4020,53 @@ JSScript::ensureHasDebugScript(JSContext
|
|
|
+ bitFields_.hasDebugScript_ = true; // safe to set this; we can't fail after this point
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Ensure that any Interpret() instances running on this script have
|
|
|
+ * interrupts enabled. The interrupts must stay enabled until the
|
|
|
+ * debug state is destroyed.
|
|
|
+ */
|
|
|
+ for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
|
|
+- if (iter->isInterpreter())
|
|
|
++ if (iter->isInterpreter()) {
|
|
|
+ iter->asInterpreter()->enableInterruptsIfRunning(this);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::setNewStepMode(FreeOp* fop, uint32_t newValue)
|
|
|
+ {
|
|
|
+ DebugScript* debug = debugScript();
|
|
|
+ uint32_t prior = debug->stepMode;
|
|
|
+ debug->stepMode = newValue;
|
|
|
+
|
|
|
+ if (!prior != !newValue) {
|
|
|
+- if (hasBaselineScript())
|
|
|
++ if (hasBaselineScript()) {
|
|
|
+ baseline->toggleDebugTraps(this, nullptr);
|
|
|
+-
|
|
|
+- if (!stepModeEnabled() && !debug->numSites)
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!stepModeEnabled() && !debug->numSites) {
|
|
|
+ fop->free_(releaseDebugScript());
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::incrementStepModeCount(JSContext* cx)
|
|
|
+ {
|
|
|
+ cx->check(this);
|
|
|
+ MOZ_ASSERT(cx->realm()->isDebuggee());
|
|
|
+
|
|
|
+ AutoRealm ar(cx, this);
|
|
|
+
|
|
|
+- if (!ensureHasDebugScript(cx))
|
|
|
++ if (!ensureHasDebugScript(cx)) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ DebugScript* debug = debugScript();
|
|
|
+ uint32_t count = debug->stepMode;
|
|
|
+ setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+@@ -3830,119 +4078,130 @@ JSScript::decrementStepModeCount(FreeOp*
|
|
|
+ setNewStepMode(fop, count - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ BreakpointSite*
|
|
|
+ JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
|
|
|
+ {
|
|
|
+ AutoRealm ar(cx, this);
|
|
|
+
|
|
|
+- if (!ensureHasDebugScript(cx))
|
|
|
++ if (!ensureHasDebugScript(cx)) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ DebugScript* debug = debugScript();
|
|
|
+ BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
|
|
|
+
|
|
|
+ if (!site) {
|
|
|
+ site = cx->new_<JSBreakpointSite>(this, pc);
|
|
|
+- if (!site)
|
|
|
++ if (!site) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ debug->numSites++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return site;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc)
|
|
|
+ {
|
|
|
+ DebugScript* debug = debugScript();
|
|
|
+ BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
|
|
|
+ MOZ_ASSERT(site);
|
|
|
+
|
|
|
+ fop->delete_(site);
|
|
|
+ site = nullptr;
|
|
|
+
|
|
|
+- if (--debug->numSites == 0 && !stepModeEnabled())
|
|
|
++ if (--debug->numSites == 0 && !stepModeEnabled()) {
|
|
|
+ fop->free_(releaseDebugScript());
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, JSObject* handler)
|
|
|
+ {
|
|
|
+- if (!hasAnyBreakpointsOrStepMode())
|
|
|
++ if (!hasAnyBreakpointsOrStepMode()) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
|
|
+ BreakpointSite* site = getBreakpointSite(pc);
|
|
|
+ if (site) {
|
|
|
+ Breakpoint* nextbp;
|
|
|
+ for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
|
|
+ nextbp = bp->nextInSite();
|
|
|
+- if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
|
|
|
++ if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) {
|
|
|
+ bp->destroy(fop);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::hasBreakpointsAt(jsbytecode* pc)
|
|
|
+ {
|
|
|
+ BreakpointSite* site = getBreakpointSite(pc);
|
|
|
+- if (!site)
|
|
|
++ if (!site) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ return site->enabledCount > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ SharedScriptData::traceChildren(JSTracer* trc)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(refCount() != 0);
|
|
|
+- for (uint32_t i = 0; i < natoms(); ++i)
|
|
|
++ for (uint32_t i = 0; i < natoms(); ++i) {
|
|
|
+ TraceNullableEdge(trc, &atoms()[i], "atom");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::traceChildren(JSTracer* trc)
|
|
|
+ {
|
|
|
+ // NOTE: this JSScript may be partially initialized at this point. E.g. we
|
|
|
+ // may have created it and partially initialized it with
|
|
|
+ // JSScript::Create(), but not yet finished initializing it with
|
|
|
+ // fullyInitFromEmitter() or fullyInitTrivial().
|
|
|
+
|
|
|
+ MOZ_ASSERT_IF(trc->isMarkingTracer() &&
|
|
|
+ GCMarker::fromTracer(trc)->shouldCheckCompartments(),
|
|
|
+ zone()->isCollecting());
|
|
|
+
|
|
|
+- if (scriptData())
|
|
|
++ if (scriptData()) {
|
|
|
+ scriptData()->traceChildren(trc);
|
|
|
+-
|
|
|
+- if (ScopeArray* scopearray = scopes())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (ScopeArray* scopearray = scopes()) {
|
|
|
+ TraceRange(trc, scopearray->length, scopearray->vector, "scopes");
|
|
|
++ }
|
|
|
+
|
|
|
+ if (hasConsts()) {
|
|
|
+ ConstArray* constarray = consts();
|
|
|
+ TraceRange(trc, constarray->length, constarray->vector, "consts");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hasObjects()) {
|
|
|
+ ObjectArray* objarray = objects();
|
|
|
+ TraceRange(trc, objarray->length, objarray->vector, "objects");
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT_IF(sourceObject(), MaybeForwarded(sourceObject())->compartment() == compartment());
|
|
|
+ TraceNullableEdge(trc, &sourceObject_, "sourceObject");
|
|
|
+
|
|
|
+- if (maybeLazyScript())
|
|
|
++ if (maybeLazyScript()) {
|
|
|
+ TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
|
|
|
+-
|
|
|
+- if (trc->isMarkingTracer())
|
|
|
++ }
|
|
|
++
|
|
|
++ if (trc->isMarkingTracer()) {
|
|
|
+ realm()->mark();
|
|
|
++ }
|
|
|
+
|
|
|
+ jit::TraceJitScripts(trc, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ LazyScript::finalize(FreeOp* fop)
|
|
|
+ {
|
|
|
+ fop->free_(table_);
|
|
|
+@@ -3950,47 +4209,51 @@ LazyScript::finalize(FreeOp* fop)
|
|
|
+
|
|
|
+ size_t
|
|
|
+ JSScript::calculateLiveFixed(jsbytecode* pc)
|
|
|
+ {
|
|
|
+ size_t nlivefixed = numAlwaysLiveFixedSlots();
|
|
|
+
|
|
|
+ if (nfixed() != nlivefixed) {
|
|
|
+ Scope* scope = lookupScope(pc);
|
|
|
+- if (scope)
|
|
|
++ if (scope) {
|
|
|
+ scope = MaybeForwarded(scope);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Find the nearest LexicalScope in the same script.
|
|
|
+ while (scope && scope->is<WithScope>()) {
|
|
|
+ scope = scope->enclosing();
|
|
|
+- if (scope)
|
|
|
++ if (scope) {
|
|
|
+ scope = MaybeForwarded(scope);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scope) {
|
|
|
+- if (scope->is<LexicalScope>())
|
|
|
++ if (scope->is<LexicalScope>()) {
|
|
|
+ nlivefixed = scope->as<LexicalScope>().nextFrameSlot();
|
|
|
+- else if (scope->is<VarScope>())
|
|
|
++ } else if (scope->is<VarScope>()) {
|
|
|
+ nlivefixed = scope->as<VarScope>().nextFrameSlot();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(nlivefixed <= nfixed());
|
|
|
+ MOZ_ASSERT(nlivefixed >= numAlwaysLiveFixedSlots());
|
|
|
+
|
|
|
+ return nlivefixed;
|
|
|
+ }
|
|
|
+
|
|
|
+ Scope*
|
|
|
+ JSScript::lookupScope(jsbytecode* pc)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(containsPC(pc));
|
|
|
+
|
|
|
+- if (!hasScopeNotes())
|
|
|
++ if (!hasScopeNotes()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ size_t offset = pc - code();
|
|
|
+
|
|
|
+ ScopeNoteArray* notes = scopeNotes();
|
|
|
+ Scope* scope = nullptr;
|
|
|
+
|
|
|
+ // Find the innermost block chain using a binary search.
|
|
|
+ size_t bottom = 0;
|
|
|
+@@ -4007,40 +4270,43 @@ JSScript::lookupScope(jsbytecode* pc)
|
|
|
+ // the searched range for coverage.
|
|
|
+ size_t check = mid;
|
|
|
+ while (check >= bottom) {
|
|
|
+ const ScopeNote* checkNote = ¬es->vector[check];
|
|
|
+ MOZ_ASSERT(checkNote->start <= offset);
|
|
|
+ if (offset < checkNote->start + checkNote->length) {
|
|
|
+ // We found a matching block chain but there may be inner ones
|
|
|
+ // at a higher block chain index than mid. Continue the binary search.
|
|
|
+- if (checkNote->index == ScopeNote::NoScopeIndex)
|
|
|
++ if (checkNote->index == ScopeNote::NoScopeIndex) {
|
|
|
+ scope = nullptr;
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ scope = getScope(checkNote->index);
|
|
|
++ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+- if (checkNote->parent == UINT32_MAX)
|
|
|
++ if (checkNote->parent == UINT32_MAX) {
|
|
|
+ break;
|
|
|
++ }
|
|
|
+ check = checkNote->parent;
|
|
|
+ }
|
|
|
+ bottom = mid + 1;
|
|
|
+ } else {
|
|
|
+ top = mid;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return scope;
|
|
|
+ }
|
|
|
+
|
|
|
+ Scope*
|
|
|
+ JSScript::innermostScope(jsbytecode* pc)
|
|
|
+ {
|
|
|
+- if (Scope* scope = lookupScope(pc))
|
|
|
++ if (Scope* scope = lookupScope(pc)) {
|
|
|
+ return scope;
|
|
|
++ }
|
|
|
+ return bodyScope();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::setArgumentsHasVarBinding()
|
|
|
+ {
|
|
|
+ bitFields_.argsHasVarBinding_ = true;
|
|
|
+ bitFields_.needsArgsAnalysis_ = true;
|
|
|
+@@ -4059,43 +4325,48 @@ js::SetFrameArgumentsObject(JSContext* c
|
|
|
+ HandleScript script, JSObject* argsobj)
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ * Replace any optimized arguments in the frame with an explicit arguments
|
|
|
+ * object. Note that 'arguments' may have already been overwritten.
|
|
|
+ */
|
|
|
+
|
|
|
+ Rooted<BindingIter> bi(cx, BindingIter(script));
|
|
|
+- while (bi && bi.name() != cx->names().arguments)
|
|
|
++ while (bi && bi.name() != cx->names().arguments) {
|
|
|
+ bi++;
|
|
|
+- if (!bi)
|
|
|
++ }
|
|
|
++ if (!bi) {
|
|
|
+ return;
|
|
|
++ }
|
|
|
+
|
|
|
+ if (bi.location().kind() == BindingLocation::Kind::Environment) {
|
|
|
+ /*
|
|
|
+ * Scan the script to find the slot in the call object that 'arguments'
|
|
|
+ * is assigned to.
|
|
|
+ */
|
|
|
+ jsbytecode* pc = script->code();
|
|
|
+- while (*pc != JSOP_ARGUMENTS)
|
|
|
++ while (*pc != JSOP_ARGUMENTS) {
|
|
|
+ pc += GetBytecodeLength(pc);
|
|
|
++ }
|
|
|
+ pc += JSOP_ARGUMENTS_LENGTH;
|
|
|
+ MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
|
|
|
+
|
|
|
+ // Note that here and below, it is insufficient to only check for
|
|
|
+ // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
|
|
|
+ // arguments slot.
|
|
|
+ EnvironmentObject& env = frame.callObj().as<EnvironmentObject>();
|
|
|
+- if (IsOptimizedPlaceholderMagicValue(env.aliasedBinding(bi)))
|
|
|
++ if (IsOptimizedPlaceholderMagicValue(env.aliasedBinding(bi))) {
|
|
|
+ env.setAliasedBinding(cx, bi, ObjectValue(*argsobj));
|
|
|
++ }
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(bi.location().kind() == BindingLocation::Kind::Frame);
|
|
|
+ uint32_t frameSlot = bi.location().slot();
|
|
|
+- if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(frameSlot)))
|
|
|
++ if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(frameSlot))) {
|
|
|
+ frame.unaliasedLocal(frameSlot) = ObjectValue(*argsobj);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(script->functionNonDelazifying());
|
|
|
+ MOZ_ASSERT(script->analyzedArgsUsage());
|
|
|
+@@ -4103,31 +4374,33 @@ JSScript::argumentsOptimizationFailed(JS
|
|
|
+
|
|
|
+ /*
|
|
|
+ * It is possible that the arguments optimization has already failed,
|
|
|
+ * everything has been fixed up, but there was an outstanding magic value
|
|
|
+ * on the stack that has just now flowed into an apply. In this case, there
|
|
|
+ * is nothing to do; GuardFunApplySpeculation will patch in the real
|
|
|
+ * argsobj.
|
|
|
+ */
|
|
|
+- if (script->needsArgsObj())
|
|
|
++ if (script->needsArgsObj()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!script->isGenerator());
|
|
|
+ MOZ_ASSERT(!script->isAsync());
|
|
|
+
|
|
|
+ script->bitFields_.needsArgsObj_ = true;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Since we can't invalidate baseline scripts, set a flag that's checked from
|
|
|
+ * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS
|
|
|
+ * should create an arguments object next time.
|
|
|
+ */
|
|
|
+- if (script->hasBaselineScript())
|
|
|
++ if (script->hasBaselineScript()) {
|
|
|
+ script->baselineScript()->setNeedsArgsObj();
|
|
|
++ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * By design, the arguments optimization is only made when there are no
|
|
|
+ * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points
|
|
|
+ * where the optimization could fail, other than an active invocation of
|
|
|
+ * 'f.apply(x, arguments)'. Thus, there are no outstanding values of
|
|
|
+ * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
|
|
|
+ * three things that need fixup:
|
|
|
+@@ -4140,41 +4413,45 @@ JSScript::argumentsOptimizationFailed(JS
|
|
|
+ for (AllScriptFramesIter i(cx); !i.done(); ++i) {
|
|
|
+ /*
|
|
|
+ * We cannot reliably create an arguments object for Ion activations of
|
|
|
+ * this script. To maintain the invariant that "script->needsArgsObj
|
|
|
+ * implies fp->hasArgsObj", the Ion bail mechanism will create an
|
|
|
+ * arguments object right after restoring the BaselineFrame and before
|
|
|
+ * entering Baseline code (in jit::FinishBailoutToBaseline).
|
|
|
+ */
|
|
|
+- if (i.isIon())
|
|
|
++ if (i.isIon()) {
|
|
|
+ continue;
|
|
|
++ }
|
|
|
+ AbstractFramePtr frame = i.abstractFramePtr();
|
|
|
+ if (frame.isFunctionFrame() && frame.script() == script) {
|
|
|
+ /* We crash on OOM since cleaning up here would be complicated. */
|
|
|
+ AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
|
+ ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
|
|
|
+- if (!argsobj)
|
|
|
++ if (!argsobj) {
|
|
|
+ oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
|
|
|
++ }
|
|
|
+ SetFrameArgumentsObject(cx, frame, script, argsobj);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::formalIsAliased(unsigned argSlot)
|
|
|
+ {
|
|
|
+- if (functionHasParameterExprs())
|
|
|
++ if (functionHasParameterExprs()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ for (PositionalFormalParameterIter fi(this); fi; fi++) {
|
|
|
+- if (fi.argumentSlot() == argSlot)
|
|
|
++ if (fi.argumentSlot() == argSlot) {
|
|
|
+ return fi.closedOver();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ MOZ_CRASH("Argument slot not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
|
|
|
+ {
|
|
|
+ return argsObjAliasesFormals() && !formalIsAliased(argSlot);
|
|
|
+@@ -4280,23 +4557,25 @@ LazyScript::CreateRaw(JSContext* cx, Han
|
|
|
+ p.treatAsRunOnce = false;
|
|
|
+
|
|
|
+ size_t bytes = (p.numClosedOverBindings * sizeof(JSAtom*))
|
|
|
+ + (p.numInnerFunctions * sizeof(GCPtrFunction));
|
|
|
+
|
|
|
+ UniquePtr<uint8_t, JS::FreePolicy> table;
|
|
|
+ if (bytes) {
|
|
|
+ table.reset(cx->pod_malloc<uint8_t>(bytes));
|
|
|
+- if (!table)
|
|
|
++ if (!table) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LazyScript* res = Allocate<LazyScript>(cx);
|
|
|
+- if (!res)
|
|
|
++ if (!res) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ cx->realm()->scheduleDelazificationForDebugger();
|
|
|
+
|
|
|
+ return new (res) LazyScript(fun, *sourceObject, table.release(), packed, sourceStart,
|
|
|
+ sourceEnd, toStringStart, lineno, column);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ LazyScript*
|
|
|
+@@ -4327,28 +4606,31 @@ LazyScript::Create(JSContext* cx, Handle
|
|
|
+ p.isLikelyConstructorWrapper = false;
|
|
|
+ p.isDerivedClassConstructor = false;
|
|
|
+ p.needsHomeObject = false;
|
|
|
+ p.parseGoal = uint32_t(parseGoal);
|
|
|
+
|
|
|
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
|
|
|
+ sourceStart, sourceEnd,
|
|
|
+ toStringStart, lineno, column);
|
|
|
+- if (!res)
|
|
|
++ if (!res) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ JSAtom** resClosedOverBindings = res->closedOverBindings();
|
|
|
+- for (size_t i = 0; i < res->numClosedOverBindings(); i++)
|
|
|
++ for (size_t i = 0; i < res->numClosedOverBindings(); i++) {
|
|
|
+ resClosedOverBindings[i] = closedOverBindings[i];
|
|
|
++ }
|
|
|
+
|
|
|
+ GCPtrFunction* resInnerFunctions = res->innerFunctions();
|
|
|
+ for (size_t i = 0; i < res->numInnerFunctions(); i++) {
|
|
|
+ resInnerFunctions[i].init(innerFunctions[i]);
|
|
|
+- if (resInnerFunctions[i]->isInterpretedLazy())
|
|
|
++ if (resInnerFunctions[i]->isInterpretedLazy()) {
|
|
|
+ resInnerFunctions[i]->lazyScript()->setEnclosingLazyScript(res);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ LazyScript*
|
|
|
+ LazyScript::CreateForXDR(JSContext* cx, HandleFunction fun,
|
|
|
+ HandleScript script, HandleScope enclosingScope,
|
|
|
+@@ -4361,42 +4643,47 @@ LazyScript::CreateForXDR(JSContext* cx,
|
|
|
+
|
|
|
+ // Dummy function which is not a valid function as this is the one which is
|
|
|
+ // holding this lazy script.
|
|
|
+ HandleFunction dummyFun = fun;
|
|
|
+
|
|
|
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
|
|
|
+ sourceStart, sourceEnd,
|
|
|
+ toStringStart, lineno, column);
|
|
|
+- if (!res)
|
|
|
++ if (!res) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ // Fill with dummies, to be GC-safe after the initialization of the free
|
|
|
+ // variables and inner functions.
|
|
|
+ size_t i, num;
|
|
|
+ JSAtom** closedOverBindings = res->closedOverBindings();
|
|
|
+- for (i = 0, num = res->numClosedOverBindings(); i < num; i++)
|
|
|
++ for (i = 0, num = res->numClosedOverBindings(); i < num; i++) {
|
|
|
+ closedOverBindings[i] = dummyAtom;
|
|
|
++ }
|
|
|
+
|
|
|
+ GCPtrFunction* functions = res->innerFunctions();
|
|
|
+- for (i = 0, num = res->numInnerFunctions(); i < num; i++)
|
|
|
++ for (i = 0, num = res->numInnerFunctions(); i < num; i++) {
|
|
|
+ functions[i].init(dummyFun);
|
|
|
++ }
|
|
|
+
|
|
|
+ // Set the enclosing scope of the lazy function. This value should only be
|
|
|
+ // set if we have a non-lazy enclosing script at this point.
|
|
|
+ // LazyScript::enclosingScriptHasEverBeenCompiled relies on the enclosing
|
|
|
+ // scope being non-null if we have ever been nested inside non-lazy
|
|
|
+ // function.
|
|
|
+ MOZ_ASSERT(!res->hasEnclosingScope());
|
|
|
+- if (enclosingScope)
|
|
|
++ if (enclosingScope) {
|
|
|
+ res->setEnclosingScope(enclosingScope);
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(!res->hasScript());
|
|
|
+- if (script)
|
|
|
++ if (script) {
|
|
|
+ res->initScript(script);
|
|
|
++ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ LazyScript::initRuntimeFields(uint64_t packedFields)
|
|
|
+ {
|
|
|
+ union {
|
|
|
+@@ -4429,18 +4716,19 @@ JSScript::updateJitCodeRaw(JSRuntime* rt
|
|
|
+ }
|
|
|
+ MOZ_ASSERT(jitCodeRaw_);
|
|
|
+ MOZ_ASSERT(jitCodeSkipArgCheck_);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ JSScript::hasLoops()
|
|
|
+ {
|
|
|
+- if (!hasTrynotes())
|
|
|
++ if (!hasTrynotes()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ JSTryNote* tn = trynotes()->vector;
|
|
|
+ JSTryNote* tnlimit = tn + trynotes()->length;
|
|
|
+ for (; tn < tnlimit; tn++) {
|
|
|
+ switch (tn->kind) {
|
|
|
+ case JSTRY_FOR_IN:
|
|
|
+ case JSTRY_FOR_OF:
|
|
|
+ case JSTRY_LOOP:
|
|
|
+ return true;
|
|
|
+@@ -4484,46 +4772,50 @@ JSScript::AutoDelazify::holdScript(JS::H
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::AutoDelazify::dropScript()
|
|
|
+ {
|
|
|
+ // Don't touch script_ if it's in the self-hosting realm, see the comment
|
|
|
+ // in holdScript.
|
|
|
+- if (script_ && !script_->realm()->isSelfHostingRealm())
|
|
|
++ if (script_ && !script_->realm()->isSelfHostingRealm()) {
|
|
|
+ script_->setDoNotRelazify(oldDoNotRelazify_);
|
|
|
++ }
|
|
|
+ script_ = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSScript::setTopLevelPrivate(void* value)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(module() || isGlobalCode());
|
|
|
+
|
|
|
+ Scope* scope = bodyScope();
|
|
|
+- if (scope->is<GlobalScope>())
|
|
|
++ if (scope->is<GlobalScope>()) {
|
|
|
+ scope->as<GlobalScope>().setTopLevelPrivate(value);
|
|
|
+- else if (scope->is<ModuleScope>())
|
|
|
++ } else if (scope->is<ModuleScope>()) {
|
|
|
+ scope->as<ModuleScope>().setTopLevelPrivate(value);
|
|
|
+- else
|
|
|
++ } else {
|
|
|
+ MOZ_CRASH("Unexpected scope kind");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void*
|
|
|
+ JSScript::maybeTopLevelPrivate() const
|
|
|
+ {
|
|
|
+- if (!module() && !isGlobalCode())
|
|
|
++ if (!module() && !isGlobalCode()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ Scope* scope = bodyScope();
|
|
|
+- if (scope->is<GlobalScope>())
|
|
|
++ if (scope->is<GlobalScope>()) {
|
|
|
+ return scope->as<GlobalScope>().topLevelPrivate();
|
|
|
+- else if (scope->is<ModuleScope>())
|
|
|
++ } else if (scope->is<ModuleScope>()) {
|
|
|
+ return scope->as<ModuleScope>().topLevelPrivate();
|
|
|
++ }
|
|
|
+
|
|
|
+ MOZ_CRASH("Unexpected scope kind");
|
|
|
+ }
|
|
|
+
|
|
|
+ JS::ubi::Base::Size
|
|
|
+ JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
|
|
+ {
|
|
|
+ Size size = gc::Arena::thingSize(get().asTenured().getAllocKind());
|
|
|
+@@ -4556,13 +4848,14 @@ JS::ubi::Concrete<js::LazyScript>::size(
|
|
|
+ size += get().sizeOfExcludingThis(mallocSizeOf);
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char*
|
|
|
+ JS::ubi::Concrete<js::LazyScript>::scriptFilename() const
|
|
|
+ {
|
|
|
+ auto source = get().sourceObject().source();
|
|
|
+- if (!source)
|
|
|
++ if (!source) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+
|
|
|
+ return source->filename();
|
|
|
+ }
|
|
|
+diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
|
|
|
+--- a/js/src/vm/JSScript.h
|
|
|
++++ b/js/src/vm/JSScript.h
|
|
|
+@@ -536,18 +536,19 @@ class ScriptSource
|
|
|
+
|
|
|
+ ~ScriptSource() {
|
|
|
+ MOZ_ASSERT(refs == 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ void incref() { refs++; }
|
|
|
+ void decref() {
|
|
|
+ MOZ_ASSERT(refs != 0);
|
|
|
+- if (--refs == 0)
|
|
|
++ if (--refs == 0) {
|
|
|
+ js_delete(this);
|
|
|
++ }
|
|
|
+ }
|
|
|
+ MOZ_MUST_USE bool initFromOptions(JSContext* cx,
|
|
|
+ const JS::ReadOnlyCompileOptions& options,
|
|
|
+ const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing());
|
|
|
+ MOZ_MUST_USE bool setSourceCopy(JSContext* cx, JS::SourceBufferHolder& srcBuf);
|
|
|
+ void setSourceRetrievable() { sourceRetrievable_ = true; }
|
|
|
+ bool sourceRetrievable() const { return sourceRetrievable_; }
|
|
|
+ bool hasSourceData() const { return !data.is<Missing>(); }
|
|
|
+@@ -697,25 +698,28 @@ class ScriptSourceHolder
|
|
|
+ {}
|
|
|
+ explicit ScriptSourceHolder(ScriptSource* ss)
|
|
|
+ : ss(ss)
|
|
|
+ {
|
|
|
+ ss->incref();
|
|
|
+ }
|
|
|
+ ~ScriptSourceHolder()
|
|
|
+ {
|
|
|
+- if (ss)
|
|
|
++ if (ss) {
|
|
|
+ ss->decref();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ void reset(ScriptSource* newss) {
|
|
|
+ // incref before decref just in case ss == newss.
|
|
|
+- if (newss)
|
|
|
++ if (newss) {
|
|
|
+ newss->incref();
|
|
|
+- if (ss)
|
|
|
++ }
|
|
|
++ if (ss) {
|
|
|
+ ss->decref();
|
|
|
++ }
|
|
|
+ ss = newss;
|
|
|
+ }
|
|
|
+ ScriptSource* get() const {
|
|
|
+ return ss;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ class ScriptSourceObject : public NativeObject
|
|
|
+@@ -744,18 +748,19 @@ class ScriptSourceObject : public Native
|
|
|
+ return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
|
|
|
+ }
|
|
|
+ const Value& elementAttributeName() const {
|
|
|
+ MOZ_ASSERT(!getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic());
|
|
|
+ return getReservedSlot(ELEMENT_PROPERTY_SLOT);
|
|
|
+ }
|
|
|
+ JSScript* introductionScript() const {
|
|
|
+ Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
|
|
|
+- if (value.isUndefined())
|
|
|
++ if (value.isUndefined()) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return value.toGCThing()->as<JSScript>();
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ static const uint32_t SOURCE_SLOT = 0;
|
|
|
+ static const uint32_t ELEMENT_SLOT = 1;
|
|
|
+ static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
|
|
|
+ static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
|
|
|
+@@ -813,36 +818,38 @@ class SharedScriptData
|
|
|
+ return refCount_;
|
|
|
+ }
|
|
|
+ void incRefCount() {
|
|
|
+ refCount_++;
|
|
|
+ }
|
|
|
+ void decRefCount() {
|
|
|
+ MOZ_ASSERT(refCount_ != 0);
|
|
|
+ uint32_t remain = --refCount_;
|
|
|
+- if (remain == 0)
|
|
|
++ if (remain == 0) {
|
|
|
+ js_free(this);
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t dataLength() const {
|
|
|
+ return (natoms_ * sizeof(GCPtrAtom)) + codeLength_ + noteLength_;
|
|
|
+ }
|
|
|
+ const uint8_t* data() const {
|
|
|
+ return reinterpret_cast<const uint8_t*>(data_);
|
|
|
+ }
|
|
|
+ uint8_t* data() {
|
|
|
+ return reinterpret_cast<uint8_t*>(data_);
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t natoms() const {
|
|
|
+ return natoms_;
|
|
|
+ }
|
|
|
+ GCPtrAtom* atoms() {
|
|
|
+- if (!natoms_)
|
|
|
++ if (!natoms_) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return reinterpret_cast<GCPtrAtom*>(data());
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t codeLength() const {
|
|
|
+ return codeLength_;
|
|
|
+ }
|
|
|
+ jsbytecode* code() {
|
|
|
+ return reinterpret_cast<jsbytecode*>(data() + natoms_ * sizeof(GCPtrAtom));
|
|
|
+@@ -876,22 +883,25 @@ struct ScriptBytecodeHasher
|
|
|
+ ~Lookup();
|
|
|
+ };
|
|
|
+
|
|
|
+ static HashNumber hash(const Lookup& l) {
|
|
|
+ return l.hash;
|
|
|
+ }
|
|
|
+ static bool match(SharedScriptData* entry, const Lookup& lookup) {
|
|
|
+ const SharedScriptData* data = lookup.scriptData;
|
|
|
+- if (entry->natoms() != data->natoms())
|
|
|
++ if (entry->natoms() != data->natoms()) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ if (entry->codeLength() != data->codeLength()) {
|
|
|
+ return false;
|
|
|
+- if (entry->codeLength() != data->codeLength())
|
|
|
++ }
|
|
|
++ if (entry->numNotes() != data->numNotes()) {
|
|
|
+ return false;
|
|
|
+- if (entry->numNotes() != data->numNotes())
|
|
|
+- return false;
|
|
|
++ }
|
|
|
+ return mozilla::ArrayEqual<uint8_t>(entry->data(), data->data(), data->dataLength());
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ class AutoLockScriptData;
|
|
|
+
|
|
|
+ using ScriptDataTable = HashSet<SharedScriptData*,
|
|
|
+ ScriptBytecodeHasher,
|
|
|
+@@ -1250,18 +1260,19 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ JS::Realm* realm() const { return realm_; }
|
|
|
+
|
|
|
+ js::SharedScriptData* scriptData() {
|
|
|
+ return scriptData_;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Script bytecode is immutable after creation.
|
|
|
+ jsbytecode* code() const {
|
|
|
+- if (!scriptData_)
|
|
|
++ if (!scriptData_) {
|
|
|
+ return nullptr;
|
|
|
++ }
|
|
|
+ return scriptData_->code();
|
|
|
+ }
|
|
|
+ bool isUncompleted() const {
|
|
|
+ // code() becomes non-null only if this script is complete.
|
|
|
+ // See the comment in JSScript::fullyInitFromEmitter.
|
|
|
+ return !code();
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1310,43 +1321,47 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ // module code) and block-scoped locals (in all kinds of code).
|
|
|
+ size_t nfixed() const {
|
|
|
+ return nfixed_;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Number of fixed slots reserved for slots that are always live. Only
|
|
|
+ // nonzero for function or module code.
|
|
|
+ size_t numAlwaysLiveFixedSlots() const {
|
|
|
+- if (bodyScope()->is<js::FunctionScope>())
|
|
|
++ if (bodyScope()->is<js::FunctionScope>()) {
|
|
|
+ return bodyScope()->as<js::FunctionScope>().nextFrameSlot();
|
|
|
+- if (bodyScope()->is<js::ModuleScope>())
|
|
|
++ }
|
|
|
++ if (bodyScope()->is<js::ModuleScope>()) {
|
|
|
+ return bodyScope()->as<js::ModuleScope>().nextFrameSlot();
|
|
|
++ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Calculate the number of fixed slots that are live at a particular bytecode.
|
|
|
+ size_t calculateLiveFixed(jsbytecode* pc);
|
|
|
+
|
|
|
+ size_t nslots() const {
|
|
|
+ return nslots_;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned numArgs() const {
|
|
|
+- if (bodyScope()->is<js::FunctionScope>())
|
|
|
++ if (bodyScope()->is<js::FunctionScope>()) {
|
|
|
+ return bodyScope()->as<js::FunctionScope>().numPositionalFormalParameters();
|
|
|
++ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::Shape* initialEnvironmentShape() const;
|
|
|
+
|
|
|
+ bool functionHasParameterExprs() const {
|
|
|
+ // Only functions have parameters.
|
|
|
+ js::Scope* scope = bodyScope();
|
|
|
+- if (!scope->is<js::FunctionScope>())
|
|
|
++ if (!scope->is<js::FunctionScope>()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return scope->as<js::FunctionScope>().hasParameterExprs();
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t nTypeSets() const {
|
|
|
+ return nTypeSets_;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t funLength() const {
|
|
|
+@@ -1680,29 +1695,31 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ * nullptr for global and eval scripts.
|
|
|
+ * The delazifying variant ensures that the function isn't lazy. The
|
|
|
+ * non-delazifying variant must only be used after earlier code has
|
|
|
+ * called ensureNonLazyCanonicalFunction and while the function can't
|
|
|
+ * have been relazified.
|
|
|
+ */
|
|
|
+ inline JSFunction* functionDelazifying() const;
|
|
|
+ JSFunction* functionNonDelazifying() const {
|
|
|
+- if (bodyScope()->is<js::FunctionScope>())
|
|
|
++ if (bodyScope()->is<js::FunctionScope>()) {
|
|
|
+ return bodyScope()->as<js::FunctionScope>().canonicalFunction();
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * De-lazifies the canonical function. Must be called before entering code
|
|
|
+ * that expects the function to be non-lazy.
|
|
|
+ */
|
|
|
+ inline void ensureNonLazyCanonicalFunction();
|
|
|
+
|
|
|
+ js::ModuleObject* module() const {
|
|
|
+- if (bodyScope()->is<js::ModuleScope>())
|
|
|
++ if (bodyScope()->is<js::ModuleScope>()) {
|
|
|
+ return bodyScope()->as<js::ModuleScope>().module();
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool isGlobalOrEvalCode() const {
|
|
|
+ return bodyScope()->is<js::GlobalScope>() || bodyScope()->is<js::EvalScope>();
|
|
|
+ }
|
|
|
+ bool isGlobalCode() const {
|
|
|
+ return bodyScope()->is<js::GlobalScope>();
|
|
|
+@@ -1742,18 +1759,19 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ /* Return whether this script was compiled for 'eval' */
|
|
|
+ bool isForEval() const {
|
|
|
+ MOZ_ASSERT_IF(isCachedEval() || isActiveEval(), bodyScope()->is<js::EvalScope>());
|
|
|
+ return isCachedEval() || isActiveEval();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Return whether this is a 'direct eval' script in a function scope. */
|
|
|
+ bool isDirectEvalInFunction() const {
|
|
|
+- if (!isForEval())
|
|
|
++ if (!isForEval()) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+ return bodyScope()->hasOnChain(js::ScopeKind::Function);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Return whether this script is a top-level script.
|
|
|
+ *
|
|
|
+ * If we evaluate some code which contains a syntax error, then we might
|
|
|
+ * produce a JSScript which has no associated bytecode. Testing with
|
|
|
+@@ -1801,27 +1819,29 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ MOZ_ASSERT_IF(bitFields_.functionHasExtraBodyVarScope_, functionHasParameterExprs());
|
|
|
+ return bitFields_.functionHasExtraBodyVarScope_;
|
|
|
+ }
|
|
|
+
|
|
|
+ js::VarScope* functionExtraBodyVarScope() const {
|
|
|
+ MOZ_ASSERT(functionHasExtraBodyVarScope());
|
|
|
+ for (uint32_t i = 0; i < scopes()->length; i++) {
|
|
|
+ js::Scope* scope = getScope(i);
|
|
|
+- if (scope->kind() == js::ScopeKind::FunctionBodyVar)
|
|
|
++ if (scope->kind() == js::ScopeKind::FunctionBodyVar) {
|
|
|
+ return &scope->as<js::VarScope>();
|
|
|
++ }
|
|
|
+ }
|
|
|
+ MOZ_CRASH("Function extra body var scope not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ bool needsBodyEnvironment() const {
|
|
|
+ for (uint32_t i = 0; i < scopes()->length; i++) {
|
|
|
+ js::Scope* scope = getScope(i);
|
|
|
+- if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment())
|
|
|
++ if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment()) {
|
|
|
+ return true;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::LexicalScope* maybeNamedLambdaScope() const;
|
|
|
+
|
|
|
+ js::Scope* enclosingScope() const {
|
|
|
+ return outermostScope()->enclosing();
|
|
|
+@@ -2002,18 +2022,19 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
|
|
|
+ MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
|
|
|
+ "Did you mean to use lookupScope(pc)?");
|
|
|
+ return getScope(GET_UINT32_INDEX(pc));
|
|
|
+ }
|
|
|
+
|
|
|
+ inline JSFunction* getFunction(size_t index);
|
|
|
+ JSFunction* function() const {
|
|
|
+- if (functionNonDelazifying())
|
|
|
++ if (functionNonDelazifying()) {
|
|
|
+ return functionNonDelazifying();
|
|
|
++ }
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::RegExpObject* getRegExp(size_t index);
|
|
|
+ inline js::RegExpObject* getRegExp(jsbytecode* pc);
|
|
|
+
|
|
|
+ const js::Value& getConst(size_t index) {
|
|
|
+ js::ConstArray* arr = consts();
|
|
|
+@@ -2030,22 +2051,24 @@ class JSScript : public js::gc::TenuredC
|
|
|
+ js::Scope* innermostScope() { return innermostScope(main()); }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The isEmpty method tells whether this script has code that computes any
|
|
|
+ * result (not return value, result AKA normal completion value) other than
|
|
|
+ * JSVAL_VOID, or any other effects.
|
|
|
+ */
|
|
|
+ bool isEmpty() const {
|
|
|
+- if (length() > 3)
|
|
|
++ if (length() > 3) {
|
|
|
+ return false;
|
|
|
++ }
|
|
|
+
|
|
|
+ jsbytecode* pc = code();
|
|
|
+- if (noScriptRval() && JSOp(*pc) == JSOP_FALSE)
|
|
|
++ if (noScriptRval() && JSOp(*pc) == JSOP_FALSE) {
|
|
|
+ ++pc;
|
|
|
++ }
|
|
|
+ return JSOp(*pc) == JSOP_RETRVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool formalIsAliased(unsigned argSlot);
|
|
|
+ bool formalLivesInArgumentsObject(unsigned argSlot);
|
|
|
+
|
|
|
+ private:
|
|
|
+ /* Change this->stepMode to |newValue|. */
|