/*
- * 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
#include "config.h"
#include "JSScope.h"
-#include "JSActivation.h"
#include "JSGlobalObject.h"
+#include "JSLexicalEnvironment.h"
#include "JSNameScope.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
-{
- 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;
- }
-
- return false;
-}
-
-JSObject* JSScope::objectAtScope(JSScope* scope)
-{
- JSObject* object = scope;
- if (object->structure()->typeInfo().type() == WithScopeType)
- return jsCast<JSWithScope*>(object)->object();
-
- return object;
-}
-
-int JSScope::localDepth()
+// 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)
{
- int scopeDepth = 0;
- ScopeChainIterator iter = this->begin();
- ScopeChainIterator end = this->end();
- while (!iter->inherits(&JSActivation::s_info)) {
- ++iter;
- if (iter == end)
- break;
- ++scopeDepth;
- }
- 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);
+ 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;
- 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));
- return true;
- }
- PropertySlot slot(globalObject);
- if (!globalObject->getPropertySlot(callFrame, propertyName, slot))
- 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;
+ }
- JSValue value = slot.getValue(callFrame, propertyName);
- if (callFrame->hadException())
- return false;
+ if (!entry.isNull()) {
+ op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, 0, lexicalEnvironment, entry.watchpointSet(), entry.scopeOffset().offset());
+ return true;
+ }
- Structure* structure = globalObject->structure();
+ if (lexicalEnvironment->symbolTable()->usesNonStrictEval())
+ needsVarInjectionChecks = true;
+ 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;
}
- }
- }
-}
-
-template <JSScope::LookupMode mode, JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScopeInternal(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool )
-{
- 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;
+ 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, 0);
+ return true;
}
- }
- return 0;
-}
-
-template <JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScope(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool isStrict)
-{
- 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;
+
+ 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;
}
-JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier, ResolveOperations* operations)
+JSObject* JSScope::objectAtScope(JSScope* scope)
{
- ASSERT(operations);
- LookupResult fastResult;
- if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- return fastResult.value();
- }
-
- if (callFrame->hadException())
- return JSValue();
-
- PropertySlot slot;
- if (JSScope::resolveContainingScope<ReturnValue>(callFrame, identifier, slot, operations, 0, false)) {
- ASSERT(operations->size());
- return slot.getValue(callFrame, identifier);
- }
- ASSERT(operations->size());
+ JSObject* object = scope;
+ if (object->type() == WithScopeType)
+ return jsCast<JSWithScope*>(object)->object();
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+ return object;
}
-JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
+int JSScope::depth()
{
- 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();
- }
-
- if (callFrame->hadException())
- return JSValue();
-
- PropertySlot slot;
- if (JSObject* base = JSScope::resolveContainingScope<ReturnBase>(callFrame, identifier, slot, operations, putToBaseOperations, isStrict)) {
- ASSERT(operations->size());
- return base;
- }
-
- if (!isStrict)
- return callFrame->lexicalGlobalObject();
-
- return throwError(callFrame, createErrorForInvalidGlobalAssignment(callFrame, identifier.string()));
+ int depth = 0;
+ for (JSScope* scope = this; scope; scope = scope->next())
+ ++depth;
+ return depth;
}
-JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
+// When an exception occurs, the result of isUnscopable becomes false.
+static inline bool isUnscopable(ExecState* exec, JSScope* scope, JSObject* object, const Identifier& ident)
{
- 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();
-
- 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 throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+ 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::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base, 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.base());
- ASSERT(fastResult.value());
- ASSERT(!callFrame->hadException());
- *base = fastResult.base();
- 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 (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;
+ 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));
}
-void JSScope::resolvePut(CallFrame* callFrame, JSValue base, const Identifier& property, JSValue value, PutToBaseOperation* operation)
+ResolveOp JSScope::abstractResolve(ExecState* exec, bool hasTopActivation, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType)
{
- 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;
- }
-
- case PutToBaseOperation::GlobalPropertyPut: {
- JSObject* object = jsCast<JSObject*>(base);
- if (operation->m_structure.get() != object->structure())
+ ResolveOp op(Dynamic, 0, 0, 0, 0, 0);
+ if (unlinkedType == Dynamic)
+ return op;
+
+ size_t depth = hasTopActivation ? 1 : 0;
+ bool needsVarInjectionChecks = JSC::needsVarInjectionChecks(unlinkedType);
+ for (; scope; scope = scope->next()) {
+ if (abstractAccess(exec, scope, ident, getOrPut, depth, needsVarInjectionChecks, op))
break;
- object->putDirect(callFrame->vm(), operation->m_offset, value);
- return;
+ ++depth;
}
- 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;
+ return op;
}
-JSValue JSScope::resolveGlobal(CallFrame* callFrame, const Identifier& identifier, JSGlobalObject* globalObject, ResolveOperation* resolveOperation)
+const char* resolveModeName(ResolveMode mode)
{
- 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();
- }
-
- if (callFrame->hadException())
- return JSValue();
-
- return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
+ static const char* const names[] = {
+ "ThrowIfNotFound",
+ "DoNotThrowIfNotFound"
+ };
+ return names[mode];
}
+const char* resolveTypeName(ResolveType type)
+{
+ static const char* const names[] = {
+ "GlobalProperty",
+ "GlobalVar",
+ "ClosureVar",
+ "LocalClosureVar",
+ "GlobalPropertyWithVarInjectionChecks",
+ "GlobalVarWithVarInjectionChecks",
+ "ClosureVarWithVarInjectionChecks",
+ "Dynamic"
+ };
+ return names[type];
+}
} // namespace JSC