summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSScope.cpp
diff options
context:
space:
mode:
authorKonstantin Tokarev <annulen@yandex.ru>2016-08-25 19:20:41 +0300
committerKonstantin Tokarev <annulen@yandex.ru>2017-02-02 12:30:55 +0000
commit6882a04fb36642862b11efe514251d32070c3d65 (patch)
treeb7959826000b061fd5ccc7512035c7478742f7b0 /Source/JavaScriptCore/runtime/JSScope.cpp
parentab6df191029eeeb0b0f16f127d553265659f739e (diff)
downloadqtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSScope.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/JSScope.cpp746
1 files changed, 213 insertions, 533 deletions
diff --git a/Source/JavaScriptCore/runtime/JSScope.cpp b/Source/JavaScriptCore/runtime/JSScope.cpp
index 69ff1e478..b13f3ff4a 100644
--- a/Source/JavaScriptCore/runtime/JSScope.cpp
+++ b/Source/JavaScriptCore/runtime/JSScope.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,610 +26,290 @@
#include "config.h"
#include "JSScope.h"
-#include "JSActivation.h"
#include "JSGlobalObject.h"
-#include "JSNameScope.h"
+#include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleRecord.h"
#include "JSWithScope.h"
-#include "Operations.h"
+#include "JSCInlines.h"
namespace JSC {
-ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSScope);
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSScope);
void JSScope::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
JSScope* thisObject = jsCast<JSScope*>(cell);
- ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
- COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
- ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
-
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(&thisObject->m_next);
}
-bool JSScope::isDynamicScope(bool& requiresDynamicChecks) const
+// 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)
{
- switch (structure()->typeInfo().type()) {
- case GlobalObjectType:
- return static_cast<const JSGlobalObject*>(this)->isDynamicScope(requiresDynamicChecks);
- case ActivationObjectType:
- return static_cast<const JSActivation*>(this)->isDynamicScope(requiresDynamicChecks);
- case NameScopeObjectType:
- return static_cast<const JSNameScope*>(this)->isDynamicScope(requiresDynamicChecks);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
+ if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<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;
+ }
- return false;
-}
+ SymbolTableEntry entry = lexicalEnvironment->symbolTable()->get(ident.impl());
+ 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;
+ }
-JSObject* JSScope::objectAtScope(JSScope* scope)
-{
- JSObject* object = scope;
- if (object->structure()->typeInfo().type() == WithScopeType)
- return jsCast<JSWithScope*>(object)->object();
+ if (!entry.isNull()) {
+ op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, 0, lexicalEnvironment, entry.watchpointSet(), entry.scopeOffset().offset());
+ return true;
+ }
- return object;
-}
+ if (JSModuleEnvironment* moduleEnvironment = jsDynamicCast<JSModuleEnvironment*>(scope)) {
+ JSModuleRecord* moduleRecord = moduleEnvironment->moduleRecord();
+ JSModuleRecord::Resolution resolution = moduleRecord->resolveImport(exec, ident);
+ if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+ JSModuleRecord* importedRecord = resolution.moduleRecord;
+ JSModuleEnvironment* importedEnvironment = importedRecord->moduleEnvironment();
+ SymbolTableEntry entry = importedEnvironment->symbolTable()->get(resolution.localName.impl());
+ ASSERT(!entry.isNull());
+ op = ResolveOp(makeType(ModuleVar, needsVarInjectionChecks), depth, 0, importedEnvironment, entry.watchpointSet(), entry.scopeOffset().offset(), resolution.localName.impl());
+ return true;
+ }
+ }
-int JSScope::localDepth()
-{
- int scopeDepth = 0;
- ScopeChainIterator iter = this->begin();
- ScopeChainIterator end = this->end();
- while (!iter->inherits(&JSActivation::s_info)) {
- ++iter;
- if (iter == end)
- break;
- ++scopeDepth;
+ if (lexicalEnvironment->symbolTable()->usesNonStrictEval())
+ needsVarInjectionChecks = true;
+ return false;
}
- return scopeDepth;
-}
-
-struct LookupResult {
- JSValue base() const { return m_base; }
- JSValue value() const { return m_value; }
- void setBase(JSValue base) { ASSERT(base); m_base = base; }
- void setValue(JSValue value) { ASSERT(value); m_value = value; }
-
-private:
- JSValue m_base;
- JSValue m_value;
-};
-
-static void setPutPropertyAccessOffset(PutToBaseOperation* operation, PropertyOffset offset)
-{
- ASSERT(isOutOfLineOffset(offset));
- operation->m_offset = offset;
- operation->m_offsetInButterfly = offsetInButterfly(offset);
-}
-
-static bool executeResolveOperations(CallFrame* callFrame, JSScope* scope, const Identifier& propertyName, ResolveOperation* pc, LookupResult& result)
-{
- while (true) {
- switch (pc->m_operation) {
- case ResolveOperation::Fail:
- return false;
- case ResolveOperation::CheckForDynamicEntriesBeforeGlobalScope: {
- while (JSScope* nextScope = scope->next()) {
- if (scope->isActivationObject() && scope->structure() != scope->globalObject()->activationStructure())
- return false;
- ASSERT(scope->isNameScopeObject() || scope->isVariableObject() || scope->isGlobalObject());
- scope = nextScope;
- }
- pc++;
- break;
- }
- case ResolveOperation::SetBaseToUndefined:
- result.setBase(jsUndefined());
- pc++;
- continue;
- case ResolveOperation::SetBaseToScope:
- result.setBase(scope);
- pc++;
- continue;
- case ResolveOperation::ReturnScopeAsBase:
- result.setBase(scope);
- return true;
- case ResolveOperation::SetBaseToGlobal:
- result.setBase(scope->globalObject());
- pc++;
- continue;
- case ResolveOperation::SkipScopes: {
- int count = pc->m_scopesToSkip;
- while (count--)
- scope = scope->next();
- ASSERT(scope);
- pc++;
- continue;
- }
- case ResolveOperation::SkipTopScopeNode:
- if (callFrame->r(pc->m_activationRegister).jsValue())
- scope = scope->next();
- ASSERT(scope);
- pc++;
- continue;
- case ResolveOperation::GetAndReturnScopedVar:
- ASSERT(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
- result.setValue(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
- return true;
- case ResolveOperation::GetAndReturnGlobalVar:
- result.setValue(pc->m_registerAddress->get());
- return true;
- case ResolveOperation::GetAndReturnGlobalVarWatchable:
- result.setValue(pc->m_registerAddress->get());
- return true;
- case ResolveOperation::ReturnGlobalObjectAsBase:
- result.setBase(callFrame->lexicalGlobalObject());
- return true;
- case ResolveOperation::GetAndReturnGlobalProperty: {
- JSGlobalObject* globalObject = scope->globalObject();
- if (globalObject->structure() == pc->m_structure.get()) {
- result.setValue(globalObject->getDirect(pc->m_offset));
+ if (JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsDynamicCast<JSGlobalLexicalEnvironment*>(scope)) {
+ SymbolTableEntry entry = globalLexicalEnvironment->symbolTable()->get(ident.impl());
+ if (!entry.isNull()) {
+ if (getOrPut == Put && entry.isReadOnly() && initializationMode != Initialization) {
+ // 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;
}
- PropertySlot slot(globalObject);
- if (!globalObject->getPropertySlot(callFrame, propertyName, slot))
- return false;
-
- JSValue value = slot.getValue(callFrame, propertyName);
- if (callFrame->hadException())
- return false;
+ // We can try to 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 == Initialization ? GlobalLexicalVar : makeType(GlobalLexicalVar, needsVarInjectionChecks);
+ op = ResolveOp(
+ resolveType, depth, 0, 0, entry.watchpointSet(),
+ reinterpret_cast<uintptr_t>(globalLexicalEnvironment->variableAt(entry.scopeOffset()).slot()));
+ return true;
+ }
- Structure* structure = globalObject->structure();
+ return false;
+ }
- // Don't try to cache prototype lookups
- if (globalObject != slot.slotBase() || !slot.isCacheableValue() || !structure->propertyAccessesAreCacheable()) {
- result.setValue(value);
+ 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, 0);
return true;
}
- pc->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), structure);
- pc->m_offset = slot.cachedOffset();
- result.setValue(value);
+ 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;
}
-template <JSScope::LookupMode mode, JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScopeInternal(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool )
+JSObject* JSScope::objectAtScope(JSScope* scope)
{
- JSScope* scope = callFrame->scope();
- ASSERT(scope);
- int scopeCount = 0;
- bool seenGenericObjectScope = false;
- bool requiresDynamicChecks = false;
- bool skipTopScopeNode = false;
- int activationRegister = 0;
- CodeBlock* codeBlock = callFrame->codeBlock();
- if (mode == UnknownResolve) {
- ASSERT(operations->isEmpty());
- if (codeBlock->codeType() == FunctionCode && codeBlock->needsActivation()) {
- activationRegister = codeBlock->activationRegister();
- JSValue activation = callFrame->r(activationRegister).jsValue();
-
- // If the activation register doesn't match our actual scope, a dynamic
- // scope has been inserted so we shouldn't skip the top scope node.
- if (activation == scope) {
- jsCast<JSActivation*>(activation.asCell())->isDynamicScope(requiresDynamicChecks);
- if (!requiresDynamicChecks) {
- ASSERT(jsCast<JSActivation*>(activation.asCell())->symbolTable()->get(identifier.impl()).isNull());
- scope = scope->next();
- ASSERT(scope);
- skipTopScopeNode = true;
- }
- } else if (!activation)
- skipTopScopeNode = true;
- }
- } else
- ASSERT(operations->size());
-
- if (codeBlock->codeType() == EvalCode && scope->next())
- requiresDynamicChecks = true;
-
- if (mode == UnknownResolve && putToBaseOperation)
- putToBaseOperation->m_kind = PutToBaseOperation::Generic;
-
- do {
- JSObject* object = JSScope::objectAtScope(scope);
- slot = PropertySlot(object);
-
- bool currentScopeNeedsDynamicChecks = false;
- if (!(scope->isVariableObject() || scope->isNameScopeObject()) || (scope->next() && scope->isDynamicScope(currentScopeNeedsDynamicChecks)))
- seenGenericObjectScope = true;
-
- requiresDynamicChecks = requiresDynamicChecks || currentScopeNeedsDynamicChecks;
-
- if (object->getPropertySlot(callFrame, identifier, slot)) {
- if (mode == UnknownResolve) {
- if (seenGenericObjectScope)
- goto fail;
- if (putToBaseOperation)
- putToBaseOperation->m_isDynamic = requiresDynamicChecks;
- if (!scope->next()) {
- // Global lookup of some kind
- JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(scope);
- SymbolTableEntry entry = globalObject->symbolTable()->get(identifier.impl());
- if (!entry.isNull()) {
- if (requiresDynamicChecks)
- operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
-
- if (putToBaseOperation) {
- putToBaseOperation->m_isDynamic = requiresDynamicChecks;
- if (entry.isReadOnly())
- putToBaseOperation->m_kind = PutToBaseOperation::Readonly;
- else if (entry.couldBeWatched()) {
- putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePutChecked;
- putToBaseOperation->m_predicatePointer = entry.addressOfIsWatched();
- } else
- putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePut;
- putToBaseOperation->m_registerAddress = &globalObject->registerAt(entry.getIndex());
- }
- // Override custom accessor behaviour that the DOM introduces for some
- // event handlers declared on function declarations.
- if (!requiresDynamicChecks)
- slot.setValue(globalObject, globalObject->registerAt(entry.getIndex()).get());
- switch (returnValues) {
- case ReturnValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
- break;
- case ReturnBase:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::returnGlobalObjectAsBase());
- break;
- case ReturnBaseAndValue:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::setBaseToGlobal());
- operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
- break;
- case ReturnThisAndValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::setBaseToUndefined());
- operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
- break;
- }
- } else {
- if (!slot.isCacheableValue() || slot.slotBase() != globalObject)
- goto fail;
-
- if (requiresDynamicChecks)
- operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
-
- if (putToBaseOperation) {
- putToBaseOperation->m_isDynamic = requiresDynamicChecks;
- putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
- putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), globalObject->structure());
- setPutPropertyAccessOffset(putToBaseOperation, slot.cachedOffset());
- }
- switch (returnValues) {
- case ReturnValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- case ReturnBase:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::returnGlobalObjectAsBase());
- break;
- case ReturnBaseAndValue:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::setBaseToGlobal());
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- case ReturnThisAndValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::setBaseToUndefined());
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- }
- }
- return object;
- }
- if (!requiresDynamicChecks) {
- // Normal lexical lookup
- JSVariableObject* variableObject = jsCast<JSVariableObject*>(scope);
- ASSERT(variableObject);
- ASSERT(variableObject->symbolTable());
- SymbolTableEntry entry = variableObject->symbolTable()->get(identifier.impl());
- // Defend against the variable being actually inserted by eval.
- if (entry.isNull()) {
- ASSERT(!jsDynamicCast<JSNameScope*>(variableObject));
- goto fail;
- }
- // If we're getting the 'arguments' then give up on life.
- if (identifier == callFrame->propertyNames().arguments)
- goto fail;
-
- if (putToBaseOperation) {
- putToBaseOperation->m_kind = entry.isReadOnly() ? PutToBaseOperation::Readonly : PutToBaseOperation::VariablePut;
- putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), callFrame->lexicalGlobalObject()->activationStructure());
- putToBaseOperation->m_offset = entry.getIndex();
- putToBaseOperation->m_scopeDepth = (skipTopScopeNode ? 1 : 0) + scopeCount;
- }
-
- if (skipTopScopeNode)
- operations->append(ResolveOperation::skipTopScopeNode(activationRegister));
-
- operations->append(ResolveOperation::skipScopes(scopeCount));
- switch (returnValues) {
- case ReturnBaseAndValue:
- operations->append(ResolveOperation::setBaseToScope());
- operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
- break;
-
- case ReturnBase:
- operations->append(ResolveOperation::returnScopeAsBase());
- break;
-
- case ReturnThisAndValue:
- operations->append(ResolveOperation::setBaseToUndefined());
- // fallthrough
- case ReturnValue:
- operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
- break;
- }
- return object;
- }
- fail:
- if (!operations->size())
- operations->append(ResolveOperation::resolveFail());
- }
- return object;
- }
- scopeCount++;
- } while ((scope = scope->next()));
-
- if (mode == UnknownResolve) {
- ASSERT(operations->isEmpty());
- if (seenGenericObjectScope) {
- operations->append(ResolveOperation::resolveFail());
- return 0;
- }
- if (putToBaseOperation) {
- putToBaseOperation->m_isDynamic = requiresDynamicChecks;
- putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
- putToBaseOperation->m_structure.clear();
- putToBaseOperation->m_offset = -1;
- }
- if (requiresDynamicChecks)
- operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
- switch (returnValues) {
- case ReturnValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- case ReturnBase:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::returnGlobalObjectAsBase());
- break;
- case ReturnBaseAndValue:
- ASSERT(putToBaseOperation);
- operations->append(ResolveOperation::setBaseToGlobal());
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- case ReturnThisAndValue:
- ASSERT(!putToBaseOperation);
- operations->append(ResolveOperation::setBaseToUndefined());
- operations->append(ResolveOperation::getAndReturnGlobalProperty());
- break;
- }
- }
- return 0;
+ JSObject* object = scope;
+ if (object->type() == WithScopeType)
+ return jsCast<JSWithScope*>(object)->object();
+
+ return object;
}
-template <JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScope(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool isStrict)
+// When an exception occurs, the result of isUnscopable becomes false.
+static inline bool isUnscopable(ExecState* exec, JSScope* scope, JSObject* object, const Identifier& ident)
{
- if (operations->size())
- return resolveContainingScopeInternal<KnownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
- JSObject* result = resolveContainingScopeInternal<UnknownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
- operations->shrinkToFit();
- return result;
+ if (scope->type() != WithScopeType)
+ return false;
+
+ JSValue unscopables = object->get(exec, exec->propertyNames().unscopablesSymbol);
+ if (exec->hadException())
+ return false;
+ if (!unscopables.isObject())
+ return false;
+ JSValue blocked = jsCast<JSObject*>(unscopables)->get(exec, ident);
+ if (exec->hadException())
+ return false;
+
+ return blocked.toBoolean(exec);
}
-JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier, ResolveOperations* operations)
+JSValue JSScope::resolve(ExecState* exec, JSScope* scope, const Identifier& ident)
{
- ASSERT(operations);
- LookupResult fastResult;
- if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- return fastResult.value();
- }
+ ScopeChainIterator end = scope->end();
+ ScopeChainIterator it = scope->begin();
+ while (1) {
+ JSScope* scope = it.scope();
+ JSObject* object = it.get();
- if (callFrame->hadException())
- return JSValue();
+ if (++it == end) // Global scope.
+ return object;
- PropertySlot slot;
- if (JSScope::resolveContainingScope<ReturnValue>(callFrame, identifier, slot, operations, 0, false)) {
- ASSERT(operations->size());
- return slot.getValue(callFrame, identifier);
+ if (object->hasProperty(exec, ident)) {
+ if (!isUnscopable(exec, scope, object, ident))
+ return object;
+ ASSERT_WITH_MESSAGE(!exec->hadException(), "When an exception occurs, the result of isUnscopable becomes false");
+ }
}
- ASSERT(operations->size());
-
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}
-JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
+ResolveOp JSScope::abstractResolve(ExecState* exec, size_t depthOffset, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType, InitializationMode initializationMode)
{
- ASSERT(operations);
- ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
- LookupResult fastResult;
- if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
- ASSERT(fastResult.base());
- ASSERT(!callFrame->hadException());
- return fastResult.base();
+ ResolveOp op(Dynamic, 0, 0, 0, 0, 0);
+ if (unlinkedType == Dynamic)
+ return op;
+
+ bool needsVarInjectionChecks = JSC::needsVarInjectionChecks(unlinkedType);
+ size_t depth = depthOffset;
+ for (; scope; scope = scope->next()) {
+ if (abstractAccess(exec, scope, ident, getOrPut, depth, needsVarInjectionChecks, op, initializationMode))
+ break;
+ ++depth;
}
- if (callFrame->hadException())
- return JSValue();
+ return op;
+}
- PropertySlot slot;
- if (JSObject* base = JSScope::resolveContainingScope<ReturnBase>(callFrame, identifier, slot, operations, putToBaseOperations, isStrict)) {
- ASSERT(operations->size());
- return base;
- }
+void JSScope::collectVariablesUnderTDZ(JSScope* scope, VariableEnvironment& result)
+{
+ for (; scope; scope = scope->next()) {
+ if (!scope->isLexicalScope() && !scope->isGlobalLexicalEnvironment())
+ continue;
- if (!isStrict)
- return callFrame->lexicalGlobalObject();
+ if (scope->isModuleScope()) {
+ JSModuleRecord* moduleRecord = jsCast<JSModuleEnvironment*>(scope)->moduleRecord();
+ for (const auto& pair : moduleRecord->importEntries())
+ result.add(pair.key);
+ }
- return throwError(callFrame, createErrorForInvalidGlobalAssignment(callFrame, identifier.string()));
+ SymbolTable* symbolTable = jsCast<JSSymbolTableObject*>(scope)->symbolTable();
+ ASSERT(symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope || symbolTable->scopeType() == SymbolTable::ScopeType::GlobalLexicalScope);
+ ConcurrentJITLocker locker(symbolTable->m_lock);
+ for (auto end = symbolTable->end(locker), iter = symbolTable->begin(locker); iter != end; ++iter)
+ result.add(iter->key);
+ }
}
-JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
+template <typename EnvironmentType, SymbolTable::ScopeType scopeType>
+inline static bool isScopeType(JSScope* scope)
{
- ASSERT(operations);
- ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
- LookupResult fastResult;
- if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
- ASSERT(fastResult.base());
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- *base = fastResult.base();
- return fastResult.value();
- }
-
- if (callFrame->hadException())
- return JSValue();
+ EnvironmentType* environment = jsDynamicCast<EnvironmentType*>(scope);
+ if (!environment)
+ return false;
- PropertySlot slot;
- if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnBaseAndValue>(callFrame, identifier, slot, operations, putToBaseOperations, false)) {
- ASSERT(operations->size());
- JSValue value = slot.getValue(callFrame, identifier);
- if (callFrame->vm().exception)
- return JSValue();
-
- *base = propertyBase;
- return value;
- }
- ASSERT(operations->size());
+ return environment->symbolTable()->scopeType() == scopeType;
+}
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+bool JSScope::isVarScope()
+{
+ return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::VarScope>(this);
}
-JSValue JSScope::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations)
+bool JSScope::isLexicalScope()
{
- ASSERT(operations);
- LookupResult fastResult;
- if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
- ASSERT(fastResult.base());
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- *base = fastResult.base();
- return fastResult.value();
- }
+ return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::LexicalScope>(this);
+}
- if (callFrame->hadException())
- return JSValue();
-
- PropertySlot slot;
- if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnThisAndValue>(callFrame, identifier, slot, operations, 0, false)) {
- ASSERT(operations->size());
- JSValue value = slot.getValue(callFrame, identifier);
- if (callFrame->vm().exception)
- return JSValue();
- ASSERT(value);
- *base = propertyBase->structure()->typeInfo().isEnvironmentRecord() ? jsUndefined() : JSValue(propertyBase);
- return value;
- }
- ASSERT(operations->size());
+bool JSScope::isModuleScope()
+{
+ return isScopeType<JSModuleEnvironment, SymbolTable::ScopeType::LexicalScope>(this);
+}
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+bool JSScope::isCatchScope()
+{
+ return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::CatchScope>(this);
}
-void JSScope::resolvePut(CallFrame* callFrame, JSValue base, const Identifier& property, JSValue value, PutToBaseOperation* operation)
+bool JSScope::isFunctionNameScopeObject()
{
- ASSERT_UNUSED(operation, operation);
- ASSERT(base);
- ASSERT(value);
- switch (operation->m_kind) {
- case PutToBaseOperation::Uninitialised:
- CRASH();
-
- case PutToBaseOperation::Readonly:
- return;
-
- case PutToBaseOperation::GlobalVariablePutChecked:
- if (*operation->m_predicatePointer)
- goto genericHandler;
- case PutToBaseOperation::GlobalVariablePut:
- if (operation->m_isDynamic) {
- JSObject* baseObject = jsCast<JSObject*>(base);
- if (baseObject != callFrame->lexicalGlobalObject()) {
- if (baseObject->isGlobalObject())
- ASSERT(!jsCast<JSGlobalObject*>(baseObject)->assertRegisterIsInThisObject(operation->m_registerAddress));
- goto genericHandler;
- }
- }
- operation->m_registerAddress->set(callFrame->vm(), base.asCell(), value);
- return;
-
- case PutToBaseOperation::VariablePut: {
- if (operation->m_isDynamic) {
- JSObject* baseObject = jsCast<JSObject*>(base);
- if (baseObject->structure() != operation->m_structure.get())
- goto genericHandler;
- }
- JSVariableObject* variableObject = jsCast<JSVariableObject*>(base);
- variableObject->registerAt(operation->m_offset).set(callFrame->vm(), variableObject, value);
- return;
- }
+ return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::FunctionNameScope>(this);
+}
- case PutToBaseOperation::GlobalPropertyPut: {
- JSObject* object = jsCast<JSObject*>(base);
- if (operation->m_structure.get() != object->structure())
- break;
- object->putDirect(callFrame->vm(), operation->m_offset, value);
- return;
- }
+bool JSScope::isGlobalLexicalEnvironment()
+{
+ return isScopeType<JSGlobalLexicalEnvironment, SymbolTable::ScopeType::GlobalLexicalScope>(this);
+}
- genericHandler:
- case PutToBaseOperation::Generic:
- PutPropertySlot slot(operation->m_isStrict);
- base.put(callFrame, property, value, slot);
- return;
- }
- ASSERT(operation->m_kind == PutToBaseOperation::GlobalPropertyPut);
- PutPropertySlot slot(operation->m_isStrict);
- base.put(callFrame, property, value, slot);
- if (!slot.isCacheable())
- return;
- if (callFrame->hadException())
- return;
- JSObject* baseObject = jsCast<JSObject*>(base);
- if (!baseObject->structure()->propertyAccessesAreCacheable())
- return;
- if (slot.base() != callFrame->lexicalGlobalObject())
- return;
- if (slot.base() != baseObject)
- return;
- ASSERT(!baseObject->hasInlineStorage());
- operation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), baseObject->structure());
- setPutPropertyAccessOffset(operation, slot.cachedOffset());
- return;
+bool JSScope::isNestedLexicalScope()
+{
+ if (JSLexicalEnvironment* environment = jsDynamicCast<JSLexicalEnvironment*>(this))
+ return environment->symbolTable()->isNestedLexicalScope();
+ return false;
}
-JSValue JSScope::resolveGlobal(CallFrame* callFrame, const Identifier& identifier, JSGlobalObject* globalObject, ResolveOperation* resolveOperation)
+JSScope* JSScope::constantScopeForCodeBlock(ResolveType type, CodeBlock* codeBlock)
{
- ASSERT(resolveOperation);
- ASSERT(resolveOperation->m_operation == ResolveOperation::GetAndReturnGlobalProperty);
- ASSERT_UNUSED(globalObject, callFrame->lexicalGlobalObject() == globalObject);
-
- LookupResult fastResult;
- if (executeResolveOperations(callFrame, callFrame->scope(), identifier, resolveOperation, fastResult)) {
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- return fastResult.value();
+ switch (type) {
+ case GlobalProperty:
+ case GlobalVar:
+ case GlobalPropertyWithVarInjectionChecks:
+ case GlobalVarWithVarInjectionChecks:
+ return codeBlock->globalObject();
+ case GlobalLexicalVarWithVarInjectionChecks:
+ case GlobalLexicalVar:
+ return codeBlock->globalObject()->globalLexicalEnvironment();
+ default:
+ return nullptr;
}
- if (callFrame->hadException())
- return JSValue();
-
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
}
-
} // namespace JSC