Exemple #1
0
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;
}
Exemple #5
0
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;
}
Exemple #7
0
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;
}
Exemple #9
0
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;
}
Exemple #10
0
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;
}
Exemple #11
0
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 = &registerAt(entry.getIndex());
    }
    reg->set(vm, this, value);
    return true;
}
Exemple #13
0
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;
}
Exemple #14
0
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;
    }
}
Exemple #15
0
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());
}