inline bool JSLexicalEnvironment::symbolTablePut(ExecState* exec, PropertyName propertyName, JSValue value, bool shouldThrow) { VM& vm = exec->vm(); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); WriteBarrierBase<Unknown>* reg; WatchpointSet* set; { GCSafeConcurrentJITLocker locker(symbolTable()->m_lock, exec->vm().heap); SymbolTable::Map::iterator iter = symbolTable()->find(locker, propertyName.uid()); if (iter == symbolTable()->end(locker)) return false; ASSERT(!iter->value.isNull()); if (iter->value.isReadOnly()) { if (shouldThrow) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return true; } ScopeOffset offset = iter->value.scopeOffset(); // Defend against the inspector asking for a var after it has been optimized out. if (!isValid(offset)) return false; set = iter->value.watchpointSet(); reg = &variableAt(offset); } reg->set(vm, this, value); if (set) set->invalidate(VariableWriteFireDetail(this, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it. return true; }
void SymbolTableEntry::notifyWriteSlow() { WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get(); if (!watchpoints) return; watchpoints->notifyWrite(); }
bool SymbolTableEntry::couldBeWatched() { if (!isFat()) return false; WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get(); if (!watchpoints) return false; return watchpoints->isStillValid(); }
bool SymbolTableEntry::couldBeWatched() { if (!isFat()) return false; WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get(); if (!watchpoints) return false; return watchpoints->state() == IsWatched; }
void AdaptiveInferredPropertyValueWatchpoint::install() { RELEASE_ASSERT(m_key.isWatchable()); m_key.object()->structure()->addTransitionWatchpoint(&m_structureWatchpoint); PropertyOffset offset = m_key.object()->structure()->getConcurrently(m_key.uid()); WatchpointSet* set = m_key.object()->structure()->propertyReplacementWatchpointSet(offset); set->add(&m_propertyWatchpoint); }
bool PropertyCondition::isWatchableWhenValid( Structure* structure, WatchabilityEffort effort) const { if (structure->transitionWatchpointSetHasBeenInvalidated()) return false; switch (m_kind) { case Equivalence: { PropertyOffset offset = structure->getConcurrently(uid()); // This method should only be called when some variant of isValid returned true, which // implies that we already confirmed that the structure knows of the property. We should // also have verified that the Structure is a cacheable dictionary, which means we // shouldn't have a TOCTOU race either. RELEASE_ASSERT(offset != invalidOffset); WatchpointSet* set = nullptr; switch (effort) { case MakeNoChanges: set = structure->propertyReplacementWatchpointSet(offset); break; case EnsureWatchability: set = structure->ensurePropertyReplacementWatchpointSet( *Heap::heap(structure)->vm(), offset); break; } if (!set || !set->isStillValid()) return false; break; } default: break; } return true; }
PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, StringImpl* uid, bool isDirect) { if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) return PutByIdStatus(TakesSlowPath); if (set.isEmpty()) return PutByIdStatus(); PutByIdStatus result; result.m_state = Simple; for (unsigned i = 0; i < set.size(); ++i) { Structure* structure = set[i]; if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) return PutByIdStatus(TakesSlowPath); if (!structure->propertyAccessesAreCacheable()) return PutByIdStatus(TakesSlowPath); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (isValidOffset(offset)) { if (attributes & CustomAccessor) return PutByIdStatus(MakesCalls); if (attributes & (Accessor | ReadOnly)) return PutByIdStatus(TakesSlowPath); WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); if (!replaceSet || replaceSet->isStillValid()) { // When this executes, it'll create, and fire, this replacement watchpoint set. // That means that this has probably never executed or that something fishy is // going on. Also, we cannot create or fire the watchpoint set from the concurrent // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. // So, better leave this alone and take slow path. return PutByIdStatus(TakesSlowPath); } if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) return PutByIdStatus(TakesSlowPath); continue; } // Our hypothesis is that we're doing a transition. Before we prove that this is really // true, we want to do some sanity checks. // Don't cache put transitions on dictionaries. if (structure->isDictionary()) return PutByIdStatus(TakesSlowPath); // If the structure corresponds to something that isn't an object, then give up, since // we don't want to be adding properties to strings. if (structure->typeInfo().type() == StringType) return PutByIdStatus(TakesSlowPath); RefPtr<IntendedStructureChain> chain; if (!isDirect) { chain = adoptRef(new IntendedStructureChain(globalObject, structure)); // If the prototype chain has setters or read-only properties, then give up. if (chain->mayInterceptStoreTo(uid)) return PutByIdStatus(TakesSlowPath); // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) // then give up. The dictionary case would only happen if this structure has not been // used in an optimized put_by_id transition. And really the only reason why we would // bail here is that I don't really feel like having the optimizing JIT go and flatten // dictionaries if we have evidence to suggest that those objects were never used as // prototypes in a cacheable prototype access - i.e. there's a good chance that some of // the other checks below will fail. if (structure->isProxy() || !chain->isNormalized()) return PutByIdStatus(TakesSlowPath); } // We only optimize if there is already a structure that the transition is cached to. Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); if (!transition) return PutByIdStatus(TakesSlowPath); ASSERT(isValidOffset(offset)); bool didAppend = result.appendVariant( PutByIdVariant::transition(structure, transition, chain.get(), offset)); if (!didAppend) return PutByIdStatus(TakesSlowPath); } return result; }
PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect) { if (parseIndex(*uid)) return PutByIdStatus(TakesSlowPath); if (set.isEmpty()) return PutByIdStatus(); PutByIdStatus result; result.m_state = Simple; for (unsigned i = 0; i < set.size(); ++i) { Structure* structure = set[i]; if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) return PutByIdStatus(TakesSlowPath); if (!structure->propertyAccessesAreCacheable()) return PutByIdStatus(TakesSlowPath); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (isValidOffset(offset)) { if (attributes & CustomAccessor) return PutByIdStatus(MakesCalls); if (attributes & (Accessor | ReadOnly)) return PutByIdStatus(TakesSlowPath); WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); if (!replaceSet || replaceSet->isStillValid()) { // When this executes, it'll create, and fire, this replacement watchpoint set. // That means that this has probably never executed or that something fishy is // going on. Also, we cannot create or fire the watchpoint set from the concurrent // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. // So, better leave this alone and take slow path. return PutByIdStatus(TakesSlowPath); } PutByIdVariant variant = PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); if (!result.appendVariant(variant)) return PutByIdStatus(TakesSlowPath); continue; } // Our hypothesis is that we're doing a transition. Before we prove that this is really // true, we want to do some sanity checks. // Don't cache put transitions on dictionaries. if (structure->isDictionary()) return PutByIdStatus(TakesSlowPath); // If the structure corresponds to something that isn't an object, then give up, since // we don't want to be adding properties to strings. if (!structure->typeInfo().isObject()) return PutByIdStatus(TakesSlowPath); ObjectPropertyConditionSet conditionSet; if (!isDirect) { conditionSet = generateConditionsForPropertySetterMissConcurrently( globalObject->vm(), globalObject, structure, uid); if (!conditionSet.isValid()) return PutByIdStatus(TakesSlowPath); } // We only optimize if there is already a structure that the transition is cached to. Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); if (!transition) return PutByIdStatus(TakesSlowPath); ASSERT(isValidOffset(offset)); bool didAppend = result.appendVariant( PutByIdVariant::transition( structure, transition, conditionSet, offset, transition->inferredTypeDescriptorFor(uid))); if (!didAppend) return PutByIdStatus(TakesSlowPath); } return result; }