bool InferredTypeTable::willStoreValue( VM& vm, PropertyName propertyName, JSValue value, StoredPropertyAge age) { // The algorithm here relies on the fact that only one thread modifies the hash map. if (age == OldProperty) { TableType::iterator iter = m_table.find(propertyName.uid()); if (iter == m_table.end() || !iter->value) return false; // Absence on replace => top. if (iter->value->willStoreValue(vm, propertyName, value)) return true; iter->value.clear(); return false; } TableType::AddResult result; { ConcurrentJSLocker locker(m_lock); result = m_table.add(propertyName.uid(), WriteBarrier<InferredType>()); } if (result.isNewEntry) { InferredType* inferredType = InferredType::create(vm); WTF::storeStoreFence(); result.iterator->value.set(vm, this, inferredType); } else if (!result.iterator->value) return false; if (result.iterator->value->willStoreValue(vm, propertyName, value)) return true; result.iterator->value.clear(); return false; }
void InferredTypeTable::makeTop(VM& vm, PropertyName propertyName, StoredPropertyAge age) { // The algorithm here relies on the fact that only one thread modifies the hash map. if (age == OldProperty) { TableType::iterator iter = m_table.find(propertyName.uid()); if (iter == m_table.end()) return; // Absence on replace => top. InferredType* entryValue = iter->value.get(); if (!entryValue) return; entryValue->makeTop(vm, propertyName); iter->value.clear(); return; } TableType::AddResult result; { ConcurrentJSLocker locker(m_lock); result = m_table.add(propertyName.uid(), WriteBarrier<InferredType>()); } if (!result.iterator->value) return; result.iterator->value->makeTop(vm, propertyName); result.iterator->value.clear(); }
bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell); // step 1. // If the property name is a symbol, we don't look into the imported bindings. // It may return the descriptor with writable: true, but namespace objects does not allow it in [[Set]] / [[DefineOwnProperty]] side. if (propertyName.isSymbol()) return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!thisObject->m_exports.contains(propertyName.uid())) return false; // https://esdiscuss.org/topic/march-24-meeting-notes // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p // section 9.4.6.5, step 6. // This property will be seen as writable: true, enumerable:true, configurable: false. // But this does not mean that this property is writable by users. // // In JSC, getOwnPropertySlot is not designed to throw any errors. But looking up the value from the module // environment may throw error if the loaded variable is the TDZ value. To workaround, we set the custom // getter function. When it is called, it looks up the variable and throws an error if the variable is not // initialized. slot.setCustom(thisObject, DontDelete, callbackGetter); return true; }
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 Structure::despecifyDictionaryFunction(JSGlobalData& globalData, PropertyName propertyName) { StringImpl* rep = propertyName.uid(); materializePropertyMapIfNecessary(globalData); ASSERT(isDictionary()); ASSERT(m_propertyTable); PropertyMapEntry* entry = m_propertyTable->find(rep).first; ASSERT(entry); entry->specificValue.clear(); }
inline bool JSLexicalEnvironment::symbolTableGet(PropertyName propertyName, PropertyDescriptor& descriptor) { SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.uid()); if (entry.isNull()) return false; // Defend against the inspector asking for a var after it has been optimized out. if (!isValid(entry)) return false; descriptor.setDescriptor(registerAt(entry.getIndex()).get(), entry.getAttributes()); return true; }
void InferredType::makeTopSlow(VM& vm, PropertyName propertyName) { Descriptor oldType; { ConcurrentJSLocker locker(m_lock); oldType = descriptor(locker); if (!set(locker, vm, Top)) return; } InferredTypeFireDetail detail(this, propertyName.uid(), oldType, Top, JSValue()); m_watchpointSet.fireAll(vm, detail); }
inline bool JSLexicalEnvironment::symbolTableGet(PropertyName propertyName, PropertySlot& slot) { SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.uid()); if (entry.isNull()) return false; // Defend against the inspector asking for a var after it has been optimized out. if (!isValid(entry)) return false; slot.setValue(this, DontEnum, registerAt(entry.getIndex()).get()); return true; }
bool Structure::despecifyFunction(JSGlobalData& globalData, PropertyName propertyName) { materializePropertyMapIfNecessary(globalData); if (!m_propertyTable) return false; PropertyMapEntry* entry = m_propertyTable->find(propertyName.uid()).first; if (!entry) return false; ASSERT(entry->specificValue); entry->specificValue.clear(); return true; }
PropertyOffset Structure::get(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes, JSCell*& specificValue) { ASSERT(structure()->classInfo() == &s_info); materializePropertyMapIfNecessary(globalData); if (!m_propertyTable) return invalidOffset; PropertyMapEntry* entry = m_propertyTable->find(propertyName.uid()).first; if (!entry) return invalidOffset; attributes = entry->attributes; specificValue = entry->specificValue.get(); return entry->offset; }
PropertyOffset Structure::putSpecificValue(JSGlobalData& globalData, PropertyName propertyName, unsigned attributes, JSCell* specificValue) { ASSERT(!JSC::isValidOffset(get(globalData, propertyName))); checkConsistency(); if (attributes & DontEnum) m_hasNonEnumerableProperties = true; StringImpl* rep = propertyName.uid(); if (!m_propertyTable) createPropertyMap(); PropertyOffset newOffset = m_propertyTable->nextOffset(m_inlineCapacity); m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue)); checkConsistency(); return newOffset; }
inline bool JSLexicalEnvironment::symbolTablePutWithAttributes(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); WriteBarrierBase<Unknown>* reg; { ConcurrentJITLocker locker(symbolTable()->m_lock); SymbolTable::Map::iterator iter = symbolTable()->find(locker, propertyName.uid()); if (iter == symbolTable()->end(locker)) return false; SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); if (!isValid(entry)) return false; entry.setAttributes(attributes); reg = ®isterAt(entry.getIndex()); } reg->set(vm, this, value); return true; }
PropertyOffset Structure::remove(PropertyName propertyName) { checkConsistency(); StringImpl* rep = propertyName.uid(); if (!m_propertyTable) return invalidOffset; PropertyTable::find_iterator position = m_propertyTable->find(rep); if (!position.first) return invalidOffset; PropertyOffset offset = position.first->offset; m_propertyTable->remove(position); m_propertyTable->addDeletedOffset(offset); checkConsistency(); return offset; }
void Structure::willStoreValueSlow( VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize, InferredTypeTable::StoredPropertyAge age) { ASSERT(!isCompilationThread()); ASSERT(structure()->classInfo() == info()); ASSERT(!hasBeenDictionary()); // Create the inferred type table before doing anything else, so that we don't GC after we have already // grabbed a pointer into the property map. InferredTypeTable* table = m_inferredTypeTable.get(); if (!table) { table = InferredTypeTable::create(vm); WTF::storeStoreFence(); m_inferredTypeTable.set(vm, this, table); } // This only works if we've got a property table. PropertyTable* propertyTable; materializePropertyMapIfNecessary(vm, propertyTable); // We must be calling this after having created the given property or confirmed that it was present // already, so we must have a property table now. ASSERT(propertyTable); // ... and the property must be present. PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); ASSERT(entry); if (shouldOptimize) entry->hasInferredType = table->willStoreValue(vm, propertyName, value, age); else { table->makeTop(vm, propertyName, age); entry->hasInferredType = false; } }
bool InferredType::willStoreValueSlow(VM& vm, PropertyName propertyName, JSValue value) { Descriptor oldType; Descriptor myType; bool result; { ConcurrentJSLocker locker(m_lock); oldType = descriptor(locker); myType = Descriptor::forValue(value); myType.merge(oldType); ASSERT(oldType != myType); // The type must have changed if we're on the slow path. bool setResult = set(locker, vm, myType); result = kind(locker) != Top; if (!setResult) return result; } InferredTypeFireDetail detail(this, propertyName.uid(), oldType, myType, value); m_watchpointSet.fireAll(vm, detail); return result; }
static EncodedJSValue callbackGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) { JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(JSValue::decode(thisValue)); JSModuleRecord* moduleRecord = thisObject->moduleRecord(); JSModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid())); ASSERT(resolution.type != JSModuleRecord::Resolution::Type::NotFound && resolution.type != JSModuleRecord::Resolution::Type::Ambiguous); JSModuleRecord* targetModule = resolution.moduleRecord; JSModuleEnvironment* targetEnvironment = targetModule->moduleEnvironment(); PropertySlot trampolineSlot(targetEnvironment); if (!targetEnvironment->methodTable(exec->vm())->getOwnPropertySlot(targetEnvironment, exec, resolution.localName, trampolineSlot)) return JSValue::encode(jsUndefined()); JSValue value = trampolineSlot.getValue(exec, propertyName); if (exec->hadException()) return JSValue::encode(jsUndefined()); // If the value is filled with TDZ value, throw a reference error. if (!value) return throwVMError(exec, createTDZError(exec)); return JSValue::encode(value); }
bool JSModuleEnvironment::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) { JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell); // All imported bindings are immutable. JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid())); if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) return false; return Base::deleteProperty(thisObject, exec, propertyName); }
bool JSModuleEnvironment::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell); JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid())); if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) { // When resolveImport resolves the resolution, the imported module environment must have the binding. JSModuleEnvironment* importedModuleEnvironment = resolution.moduleRecord->moduleEnvironment(); PropertySlot redirectSlot(importedModuleEnvironment, PropertySlot::InternalMethodType::Get); bool result = importedModuleEnvironment->methodTable(vm)->getOwnPropertySlot(importedModuleEnvironment, exec, resolution.localName, redirectSlot); ASSERT_UNUSED(result, result); ASSERT(redirectSlot.isValue()); JSValue value = redirectSlot.getValue(exec, resolution.localName); ASSERT_UNUSED(scope, !scope.exception()); slot.setValue(thisObject, redirectSlot.attributes(), value); return true; } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
bool JSModuleEnvironment::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell); // All imported bindings are immutable. JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid())); if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) { throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError)); return false; } return Base::put(thisObject, exec, propertyName, value, slot); }
bool JSModuleNamespaceObject::deleteProperty(JSCell* cell, ExecState*, PropertyName propertyName) { // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-delete-p JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell); return !thisObject->m_exports.contains(propertyName.uid()); }