X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/93a3786624b2768d89bfa27e46598dc64e2fb70a..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/runtime/JSScope.cpp?ds=sidebyside diff --git a/runtime/JSScope.cpp b/runtime/JSScope.cpp index 69ff1e4..32ced61 100644 --- a/runtime/JSScope.cpp +++ b/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,189 @@ #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(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(this)->isDynamicScope(requiresDynamicChecks); - case ActivationObjectType: - return static_cast(this)->isDynamicScope(requiresDynamicChecks); - case NameScopeObjectType: - return static_cast(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(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(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(scope)->registerAt(pc->m_offset).get()); - result.setValue(jsCast(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(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(globalObject->variableAt(entry.scopeOffset()).slot())); return true; } - } - } -} - -template JSObject* JSScope::resolveContainingScopeInternal(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector* 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(activation.asCell())->isDynamicScope(requiresDynamicChecks); - if (!requiresDynamicChecks) { - ASSERT(jsCast(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(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(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(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 JSObject* JSScope::resolveContainingScope(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector* operations, PutToBaseOperation* putToBaseOperation, bool isStrict) -{ - if (operations->size()) - return resolveContainingScopeInternal(callFrame, identifier, slot, operations, putToBaseOperation, isStrict); - JSObject* result = resolveContainingScopeInternal(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(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(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(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(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(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(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(base); - if (baseObject != callFrame->lexicalGlobalObject()) { - if (baseObject->isGlobalObject()) - ASSERT(!jsCast(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(base); - if (baseObject->structure() != operation->m_structure.get()) - goto genericHandler; - } - JSVariableObject* variableObject = jsCast(base); - variableObject->registerAt(operation->m_offset).set(callFrame->vm(), variableObject, value); - return; - } - - case PutToBaseOperation::GlobalPropertyPut: { - JSObject* object = jsCast(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(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