MethodList EAClass::methodsNamed(const Identifier& identifier, Instance* instance) const { MethodList methodList; // Check to see if the method has been cached first. Method* method = mMethods.get(identifier.impl()); if (method) { methodList.append(method); return methodList; } // Method hasn't been called before, see if the object supports it. // Rather than doing identifier.ascii().data(), we need to create CString separately otherwise it goes out of scope. CString identStr = identifier.ascii(); const char *ident = identStr.data(); const EAInstance *inst = static_cast<const EAInstance*>(instance); EA::WebKit::IJSBoundObject *obj = inst->getObject(); if (obj->hasMethod(ident)) { // The object says it has this method, cache it so that the string // lookup can be avoided in the future. EAMethod* aMethod = new EAMethod(ident); { JSLock lock(SilenceAssertionsOnly); mMethods.set(identifier.impl(), aMethod); } methodList.append(aMethod); } return methodList; }
// Returns true if we found enough information to terminate optimization. static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, size_t depth, bool& needsVarInjectionChecks, ResolveOp& op) { if (JSActivation* activation = jsDynamicCast<JSActivation*>(scope)) { if (ident == exec->propertyNames().arguments) { // We know the property will be at this activation scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0); return true; } SymbolTableEntry entry = activation->symbolTable()->get(ident.impl()); if (entry.isReadOnly() && getOrPut == Put) { // We know the property will be at this activation scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0); return true; } if (!entry.isNull()) { op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, activation->structure(), 0, entry.getIndex()); return true; } if (activation->symbolTable()->usesNonStrictEval()) needsVarInjectionChecks = true; return false; } if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(scope)) { SymbolTableEntry entry = globalObject->symbolTable()->get(ident.impl()); if (!entry.isNull()) { if (getOrPut == Put && entry.isReadOnly()) { // We know the property will be at global scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0); return true; } op = ResolveOp( makeType(GlobalVar, needsVarInjectionChecks), depth, 0, entry.watchpointSet(), reinterpret_cast<uintptr_t>(globalObject->registerAt(entry.getIndex()).slot())); return true; } PropertySlot slot(globalObject); if (!globalObject->getOwnPropertySlot(globalObject, exec, ident, slot) || !slot.isCacheableValue() || !globalObject->structure()->propertyAccessesAreCacheable() || (globalObject->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && getOrPut == Put)) { // We know the property will be at global scope, but we don't know how to cache it. ASSERT(!scope->next()); op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, 0, 0, 0); return true; } op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, globalObject->structure(), 0, slot.cachedOffset()); return true; } op = ResolveOp(Dynamic, 0, 0, 0, 0); return true; }
PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) specificValue = 0; if (structure->transitionCount() > s_maxTransitionLength) { RefPtr<Structure> transition = toCacheableDictionaryTransition(structure); ASSERT(structure != transition); offset = transition->put(propertyName, attributes, specificValue); ASSERT(offset >= structure->m_anonymousSlotCount); ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) transition->growPropertyStorageCapacity(); return transition.release(); } RefPtr<Structure> transition = create(structure->m_prototype.get(), structure->typeInfo(), structure->anonymousSlotCount()); transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; transition->m_previous = structure; transition->m_nameInPrevious = propertyName.impl(); transition->m_attributesInPrevious = attributes; transition->m_specificValueInPrevious = specificValue; transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; if (structure->m_propertyTable) { if (structure->m_isPinnedPropertyTable) transition->m_propertyTable = structure->copyPropertyTable(); else { transition->m_propertyTable = structure->m_propertyTable; structure->m_propertyTable = 0; } } else { if (structure->m_previous) transition->materializePropertyMap(); else transition->createPropertyMapHashTable(); } offset = transition->put(propertyName, attributes, specificValue); ASSERT(offset >= structure->m_anonymousSlotCount); ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) transition->growPropertyStorageCapacity(); transition->m_offset = offset - structure->m_anonymousSlotCount; ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); structure->transitionTableAdd(make_pair(propertyName.impl(), attributes), transition.get(), specificValue); return transition.release(); }
EncodedJSValue JSC_HOST_CALL moduleLoaderObjectParseModule(ExecState* exec) { VM& vm = exec->vm(); const Identifier moduleKey = exec->argument(0).toPropertyKey(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); String source = exec->argument(1).toString(exec)->value(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); SourceCode sourceCode = makeSource(source, moduleKey.impl()); CodeProfiling profile(sourceCode); ParserError error; std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>( &vm, sourceCode, Identifier(), JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::Strict, SourceParseMode::ModuleAnalyzeMode, error); if (error.isValid()) { throwVMError(exec, error.toErrorObject(exec->lexicalGlobalObject(), sourceCode)); return JSValue::encode(jsUndefined()); } ASSERT(moduleProgramNode); ModuleAnalyzer moduleAnalyzer(exec, moduleKey, sourceCode, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables()); JSModuleRecord* moduleRecord = moduleAnalyzer.analyze(*moduleProgramNode); return JSValue::encode(moduleRecord); }
bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propertyName) { if (symbolTable().contains(propertyName.impl())) return false; return JSObject::deleteProperty(exec, propertyName); }
size_t Structure::putSpecificValue(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue) { ASSERT(!propertyName.isNull()); ASSERT(get(globalData, propertyName) == notFound); checkConsistency(); if (attributes & DontEnum) m_hasNonEnumerableProperties = true; StringImpl* rep = propertyName.impl(); if (!m_propertyTable) createPropertyMap(); unsigned newOffset; if (m_propertyTable->hasDeletedOffset()) newOffset = m_propertyTable->getDeletedOffset(); else newOffset = m_propertyTable->size(); m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue)); checkConsistency(); return newOffset; }
Storage storageForGeneratorLocal(unsigned index) { // We assign a symbol to a register. There is one-on-one corresponding between a register and a symbol. // By doing so, we allocate the specific storage to save the given register. // This allow us not to save all the live registers even if the registers are not overwritten from the previous resuming time. // It means that, the register can be retrieved even if the immediate previous op_save does not save it. if (m_storages.size() <= index) m_storages.resize(index + 1); if (Optional<Storage> storage = m_storages[index]) return *storage; Identifier identifier = Identifier::fromUid(PrivateName()); unsigned identifierIndex = m_codeBlock->numberOfIdentifiers(); m_codeBlock->addIdentifier(identifier); ScopeOffset scopeOffset = m_generatorFrameSymbolTable->takeNextScopeOffset(NoLockingNecessary); m_generatorFrameSymbolTable->set(NoLockingNecessary, identifier.impl(), SymbolTableEntry(VarOffset(scopeOffset))); Storage storage = { identifier, identifierIndex, scopeOffset }; m_storages[index] = storage; return storage; }
BytecodeIntrinsicNode::EmitterType BytecodeIntrinsicRegistry::lookup(const Identifier& ident) const { if (!m_vm.propertyNames->isPrivateName(ident)) return nullptr; auto iterator = m_bytecodeIntrinsicMap.find(ident.impl()); if (iterator == m_bytecodeIntrinsicMap.end()) return nullptr; return iterator->value; }
bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertyDescriptor& descriptor) { SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (!entry.isNull()) { descriptor.setDescriptor(registerAt(entry.getIndex()).jsValue(), entry.getAttributes() | DontDelete); return true; } return false; }
static String cssPropertyName(const Identifier& propertyName, bool* hadPixelOrPosPrefix = 0) { if (hadPixelOrPosPrefix) *hadPixelOrPosPrefix = false; unsigned length = propertyName.length(); if (!length) return String(); StringImpl* propertyNameString = propertyName.impl(); // If there is no uppercase character in the propertyName, there can // be no prefix, nor extension and we can return the same string. if (!containsASCIIUpperChar(*propertyNameString)) return String(propertyNameString); StringBuilder builder; builder.reserveCapacity(length); unsigned i = 0; switch (getCSSPropertyNamePrefix(*propertyNameString)) { case PropertyNamePrefixNone: if (isASCIIUpper((*propertyNameString)[0])) return String(); break; case PropertyNamePrefixCSS: i += 3; break; case PropertyNamePrefixPixel: i += 5; if (hadPixelOrPosPrefix) *hadPixelOrPosPrefix = true; break; case PropertyNamePrefixPos: i += 3; if (hadPixelOrPosPrefix) *hadPixelOrPosPrefix = true; break; case PropertyNamePrefixApple: case PropertyNamePrefixEpub: case PropertyNamePrefixKHTML: case PropertyNamePrefixWebKit: builder.append('-'); } builder.append(toASCIILower((*propertyNameString)[i++])); for (; i < length; ++i) { UChar c = (*propertyNameString)[i]; if (!isASCIIUpper(c)) builder.append(c); else builder.append(makeString('-', toASCIILower(c))); } return builder.toString(); }
inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) { SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (!entry.isNull()) { ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); slot.setRegisterSlot(®isterAt(entry.getIndex())); return true; } return false; }
inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) { SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; if (entry.getIndex() >= m_numCapturedVars) return false; slot.setValue(registerAt(entry.getIndex()).get()); return true; }
inline bool JSActivation::symbolTablePut(const Identifier& propertyName, JSValue value) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; if (entry.isReadOnly()) return true; ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); registerAt(entry.getIndex()) = value; return true; }
void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName) { StringImpl* rep = propertyName.impl(); materializePropertyMapIfNecessary(globalData); ASSERT(isDictionary()); ASSERT(m_propertyTable); PropertyMapEntry* entry = m_propertyTable->find(rep).first; ASSERT(entry); entry->specificValue.clear(); }
bool Structure::despecifyFunction(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); materializePropertyMapIfNecessary(); if (!m_propertyTable) return false; StringImpl* rep = propertyName.impl(); unsigned i = rep->existingHash(); #if DUMP_PROPERTYMAP_STATS ++numProbes; #endif unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return false; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); m_propertyTable->entries()[entryIndex - 1].specificValue = 0; return true; } #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif unsigned k = 1 | doubleHash(rep->existingHash()); while (1) { i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return false; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); m_propertyTable->entries()[entryIndex - 1].specificValue = 0; return true; } } }
PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.impl(), attributes), specificValue)) { ASSERT(existingTransition->m_offset != noOffset); offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount; ASSERT(offset >= structure->m_anonymousSlotCount); ASSERT(structure->m_anonymousSlotCount == existingTransition->m_anonymousSlotCount); return existingTransition; } return 0; }
inline bool JSActivation::symbolTablePutWithAttributes(const Identifier& propertyName, JSValue value, unsigned attributes) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); if (iter == symbolTable().end()) return false; SymbolTableEntry& entry = iter->second; ASSERT(!entry.isNull()); if (entry.getIndex() >= static_cast<int>(d()->functionExecutable->capturedVariableCount())) return false; entry.setAttributes(attributes); registerAt(entry.getIndex()) = value; return true; }
inline bool JSActivation::symbolTablePut(JSGlobalData& globalData, const Identifier& propertyName, JSValue value) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; if (entry.isReadOnly()) return true; if (entry.getIndex() >= m_numCapturedVars) return false; registerAt(entry.getIndex()).set(globalData, this, value); return true; }
bool Structure::despecifyFunction(JSGlobalData& globalData, const Identifier& propertyName) { materializePropertyMapIfNecessary(globalData); if (!m_propertyTable) return false; ASSERT(!propertyName.isNull()); PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; if (!entry) return false; ASSERT(entry->specificValue); entry->specificValue.clear(); return true; }
inline bool JSActivation::symbolTablePutWithAttributes(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); if (iter == symbolTable().end()) return false; SymbolTableEntry& entry = iter->second; ASSERT(!entry.isNull()); if (entry.getIndex() >= m_numCapturedVars) return false; entry.setAttributes(attributes); registerAt(entry.getIndex()).set(globalData, this, value); return true; }
int ProgramExecutable::addGlobalVar(JSGlobalObject* globalObject, const Identifier& ident, ConstantMode constantMode, FunctionMode functionMode) { // Try to share the symbolTable if possible SharedSymbolTable* symbolTable = globalObject->symbolTable(); UNUSED_PARAM(functionMode); int index = symbolTable->size(); SymbolTableEntry newEntry(index, (constantMode == IsConstant) ? ReadOnly : 0); if (functionMode == IsFunctionToSpecialize) newEntry.attemptToWatch(); SymbolTable::AddResult result = symbolTable->add(ident.impl(), newEntry); if (!result.isNewEntry) { result.iterator->value.notifyWrite(); index = result.iterator->value.getIndex(); } return index; }
JSGlobalObject::NewGlobalVar JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode) { ConcurrentJITLocker locker(symbolTable()->m_lock); int index = symbolTable()->size(locker); SymbolTableEntry newEntry(index, (constantMode == IsConstant) ? ReadOnly : 0); if (constantMode == IsVariable) newEntry.prepareToWatch(); SymbolTable::Map::AddResult result = symbolTable()->add(locker, ident.impl(), newEntry); if (result.isNewEntry) addRegisters(1); else index = result.iterator->value.getIndex(); NewGlobalVar var; var.registerNumber = index; var.set = result.iterator->value.watchpointSet(); return var; }
inline bool JSActivation::symbolTablePut(ExecState* exec, const Identifier& propertyName, JSValue value, bool shouldThrow) { JSGlobalData& globalData = exec->globalData(); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; if (entry.isReadOnly()) { if (shouldThrow) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return true; } if (entry.getIndex() >= m_numCapturedVars) return false; registerAt(entry.getIndex()).set(globalData, this, value); return true; }
size_t Structure::remove(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); checkConsistency(); StringImpl* rep = propertyName.impl(); if (!m_propertyTable) return notFound; PropertyTable::find_iterator position = m_propertyTable->find(rep); if (!position.first) return notFound; size_t offset = position.first->offset; m_propertyTable->remove(position); m_propertyTable->addDeletedOffset(offset); checkConsistency(); return offset; }
void AbstractModuleRecord::addStarExportEntry(const Identifier& moduleName) { m_starExportEntries.add(moduleName.impl()); }
// Returns true if we found enough information to terminate optimization. static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, size_t depth, bool& needsVarInjectionChecks, ResolveOp& op, InitializationMode initializationMode) { if (scope->isJSLexicalEnvironment()) { JSLexicalEnvironment* lexicalEnvironment = jsCast<JSLexicalEnvironment*>(scope); if (ident == exec->propertyNames().arguments) { // We know the property will be at this lexical environment scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); return true; } SymbolTable* symbolTable = lexicalEnvironment->symbolTable(); { ConcurrentJSLocker locker(symbolTable->m_lock); auto iter = symbolTable->find(locker, ident.impl()); if (iter != symbolTable->end(locker)) { SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); if (entry.isReadOnly() && getOrPut == Put) { // We know the property will be at this lexical environment scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); return true; } op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, 0, lexicalEnvironment, entry.watchpointSet(), entry.scopeOffset().offset()); return true; } } if (scope->type() == ModuleEnvironmentType) { JSModuleEnvironment* moduleEnvironment = jsCast<JSModuleEnvironment*>(scope); AbstractModuleRecord* moduleRecord = moduleEnvironment->moduleRecord(); AbstractModuleRecord::Resolution resolution = moduleRecord->resolveImport(exec, ident); if (resolution.type == AbstractModuleRecord::Resolution::Type::Resolved) { AbstractModuleRecord* importedRecord = resolution.moduleRecord; JSModuleEnvironment* importedEnvironment = importedRecord->moduleEnvironment(); SymbolTable* symbolTable = importedEnvironment->symbolTable(); ConcurrentJSLocker locker(symbolTable->m_lock); auto iter = symbolTable->find(locker, resolution.localName.impl()); ASSERT(iter != symbolTable->end(locker)); SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); op = ResolveOp(makeType(ModuleVar, needsVarInjectionChecks), depth, 0, importedEnvironment, entry.watchpointSet(), entry.scopeOffset().offset(), resolution.localName.impl()); return true; } } if (symbolTable->usesNonStrictEval()) needsVarInjectionChecks = true; return false; } if (scope->isGlobalLexicalEnvironment()) { JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(scope); SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable(); ConcurrentJSLocker locker(symbolTable->m_lock); auto iter = symbolTable->find(locker, ident.impl()); if (iter != symbolTable->end(locker)) { SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); if (getOrPut == Put && entry.isReadOnly() && !isInitialization(initializationMode)) { // We know the property will be at global lexical environment, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); return true; } // We can force const Initialization to always go down the fast path. It is provably impossible to construct // a program that needs a var injection check here. You can convince yourself of this as follows: // Any other let/const/class would be a duplicate of this in the global scope, so we would never get here in that situation. // Also, if we had an eval in the global scope that defined a const, it would also be a duplicate of this const, and so it would // also throw an error. Therefore, we're *the only* thing that can assign to this "const" slot for the first (and only) time. Also, // we will never have a Dynamic ResolveType here because if we were inside a "with" statement, that would mean the "const" definition // isn't a global, it would be a local to the "with" block. // We still need to make the slow path correct for when we need to fire a watchpoint. ResolveType resolveType = initializationMode == InitializationMode::ConstInitialization ? GlobalLexicalVar : makeType(GlobalLexicalVar, needsVarInjectionChecks); op = ResolveOp( resolveType, depth, 0, 0, entry.watchpointSet(), reinterpret_cast<uintptr_t>(globalLexicalEnvironment->variableAt(entry.scopeOffset()).slot())); return true; } return false; } if (scope->isGlobalObject()) { JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(scope); { SymbolTable* symbolTable = globalObject->symbolTable(); ConcurrentJSLocker locker(symbolTable->m_lock); auto iter = symbolTable->find(locker, ident.impl()); if (iter != symbolTable->end(locker)) { SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); if (getOrPut == Put && entry.isReadOnly()) { // We know the property will be at global scope, but we don't know how to cache it. op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); return true; } op = ResolveOp( makeType(GlobalVar, needsVarInjectionChecks), depth, 0, 0, entry.watchpointSet(), reinterpret_cast<uintptr_t>(globalObject->variableAt(entry.scopeOffset()).slot())); return true; } } PropertySlot slot(globalObject, PropertySlot::InternalMethodType::VMInquiry); bool hasOwnProperty = globalObject->getOwnPropertySlot(globalObject, exec, ident, slot); if (!hasOwnProperty) { op = ResolveOp(makeType(UnresolvedProperty, needsVarInjectionChecks), 0, 0, 0, 0, 0); return true; } if (!slot.isCacheableValue() || !globalObject->structure()->propertyAccessesAreCacheable() || (globalObject->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && getOrPut == Put)) { // We know the property will be at global scope, but we don't know how to cache it. ASSERT(!scope->next()); op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), 0, 0, 0, 0, 0); return true; } WatchpointState state = globalObject->structure()->ensurePropertyReplacementWatchpointSet(exec->vm(), slot.cachedOffset())->state(); if (state == IsWatched && getOrPut == Put) { // The field exists, but because the replacement watchpoint is still intact. This is // kind of dangerous. We have two options: // 1) Invalidate the watchpoint set. That would work, but it's possible that this code // path never executes - in which case this would be unwise. // 2) Have the invalidation happen at run-time. All we have to do is leave the code // uncached. The only downside is slightly more work when this does execute. // We go with option (2) here because it seems less evil. op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, 0, 0, 0, 0); } else op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, globalObject->structure(), 0, 0, slot.cachedOffset()); return true; } op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); return true; }
void ModuleAnalyzer::declareExportAlias(const Identifier& localName, const Identifier& exportName) { m_aliasMap.add(localName.impl(), exportName); }
static JSValue identifierToJSValue(VM& vm, const Identifier& identifier) { if (identifier.isSymbol()) return Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())); return jsString(&vm, identifier.impl()); }
size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) { ASSERT(!propertyName.isNull()); ASSERT(get(propertyName) == notFound); checkConsistency(); if (attributes & DontEnum) m_hasNonEnumerableProperties = true; StringImpl* rep = propertyName.impl(); if (!m_propertyTable) createPropertyMapHashTable(); // FIXME: Consider a fast case for tables with no deleted sentinels. unsigned i = rep->existingHash(); unsigned k = 0; bool foundDeletedElement = false; unsigned deletedElementIndex = 0; // initialize to make the compiler happy #if DUMP_PROPERTYMAP_STATS ++numProbes; #endif while (1) { unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) break; if (entryIndex == deletedSentinelIndex) { // If we find a deleted-element sentinel, remember it for use later. if (!foundDeletedElement) { foundDeletedElement = true; deletedElementIndex = i; } } if (k == 0) { k = 1 | doubleHash(rep->existingHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif } i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif } // Figure out which entry to use. unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2; if (foundDeletedElement) { i = deletedElementIndex; --m_propertyTable->deletedSentinelCount; // Since we're not making the table bigger, we can't use the entry one past // the end that we were planning on using, so search backwards for the empty // slot that we can use. We know it will be there because we did at least one // deletion in the past that left an entry empty. while (m_propertyTable->entries()[--entryIndex - 1].key) { } } // Create a new hash table entry. m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; // Create a new hash table entry. rep->ref(); m_propertyTable->entries()[entryIndex - 1].key = rep; m_propertyTable->entries()[entryIndex - 1].attributes = attributes; m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue; m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; unsigned newOffset; if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) { newOffset = m_propertyTable->deletedOffsets->last(); m_propertyTable->deletedOffsets->removeLast(); } else newOffset = m_propertyTable->keyCount + m_anonymousSlotCount; m_propertyTable->entries()[entryIndex - 1].offset = newOffset; ASSERT(newOffset >= m_anonymousSlotCount); ++m_propertyTable->keyCount; if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size) expandPropertyMapHashTable(); checkConsistency(); return newOffset; }
size_t Structure::remove(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); checkConsistency(); StringImpl* rep = propertyName.impl(); if (!m_propertyTable) return notFound; #if DUMP_PROPERTYMAP_STATS ++numProbes; ++numRemoves; #endif // Find the thing to remove. unsigned i = rep->existingHash(); unsigned k = 0; unsigned entryIndex; StringImpl* key = 0; while (1) { entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return notFound; key = m_propertyTable->entries()[entryIndex - 1].key; if (rep == key) break; if (k == 0) { k = 1 | doubleHash(rep->existingHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif } i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif } // Replace this one element with the deleted sentinel. Also clear out // the entry so we can iterate all the entries as needed. m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex; size_t offset = m_propertyTable->entries()[entryIndex - 1].offset; ASSERT(offset >= m_anonymousSlotCount); key->deref(); m_propertyTable->entries()[entryIndex - 1].key = 0; m_propertyTable->entries()[entryIndex - 1].attributes = 0; m_propertyTable->entries()[entryIndex - 1].specificValue = 0; m_propertyTable->entries()[entryIndex - 1].offset = 0; if (!m_propertyTable->deletedOffsets) m_propertyTable->deletedOffsets = new Vector<unsigned>; m_propertyTable->deletedOffsets->append(offset); ASSERT(m_propertyTable->keyCount >= 1); --m_propertyTable->keyCount; ++m_propertyTable->deletedSentinelCount; if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size) rehashPropertyMapHashTable(); checkConsistency(); return offset; }