| /* |
| * Copyright (C) 2012 Apple Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "JSScope.h" |
| |
| #include "JSActivation.h" |
| #include "JSGlobalObject.h" |
| #include "JSNameScope.h" |
| #include "JSWithScope.h" |
| |
| namespace JSC { |
| |
| ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSScope); |
| |
| void JSScope::visitChildren(JSCell* cell, SlotVisitor& visitor) |
| { |
| JSScope* thisObject = jsCast<JSScope*>(cell); |
| ASSERT_GC_OBJECT_INHERITS(thisObject, JSObject::s_classinfo()); |
| COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); |
| ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); |
| |
| 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: |
| 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() |
| { |
| int scopeDepth = 0; |
| ScopeChainIterator iter = this->begin(); |
| ScopeChainIterator end = this->end(); |
| while (!iter->inherits(JSActivation::s_classinfo())) { |
| ++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); |
| 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; |
| |
| JSValue value = slot.getValue(callFrame, propertyName); |
| if (callFrame->hadException()) |
| return false; |
| |
| Structure* structure = globalObject->structure(); |
| |
| // Don't try to cache prototype lookups |
| if (globalObject != slot.slotBase() || !slot.isCacheableValue() || !structure->propertyAccessesAreCacheable()) { |
| result.setValue(value); |
| return true; |
| } |
| |
| pc->m_structure.set(callFrame->globalData(), callFrame->codeBlock()->ownerExecutable(), structure); |
| pc->m_offset = slot.cachedOffset(); |
| result.setValue(value); |
| 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->globalData(), 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->globalData(), 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; |
| } |
| |
| 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; |
| } |
| |
| JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier, ResolveOperations* operations) |
| { |
| 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()); |
| |
| return throwError(callFrame, createUndefinedVariableError(callFrame, identifier)); |
| } |
| |
| JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations) |
| { |
| 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())); |
| } |
| |
| JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations) |
| { |
| 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->globalData().exception) |
| return JSValue(); |
| |
| *base = propertyBase; |
| return value; |
| } |
| ASSERT(operations->size()); |
| |
| return throwError(callFrame, createUndefinedVariableError(callFrame, identifier)); |
| } |
| |
| JSValue JSScope::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations) |
| { |
| 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(); |
| } |
| |
| 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->globalData().exception) |
| return JSValue(); |
| ASSERT(value); |
| *base = propertyBase->structure()->typeInfo().isEnvironmentRecord() ? jsUndefined() : JSValue(propertyBase); |
| return value; |
| } |
| ASSERT(operations->size()); |
| |
| return throwError(callFrame, createUndefinedVariableError(callFrame, identifier)); |
| } |
| |
| void JSScope::resolvePut(CallFrame* callFrame, JSValue base, const Identifier& property, JSValue value, PutToBaseOperation* operation) |
| { |
| 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->globalData(), 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->globalData(), variableObject, value); |
| return; |
| } |
| |
| case PutToBaseOperation::GlobalPropertyPut: { |
| JSObject* object = jsCast<JSObject*>(base); |
| if (operation->m_structure.get() != object->structure()) |
| break; |
| object->putDirect(callFrame->globalData(), operation->m_offset, value); |
| return; |
| } |
| |
| 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->globalData(), callFrame->codeBlock()->ownerExecutable(), baseObject->structure()); |
| setPutPropertyAccessOffset(operation, slot.cachedOffset()); |
| return; |
| } |
| |
| JSValue JSScope::resolveGlobal(CallFrame* callFrame, const Identifier& identifier, JSGlobalObject* globalObject, ResolveOperation* resolveOperation) |
| { |
| 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)); |
| } |
| |
| |
| } // namespace JSC |