Ignore:
Timestamp:
Jul 24, 2013, 9:02:40 PM (12 years ago)
Author:
[email protected]
Message:

fourthTier: Re-worked non-local variable resolution
https://bugs.webkit.org/show_bug.cgi?id=117375

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch has two goals:

(1) Simplicity.

  • Net removes 15 opcodes.
  • Net removes 2,000 lines of code.
  • Removes setPair() from the DFG: All DFG nodes have 1 result register now.

(2) Performance.

  • 2%-3% speedup on SunSpider (20% in LLInt and Baseline JIT)
  • 2% speedup on v8-spider
  • 10% speedup on js-regress-hashmap*
  • Amusing 2X speedup on js-regress-poly-stricteq

The bytecode now separates the scope chain resolution opcode from the
scope access opcode.

OLD:

get_scoped_var r0, 1, 0
inc r0
put_scoped_var 1, 0, r0

NEW:

resolve_scope r0, x(@id0)
get_from_scope r1, r0, x(@id0)
inc r1
put_to_scope r0, x(@id0), r1

Also, we link non-local variable resolution opcodes at CodeBlock link
time instead of time of first opcode execution.

This means that we can represent all possible non-local variable
resolutions using just three opcodes, and any optimizations in these
opcodes naturally apply across-the-board.

  • API/JSCTestRunnerUtils.cpp:

(JSC::numberOfDFGCompiles):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpBytecode): Updated for removed things.

(JSC::CodeBlock::CodeBlock): Always provide the full scope chain when
creating a CodeBlock, so we can perform non-local variable resolution.

Added code to perform linking for these opcodes. This is where we figure
out which non-local variable resolutions are optimizable, and how.

(JSC::CodeBlock::finalizeUnconditionally):
(JSC::CodeBlock::noticeIncomingCall):
(JSC::CodeBlock::optimizeAfterWarmUp):
(JSC::CodeBlock::optimizeAfterLongWarmUp):
(JSC::CodeBlock::optimizeSoon): Updated for removed things.

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::needsActivation):
(JSC::GlobalCodeBlock::GlobalCodeBlock):
(JSC::ProgramCodeBlock::ProgramCodeBlock):
(JSC::EvalCodeBlock::EvalCodeBlock):
(JSC::FunctionCodeBlock::FunctionCodeBlock):

  • bytecode/EvalCodeCache.h:

(JSC::EvalCodeCache::getSlow): Updated for interface changes.

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeFor): Treat global object access as
optimizable even though the global object has a custom property access
callback. This is what we've always done since, otherwise, we can't
optimize globals. (In future, we probably want to figure out a more
targeted policy than "any property access callback means no
optimization".)

  • bytecode/GlobalResolveInfo.h: Removed.
  • bytecode/Instruction.h:
  • bytecode/Opcode.h:

(JSC::padOpcodeName):

  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeFor): Like GetByIdStatus.

  • bytecode/ResolveGlobalStatus.cpp: Removed.
  • bytecode/ResolveGlobalStatus.h: Removed.
  • bytecode/ResolveOperation.h: Removed.
  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::generateFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::codeBlockFor):
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):

  • bytecode/UnlinkedCodeBlock.h: Don't provide a scope chain to unlinked

code blocks. Giving a scope to an unscoped compilation unit invites
programming errors.

  • bytecode/Watchpoint.h:

(JSC::WatchpointSet::addressOfIsInvalidated):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::local):
(JSC::BytecodeGenerator::constLocal):
(JSC::BytecodeGenerator::resolveType):
(JSC::BytecodeGenerator::emitResolveScope):
(JSC::BytecodeGenerator::emitGetFromScope):
(JSC::BytecodeGenerator::emitPutToScope):
(JSC::BytecodeGenerator::emitInstanceOf):
(JSC::BytecodeGenerator::emitPushWithScope):
(JSC::BytecodeGenerator::emitPopScope):
(JSC::BytecodeGenerator::pushFinallyContext):
(JSC::BytecodeGenerator::emitComplexPopScopes):
(JSC::BytecodeGenerator::popTryAndEmitCatch):
(JSC::BytecodeGenerator::emitPushNameScope):
(JSC::BytecodeGenerator::isArgumentNumber):

  • bytecompiler/BytecodeGenerator.h:

(JSC::Local::Local):
(JSC::Local::operator bool):
(JSC::Local::get):
(JSC::Local::isReadOnly):
(JSC::BytecodeGenerator::scopeDepth):
(JSC::BytecodeGenerator::shouldOptimizeLocals):
(JSC::BytecodeGenerator::canOptimizeNonLocals): Refactored the bytecode
generator to resolve all variables within local scope, as if there
were no non-local scope. This helps provide a separation of concerns:
unlinked bytecode is always scope-free, and the linking stage links
in the provided scope.

  • bytecompiler/NodesCodegen.cpp:

(JSC::ResolveNode::isPure):
(JSC::ResolveNode::emitBytecode):
(JSC::EvalFunctionCallNode::emitBytecode):
(JSC::FunctionCallResolveNode::emitBytecode):
(JSC::PostfixNode::emitResolve):
(JSC::DeleteResolveNode::emitBytecode):
(JSC::TypeOfResolveNode::emitBytecode):
(JSC::PrefixNode::emitResolve):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
(JSC::ForInNode::emitBytecode): A bunch of this codegen is no longer
necessary, since it's redundant with the linking stage.

  • dfg/DFGAbstractState.cpp:

(JSC::DFG::AbstractState::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::ByteCodeParser):
(JSC::DFG::ByteCodeParser::cellConstantWithStructureCheck):
(JSC::DFG::ByteCodeParser::handlePutByOffset):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock): Updated for interface changes.
Notably, we can reuse existing DFG nodes -- but the mapping between
bytecode and DFG nodes has changed, and some nodes and corner cases have
been removed.

  • dfg/DFGCSEPhase.cpp:

(JSC::DFG::CSEPhase::scopedVarLoadElimination):
(JSC::DFG::CSEPhase::varInjectionWatchpointElimination):
(JSC::DFG::CSEPhase::globalVarStoreElimination):
(JSC::DFG::CSEPhase::scopedVarStoreElimination):
(JSC::DFG::CSEPhase::getLocalLoadElimination):
(JSC::DFG::CSEPhase::setLocalStoreElimination):
(JSC::DFG::CSEPhase::performNodeCSE): Added CSE for var injection
watchpoints. Even though watchpoints are "free", they're quite common
inside code that's subject to var injection, so I figured we'd save a
little memory.

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGCapabilities.h: Removed detection for old forms.
  • dfg/DFGDriver.h:

(JSC::DFG::tryCompile):
(JSC::DFG::tryCompileFunction):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGGraph.h:
  • dfg/DFGJITCode.cpp:
  • dfg/DFGNode.h:

(JSC::DFG::Node::convertToStructureTransitionWatchpoint):
(JSC::DFG::Node::hasVarNumber):
(JSC::DFG::Node::hasIdentifierNumberForCheck):
(JSC::DFG::Node::hasRegisterPointer):
(JSC::DFG::Node::hasHeapPrediction):

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGRepatch.h:

(JSC::DFG::dfgResetGetByID):
(JSC::DFG::dfgResetPutByID):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation): Removed some unneeded things,
and updated for renames.

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile): The two primary changes here are:

(1) Use a watchpoint for var injection instead of looping over the scope
chain and checking. This is more efficient and much easier to model in
code generation.

(2) I've eliminated the notion of an optimized global assignment that
needs to check for whether it should fire a watchpiont. Instead, we
fire pre-emptively at the point of optimization. This removes a bunch
of edge cases, and it seems like a more honest representation of
the fact that our new optimization contradicts our old one.

  • dfg/DFGTypeCheckHoistingPhase.cpp:

(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):

  • heap/DFGCodeBlocks.cpp:

(JSC::DFGCodeBlocks::jettison):

  • interpreter/CallFrame.h:

(JSC::ExecState::trueCallFrame): Removed stuff that's unused now, and
fixed the build.

  • interpreter/Interpreter.cpp:

(JSC::eval):
(JSC::getBytecodeOffsetForCallFrame):
(JSC::getCallerInfo):
(JSC::Interpreter::throwException): Updated exception scope tracking
to match the rest of our linking strategy: The unlinked bytecode compiles
exception scope as if non-local scope did not exist, and we add in
non-local scope at link time. This means that we can restore the right
scope depth based on a simple number, without checking the contents of
the scope chain.

(JSC::Interpreter::execute): Make sure to establish the full scope chain
before linking eval code. We now require the full scope chain at link
time, in order to link non-local variable resolution opcodes.

  • jit/JIT.cpp:

(JSC::JIT::JIT):
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):

  • jit/JIT.h:
  • jit/JITArithmetic.cpp:

(JSC::JIT::emit_op_add):

  • jit/JITCode.cpp:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emitSlow_op_bitxor):
(JSC::JIT::emitSlow_op_bitor):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emitSlow_op_to_primitive):
(JSC::JIT::emit_op_strcat):
(JSC::JIT::emitSlow_op_create_this):
(JSC::JIT::emitSlow_op_to_this):

  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emitVarInjectionCheck):
(JSC::JIT::emitResolveClosure):
(JSC::JIT::emit_op_resolve_scope):
(JSC::JIT::emitSlow_op_resolve_scope):
(JSC::JIT::emitLoadWithStructureCheck):
(JSC::JIT::emitGetGlobalProperty):
(JSC::JIT::emitGetGlobalVar):
(JSC::JIT::emitGetClosureVar):
(JSC::JIT::emit_op_get_from_scope):
(JSC::JIT::emitSlow_op_get_from_scope):
(JSC::JIT::emitPutGlobalProperty):
(JSC::JIT::emitPutGlobalVar):
(JSC::JIT::emitPutClosureVar):
(JSC::JIT::emit_op_put_to_scope):
(JSC::JIT::emitSlow_op_put_to_scope):
(JSC::JIT::emit_op_init_global_const):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emitVarInjectionCheck):
(JSC::JIT::emitResolveClosure):
(JSC::JIT::emit_op_resolve_scope):
(JSC::JIT::emitSlow_op_resolve_scope):
(JSC::JIT::emitLoadWithStructureCheck):
(JSC::JIT::emitGetGlobalProperty):
(JSC::JIT::emitGetGlobalVar):
(JSC::JIT::emitGetClosureVar):
(JSC::JIT::emit_op_get_from_scope):
(JSC::JIT::emitSlow_op_get_from_scope):
(JSC::JIT::emitPutGlobalProperty):
(JSC::JIT::emitPutGlobalVar):
(JSC::JIT::emitPutClosureVar):
(JSC::JIT::emit_op_put_to_scope):
(JSC::JIT::emitSlow_op_put_to_scope):
(JSC::JIT::emit_op_init_global_const):

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

  • jit/JITStubs.h: Re-wrote baseline JIT codegen for our new variable

resolution model.

  • llint/LLIntData.cpp:

(JSC::LLInt::Data::performAssertions):

  • llint/LLIntSlowPaths.cpp:
  • llint/LLIntSlowPaths.h:
  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter.cpp:

(JSC::CLoop::execute):

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm: Ditto for LLInt.
  • offlineasm/x86.rb: Fixed a pre-existing encoding bug for a syntactic

form that we never used before.

  • runtime/ArrayPrototype.cpp:

(JSC::arrayProtoFuncToString):
(JSC::arrayProtoFuncToLocaleString):
(JSC::arrayProtoFuncJoin):
(JSC::arrayProtoFuncConcat):
(JSC::arrayProtoFuncPop):
(JSC::arrayProtoFuncPush):
(JSC::arrayProtoFuncReverse):
(JSC::arrayProtoFuncShift):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoFuncSort):
(JSC::arrayProtoFuncSplice):
(JSC::arrayProtoFuncUnShift):
(JSC::arrayProtoFuncFilter):
(JSC::arrayProtoFuncMap):
(JSC::arrayProtoFuncEvery):
(JSC::arrayProtoFuncForEach):
(JSC::arrayProtoFuncSome):
(JSC::arrayProtoFuncReduce):
(JSC::arrayProtoFuncReduceRight):
(JSC::arrayProtoFuncIndexOf):
(JSC::arrayProtoFuncLastIndexOf): Fixed some pre-existing bugs in
'this' value conversion, which I made much more common by removing
special cases in bytecode generation.

These functions need to invoke toThis() because they observe the 'this'
value. Also, toLocaleString() is specified to accept non-array 'this'
values.

(Most other host functions don't need this fix because they perform
strict 'this' checking, which never coerces unexpected types.)

  • runtime/CodeCache.cpp:

(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::getProgramCodeBlock):
(JSC::CodeCache::getEvalCodeBlock):

  • runtime/CodeCache.h: Don't supply a scope to the unlinked code cache.

Unlinked code is supposed to be scope-free, so let's have the compiler
help verify that.

  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/CommonSlowPaths.h:
  • runtime/Executable.cpp:

(JSC::EvalExecutable::create):
(JSC::EvalExecutable::compileInternal):
(JSC::ProgramExecutable::compileInternal):
(JSC::FunctionExecutable::produceCodeBlockFor):
(JSC::FunctionExecutable::compileForCallInternal):
(JSC::FunctionExecutable::compileForConstructInternal):

  • runtime/Executable.h:

(JSC::EvalExecutable::numVariables):
(JSC::EvalExecutable::numberOfFunctionDecls):

  • runtime/ExecutionHarness.h:

(JSC::prepareForExecutionImpl):
(JSC::prepareFunctionForExecutionImpl):
(JSC::installOptimizedCode): Fiddled with executable initialization so
that we can always generate a full scope chain before we go to link a
code block. We need this because code block linking now depends on the
scope chain to link non-local variable resolution opcodes.

  • runtime/JSActivation.h:
  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::createEvalCodeBlock):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::varInjectionWatchpoint):

  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::globalFuncEval):

  • runtime/JSNameScope.h:
  • runtime/JSScope.cpp:

(JSC::abstractAccess):
(JSC::JSScope::objectAtScope):
(JSC::JSScope::depth):
(JSC::JSScope::resolve):
(JSC::JSScope::abstractResolve): Updated to match changes explained above.

  • runtime/JSScope.h:

(JSC::makeType):
(JSC::needsVarInjectionChecks):
(JSC::ResolveOp::ResolveOp):
(JSC::ResolveModeAndType::ResolveModeAndType):
(JSC::ResolveModeAndType::mode):
(JSC::ResolveModeAndType::type):
(JSC::ResolveModeAndType::operand): Removed the old variable resolution
state machine, since it's unused now. Added logic for performing abstract
variable resolution at link time. This is used by codeblock linking.

  • runtime/ObjectPrototype.cpp:

(JSC::objectProtoFuncValueOf):
(JSC::objectProtoFuncHasOwnProperty):
(JSC::objectProtoFuncIsPrototypeOf):
(JSC::objectProtoFuncDefineGetter):
(JSC::objectProtoFuncDefineSetter):
(JSC::objectProtoFuncLookupGetter):
(JSC::objectProtoFuncLookupSetter):
(JSC::objectProtoFuncPropertyIsEnumerable):
(JSC::objectProtoFuncToLocaleString):
(JSC::objectProtoFuncToString): Fixed some pre-existing bugs in
'this' value conversion, which I made much more common by removing
special cases in bytecode generation.

These functions need to invoke toThis() because they observe the 'this'
value.

  • runtime/StringPrototype.cpp:

(JSC::checkObjectCoercible):
(JSC::stringProtoFuncReplace):
(JSC::stringProtoFuncCharAt):
(JSC::stringProtoFuncCharCodeAt):
(JSC::stringProtoFuncConcat):
(JSC::stringProtoFuncIndexOf):
(JSC::stringProtoFuncLastIndexOf):
(JSC::stringProtoFuncMatch):
(JSC::stringProtoFuncSearch):
(JSC::stringProtoFuncSlice):
(JSC::stringProtoFuncSplit):
(JSC::stringProtoFuncSubstr):
(JSC::stringProtoFuncSubstring):
(JSC::stringProtoFuncToLowerCase):
(JSC::stringProtoFuncToUpperCase):
(JSC::stringProtoFuncLocaleCompare):
(JSC::stringProtoFuncBig):
(JSC::stringProtoFuncSmall):
(JSC::stringProtoFuncBlink):
(JSC::stringProtoFuncBold):
(JSC::stringProtoFuncFixed):
(JSC::stringProtoFuncItalics):
(JSC::stringProtoFuncStrike):
(JSC::stringProtoFuncSub):
(JSC::stringProtoFuncSup):
(JSC::stringProtoFuncFontcolor):
(JSC::stringProtoFuncFontsize):
(JSC::stringProtoFuncAnchor):
(JSC::stringProtoFuncLink):
(JSC::trimString): Fixed some pre-existing bugs in
'this' value conversion, which I made much more common by removing
special cases in bytecode generation.

These functions need to invoke toThis() because they observe the 'this'
value.

  • runtime/StructureRareData.cpp:
  • runtime/VM.cpp:

(JSC::VM::~VM):

  • runtime/WriteBarrier.h:

(JSC::WriteBarrierBase::slot): Modified to reduce casting in client code.

LayoutTests:

This patch removed special-case 'this' resolution from bytecode, making
some pre-existing edge cases in 'this' value treatment much more common.

I updated the test results below, and added some tests, to match bug
fixes for these cases.

  • fast/js/script-tests/array-functions-non-arrays.js:
  • fast/js/array-functions-non-arrays-expected.txt: As specified, it's

not an error to pass a non-array to toLocaleString. Our new result
matches Firefox and Chrome.

  • fast/js/array-prototype-properties-expected.txt: Updated for slightly

clearer error message.

  • fast/js/basic-strict-mode-expected.txt: Updated for slightly more

standard error message.

  • fast/js/object-prototype-toString-expected.txt: Added.
  • fast/js/object-prototype-toString.html: Added. This test demonstrates

why we now fail a Sputnik test below, while Firefox and Chrome pass it.
(The test doesn't test what it thinks it tests, and this test verifies
that we get right what it does think it tests.)

  • fast/js/string-prototype-function-this-expected.txt: Added.
  • fast/js/string-prototype-function-this.html: Added. This test shows

that we CheckObjectCoercible in string prototype functions. (We used
to get this wrong, but Sputnik tests made it seem like we got it right
because they didn't test the dynamic scope case.)

  • sputnik/Conformance/11_Expressions/11.1_Primary_Expressions/11.1.1_The_this_Keyword/S11.1.1_A2-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.4_Array/15.4.4/15.4.4.3_Array_prototype_toLocaleString/S15.4.4.3_A2_T1-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.10_String.prototype.match/S15.5.4.10_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.12_String.prototype.search/S15.5.4.12_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.13_String.prototype.slice/S15.5.4.13_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.14_String.prototype.split/S15.5.4.14_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.15_String.prototype.substring/S15.5.4.15_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.6_String.prototype.concat/S15.5.4.6_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.7_String.prototype.indexOf/S15.5.4.7_A1_T3-expected.txt:
  • sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.8_String.prototype.lastIndexOf/S15.5.4.8_A1_T3-expected.txt:

Updated to show failing results. Firefox and Chrome also fail these
tests, and the ES5 spec seems to mandate failure. Because these tests
resolve a String.prototype function at global scope, the 'this' value
for the call is an environment record. Logically, an environment record
converts to 'undefined' at the call site, and should then fail the
CheckObjectCoercible test.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSScope.cpp

    r153170 r153221  
    4848}
    4949
    50 bool JSScope::isDynamicScope(bool& requiresDynamicChecks) const
     50// Returns true if we found enough information to terminate optimization.
     51static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, size_t depth, bool& needsVarInjectionChecks, ResolveOp& op)
    5152{
    52     switch (structure()->typeInfo().type()) {
    53     case GlobalObjectType:
    54         return static_cast<const JSGlobalObject*>(this)->isDynamicScope(requiresDynamicChecks);
    55     case ActivationObjectType:
    56         return static_cast<const JSActivation*>(this)->isDynamicScope(requiresDynamicChecks);
    57     case NameScopeObjectType:
    58         return static_cast<const JSNameScope*>(this)->isDynamicScope(requiresDynamicChecks);
    59     default:
    60         RELEASE_ASSERT_NOT_REACHED();
    61         break;
     53    if (JSActivation* activation = jsDynamicCast<JSActivation*>(scope)) {
     54        if (ident == exec->propertyNames().arguments) {
     55            // We know the property will be at this activation scope, but we don't know how to cache it.
     56            op = ResolveOp(Dynamic, 0, 0, 0);
     57            return true;
     58        }
     59
     60        SymbolTableEntry entry = activation->symbolTable()->get(ident.impl());
     61        if (entry.isReadOnly() && getOrPut == Put) {
     62            // We know the property will be at this activation scope, but we don't know how to cache it.
     63            op = ResolveOp(Dynamic, 0, 0, 0);
     64            return true;
     65        }
     66
     67        if (!entry.isNull()) {
     68            op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, activation->structure(), entry.getIndex());
     69            return true;
     70        }
     71
     72        if (activation->symbolTable()->usesNonStrictEval())
     73            needsVarInjectionChecks = true;
     74        return false;
    6275    }
    6376
    64     return false;
     77    if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(scope)) {
     78        SymbolTableEntry entry = globalObject->symbolTable()->get(ident.impl());
     79        if (!entry.isNull()) {
     80            if (getOrPut == Put) {
     81                if (entry.isReadOnly()) {
     82                    // We know the property will be at global scope, but we don't know how to cache it.
     83                    op = ResolveOp(Dynamic, 0, 0, 0);
     84                    return true;
     85                }
     86
     87                // It's likely that we'll write to this var, so notify now and avoid the overhead of doing so at runtime.
     88                entry.notifyWrite();
     89            }
     90
     91            op = ResolveOp(makeType(GlobalVar, needsVarInjectionChecks), depth, globalObject->structure(),
     92                reinterpret_cast<uintptr_t>(globalObject->registerAt(entry.getIndex()).slot()));
     93            return true;
     94        }
     95
     96        PropertySlot slot(globalObject);
     97        if (!globalObject->getOwnPropertySlot(globalObject, exec, ident, slot)
     98            || !slot.isCacheableValue()
     99            || !globalObject->structure()->propertyAccessesAreCacheable()
     100            || (globalObject->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && getOrPut == Put)) {
     101            // We know the property will be at global scope, but we don't know how to cache it.
     102            ASSERT(!scope->next());
     103            op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, 0, 0);
     104            return true;
     105        }
     106
     107        op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, globalObject->structure(), slot.cachedOffset());
     108        return true;
     109    }
     110
     111    op = ResolveOp(Dynamic, 0, 0, 0);
     112    return true;
    65113}
    66114
     
    74122}
    75123
    76 int JSScope::localDepth()
     124int JSScope::depth()
    77125{
    78     int scopeDepth = 0;
    79     ScopeChainIterator iter = this->begin();
    80     ScopeChainIterator end = this->end();
    81     while (!iter->inherits(&JSActivation::s_info)) {
    82         ++iter;
    83         if (iter == end)
    84             break;
    85         ++scopeDepth;
    86     }
    87     return scopeDepth;
     126    int depth = 0;
     127    for (JSScope* scope = this; scope; scope = scope->next())
     128        ++depth;
     129    return depth;
    88130}
    89131
    90 struct LookupResult {
    91     JSValue base() const { return m_base; }
    92     JSValue value() const { return m_value; }
    93     void setBase(JSValue base) { ASSERT(base); m_base = base; }
    94     void setValue(JSValue value) { ASSERT(value); m_value = value; }
     132JSValue JSScope::resolve(ExecState* exec, JSScope* scope, const Identifier& ident)
     133{
     134    ScopeChainIterator end = scope->end();
     135    ScopeChainIterator it = scope->begin();
     136    while (1) {
     137        JSObject* object = it.get();
    95138
    96 private:
    97     JSValue m_base;
    98     JSValue m_value;
    99 };
     139        if (++it == end) // Global scope.
     140            return object;
    100141
    101 
    102 static void setPutPropertyAccessOffset(PutToBaseOperation* operation, PropertyOffset offset)
    103 {
    104     ASSERT(isOutOfLineOffset(offset));
    105     operation->m_offset = offset;
    106     operation->m_offsetInButterfly = offsetInButterfly(offset);
    107 }
    108 
    109 static bool executeResolveOperations(CallFrame* callFrame, JSScope* scope, const Identifier& propertyName, ResolveOperation* pc, LookupResult& result)
    110 {
    111     while (true) {
    112         switch (pc->m_operation) {
    113         case ResolveOperation::Fail:
    114             return false;
    115         case ResolveOperation::CheckForDynamicEntriesBeforeGlobalScope: {
    116             while (JSScope* nextScope = scope->next()) {
    117                 if (scope->isActivationObject() && scope->structure() != scope->globalObject()->activationStructure())
    118                     return false;
    119                 ASSERT(scope->isNameScopeObject() || scope->isVariableObject() || scope->isGlobalObject());
    120                 scope = nextScope;
    121             }
    122             pc++;
    123             break;
    124         }
    125         case ResolveOperation::SetBaseToUndefined:
    126             result.setBase(jsUndefined());
    127             pc++;
    128             continue;
    129         case ResolveOperation::SetBaseToScope:
    130             result.setBase(scope);
    131             pc++;
    132             continue;
    133         case ResolveOperation::ReturnScopeAsBase:
    134             result.setBase(scope);
    135             return true;
    136         case ResolveOperation::SetBaseToGlobal:
    137             result.setBase(scope->globalObject());
    138             pc++;
    139             continue;
    140         case ResolveOperation::SkipScopes: {
    141             int count = pc->m_scopesToSkip;
    142             while (count--)
    143                 scope = scope->next();
    144             ASSERT(scope);
    145             pc++;
    146             continue;
    147         }
    148         case ResolveOperation::SkipTopScopeNode:
    149             if (callFrame->r(pc->m_activationRegister).jsValue())
    150                 scope = scope->next();
    151             ASSERT(scope);
    152             pc++;
    153             continue;
    154         case ResolveOperation::GetAndReturnScopedVar:
    155             ASSERT(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
    156             result.setValue(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
    157             return true;
    158         case ResolveOperation::GetAndReturnGlobalVar:
    159             result.setValue(pc->m_registerAddress->get());
    160             return true;
    161         case ResolveOperation::GetAndReturnGlobalVarWatchable:
    162             result.setValue(pc->m_registerAddress->get());
    163             return true;
    164         case ResolveOperation::ReturnGlobalObjectAsBase:
    165             result.setBase(callFrame->lexicalGlobalObject());
    166             return true;
    167         case ResolveOperation::GetAndReturnGlobalProperty: {
    168             JSGlobalObject* globalObject = scope->globalObject();
    169             if (globalObject->structure() == pc->m_structure.get()) {
    170                 result.setValue(globalObject->getDirect(pc->m_offset));
    171                 return true;
    172             }
    173 
    174             PropertySlot slot(globalObject);
    175             if (!globalObject->getPropertySlot(callFrame, propertyName, slot))
    176                 return false;
    177 
    178             JSValue value = slot.getValue(callFrame, propertyName);
    179             if (callFrame->hadException())
    180                 return false;
    181 
    182             Structure* structure = globalObject->structure();
    183 
    184             // Don't try to cache prototype lookups
    185             if (globalObject != slot.slotBase() || !slot.isCacheableValue() || !structure->propertyAccessesAreCacheable()) {
    186                 result.setValue(value);
    187                 return true;
    188             }
    189 
    190             pc->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), structure);
    191             pc->m_offset = slot.cachedOffset();
    192             result.setValue(value);
    193             return true;
    194         }
    195         }
     142        if (object->hasProperty(exec, ident))
     143            return object;
    196144    }
    197145}
    198146
    199 template <JSScope::LookupMode mode, JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScopeInternal(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, ResolveOperations* operations, PutToBaseOperation* putToBaseOperation, bool )
     147ResolveOp JSScope::abstractResolve(ExecState* exec, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType)
    200148{
    201     JSScope* scope = callFrame->scope();
    202     ASSERT(scope);
    203     int scopeCount = 0;
    204     bool seenGenericObjectScope = false;
    205     bool requiresDynamicChecks = false;
    206     bool skipTopScopeNode = false;
    207     int activationRegister = 0;
    208     CodeBlock* codeBlock = callFrame->codeBlock();
    209     if (mode == UnknownResolve) {
    210         ASSERT(operations->isEmpty());
    211         if (codeBlock->codeType() == FunctionCode && codeBlock->needsActivation()) {
    212             activationRegister = codeBlock->activationRegister();
    213             JSValue activation = callFrame->r(activationRegister).jsValue();
    214            
    215             // If the activation register doesn't match our actual scope, a dynamic
    216             // scope has been inserted so we shouldn't skip the top scope node.
    217             if (activation == scope) {
    218                 jsCast<JSActivation*>(activation.asCell())->isDynamicScope(requiresDynamicChecks);
    219                 if (!requiresDynamicChecks) {
    220                     ASSERT(jsCast<JSActivation*>(activation.asCell())->symbolTable()->get(identifier.impl()).isNull());
    221                     scope = scope->next();
    222                     ASSERT(scope);
    223                     skipTopScopeNode = true;
    224                 }
    225             } else if (!activation)
    226                 skipTopScopeNode = true;
    227         }
    228     } else
    229         ASSERT(operations->size());
     149    ResolveOp op(Dynamic, 0, 0, 0);
     150    if (unlinkedType == Dynamic)
     151        return op;
    230152
    231     if (codeBlock->codeType() == EvalCode && scope->next())
    232         requiresDynamicChecks = true;
     153    size_t depth = 0;
     154    bool needsVarInjectionChecks = JSC::needsVarInjectionChecks(unlinkedType);
     155    for (; scope; scope = scope->next()) {
     156        if (abstractAccess(exec, scope, ident, getOrPut, depth, needsVarInjectionChecks, op))
     157            break;
     158        ++depth;
     159    }
    233160
    234     if (mode == UnknownResolve && putToBaseOperation)
    235         putToBaseOperation->m_kind = PutToBaseOperation::Generic;
    236 
    237     do {
    238         JSObject* object = JSScope::objectAtScope(scope);
    239         slot = PropertySlot(object);
    240 
    241         bool currentScopeNeedsDynamicChecks = false;
    242         if (!(scope->isVariableObject() || scope->isNameScopeObject()) || (scope->next() && scope->isDynamicScope(currentScopeNeedsDynamicChecks)))
    243             seenGenericObjectScope = true;
    244 
    245         requiresDynamicChecks = requiresDynamicChecks || currentScopeNeedsDynamicChecks;
    246 
    247         if (object->getPropertySlot(callFrame, identifier, slot)) {
    248             if (mode == UnknownResolve) {
    249                 if (seenGenericObjectScope)
    250                     goto fail;
    251                 if (putToBaseOperation)
    252                     putToBaseOperation->m_isDynamic = requiresDynamicChecks;
    253                 if (!scope->next()) {
    254                     // Global lookup of some kind
    255                     JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(scope);
    256                     SymbolTableEntry entry = globalObject->symbolTable()->get(identifier.impl());
    257                     if (!entry.isNull()) {
    258                         if (requiresDynamicChecks)
    259                             operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
    260 
    261                         if (putToBaseOperation) {
    262                             putToBaseOperation->m_isDynamic = requiresDynamicChecks;
    263                             if (entry.isReadOnly())
    264                                 putToBaseOperation->m_kind = PutToBaseOperation::Readonly;
    265                             else if (entry.couldBeWatched()) {
    266                                 putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePutChecked;
    267                                 putToBaseOperation->m_predicatePointer = entry.addressOfIsWatched();
    268                             } else
    269                                 putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePut;
    270                             putToBaseOperation->m_registerAddress = &globalObject->registerAt(entry.getIndex());
    271                         }
    272                         // Override custom accessor behaviour that the DOM introduces for some
    273                         // event handlers declared on function declarations.
    274                         if (!requiresDynamicChecks)
    275                             slot.setValue(globalObject, globalObject->registerAt(entry.getIndex()).get());
    276                         switch (returnValues) {
    277                         case ReturnValue:
    278                             ASSERT(!putToBaseOperation);
    279                             operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
    280                             break;
    281                         case ReturnBase:
    282                             ASSERT(putToBaseOperation);
    283                             operations->append(ResolveOperation::returnGlobalObjectAsBase());
    284                             break;
    285                         case ReturnBaseAndValue:
    286                             ASSERT(putToBaseOperation);
    287                             operations->append(ResolveOperation::setBaseToGlobal());
    288                             operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
    289                             break;
    290                         case ReturnThisAndValue:
    291                             ASSERT(!putToBaseOperation);
    292                             operations->append(ResolveOperation::setBaseToUndefined());
    293                             operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
    294                             break;
    295                         }
    296                     } else {
    297                         if (!slot.isCacheableValue() || slot.slotBase() != globalObject)
    298                             goto fail;
    299 
    300                         if (requiresDynamicChecks)
    301                             operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
    302 
    303                         if (putToBaseOperation) {
    304                             ConcurrentJITLocker locker(callFrame->codeBlock()->m_lock);
    305                            
    306                             putToBaseOperation->m_isDynamic = requiresDynamicChecks;
    307                             putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
    308                             putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), globalObject->structure());
    309                             setPutPropertyAccessOffset(putToBaseOperation, slot.cachedOffset());
    310                         }
    311                         switch (returnValues) {
    312                         case ReturnValue:
    313                             ASSERT(!putToBaseOperation);
    314                             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    315                             break;
    316                         case ReturnBase:
    317                             ASSERT(putToBaseOperation);
    318                             operations->append(ResolveOperation::returnGlobalObjectAsBase());
    319                             break;
    320                         case ReturnBaseAndValue:
    321                             ASSERT(putToBaseOperation);
    322                             operations->append(ResolveOperation::setBaseToGlobal());
    323                             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    324                             break;
    325                         case ReturnThisAndValue:
    326                             ASSERT(!putToBaseOperation);
    327                             operations->append(ResolveOperation::setBaseToUndefined());
    328                             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    329                             break;
    330                         }
    331                     }
    332                     return object;
    333                 }
    334                 if (!requiresDynamicChecks) {
    335                     // Normal lexical lookup
    336                     JSVariableObject* variableObject = jsCast<JSVariableObject*>(scope);
    337                     ASSERT(variableObject);
    338                     ASSERT(variableObject->symbolTable());
    339                     SymbolTableEntry entry = variableObject->symbolTable()->get(identifier.impl());
    340                     // Defend against the variable being actually inserted by eval.
    341                     if (entry.isNull()) {
    342                         ASSERT(!jsDynamicCast<JSNameScope*>(variableObject));
    343                         goto fail;
    344                     }
    345                     // If we're getting the 'arguments' then give up on life.
    346                     if (identifier == callFrame->propertyNames().arguments)
    347                         goto fail;
    348 
    349                     if (putToBaseOperation) {
    350                         ConcurrentJITLocker locker(callFrame->codeBlock()->m_lock);
    351                        
    352                         putToBaseOperation->m_kind = entry.isReadOnly() ? PutToBaseOperation::Readonly : PutToBaseOperation::VariablePut;
    353                         putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), callFrame->lexicalGlobalObject()->activationStructure());
    354                         putToBaseOperation->m_offset = entry.getIndex();
    355                         putToBaseOperation->m_scopeDepth = (skipTopScopeNode ? 1 : 0) + scopeCount;
    356                     }
    357 
    358                     if (skipTopScopeNode)
    359                         operations->append(ResolveOperation::skipTopScopeNode(activationRegister));
    360 
    361                     operations->append(ResolveOperation::skipScopes(scopeCount));
    362                     switch (returnValues) {
    363                     case ReturnBaseAndValue:
    364                         operations->append(ResolveOperation::setBaseToScope());
    365                         operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
    366                         break;
    367 
    368                     case ReturnBase:
    369                         operations->append(ResolveOperation::returnScopeAsBase());
    370                         break;
    371 
    372                     case ReturnThisAndValue:
    373                         operations->append(ResolveOperation::setBaseToUndefined());
    374                         // fallthrough
    375                     case ReturnValue:
    376                         operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
    377                         break;
    378                     }
    379                     return object;
    380                 }
    381             fail:
    382                 if (!operations->size())
    383                     operations->append(ResolveOperation::resolveFail());
    384             }
    385             return object;
    386         }
    387         scopeCount++;
    388     } while ((scope = scope->next()));
    389    
    390     if (mode == UnknownResolve) {
    391         ASSERT(operations->isEmpty());
    392         if (seenGenericObjectScope) {
    393             operations->append(ResolveOperation::resolveFail());
    394             return 0;
    395         }
    396         if (putToBaseOperation) {
    397             putToBaseOperation->m_isDynamic = requiresDynamicChecks;
    398             putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
    399             putToBaseOperation->m_structure.clear();
    400             putToBaseOperation->m_offset = -1;
    401         }
    402         if (requiresDynamicChecks)
    403             operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
    404         switch (returnValues) {
    405         case ReturnValue:
    406             ASSERT(!putToBaseOperation);
    407             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    408             break;
    409         case ReturnBase:
    410             ASSERT(putToBaseOperation);
    411             operations->append(ResolveOperation::returnGlobalObjectAsBase());
    412             break;
    413         case ReturnBaseAndValue:
    414             ASSERT(putToBaseOperation);
    415             operations->append(ResolveOperation::setBaseToGlobal());
    416             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    417             break;
    418         case ReturnThisAndValue:
    419             ASSERT(!putToBaseOperation);
    420             operations->append(ResolveOperation::setBaseToUndefined());
    421             operations->append(ResolveOperation::getAndReturnGlobalProperty());
    422             break;
    423         }
    424     }
    425     return 0;
     161    return op;
    426162}
    427163
    428 template <JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScope(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, ResolveOperations* operations, PutToBaseOperation* putToBaseOperation, bool isStrict)
    429 {
    430     if (operations->size())
    431         return resolveContainingScopeInternal<KnownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
    432     JSObject* result = resolveContainingScopeInternal<UnknownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
    433     operations->shrinkToFit();
    434     return result;
    435 }
    436 
    437 JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier, ResolveOperations* operations)
    438 {
    439     ASSERT(operations);
    440     LookupResult fastResult;
    441     if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
    442         ASSERT(fastResult.value());
    443         ASSERT(!callFrame->hadException());
    444         return fastResult.value();
    445     }
    446 
    447     if (callFrame->hadException())
    448         return JSValue();
    449 
    450     PropertySlot slot;
    451     if (JSScope::resolveContainingScope<ReturnValue>(callFrame, identifier, slot, operations, 0, false)) {
    452         ASSERT(operations->size());
    453         return slot.getValue(callFrame, identifier);
    454     }
    455     ASSERT(operations->size());
    456 
    457     return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
    458 }
    459 
    460 JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
    461 {
    462     ASSERT(operations);
    463     ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
    464     LookupResult fastResult;
    465     if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
    466         ASSERT(fastResult.base());
    467         ASSERT(!callFrame->hadException());
    468         return fastResult.base();
    469     }
    470 
    471     if (callFrame->hadException())
    472         return JSValue();
    473 
    474     PropertySlot slot;
    475     if (JSObject* base = JSScope::resolveContainingScope<ReturnBase>(callFrame, identifier, slot, operations, putToBaseOperations, isStrict)) {
    476         ASSERT(operations->size());
    477         return base;
    478     }
    479 
    480     if (!isStrict)
    481         return callFrame->lexicalGlobalObject();
    482 
    483     return throwError(callFrame, createErrorForInvalidGlobalAssignment(callFrame, identifier.string()));
    484 }
    485 
    486 JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
    487 {
    488     ASSERT(operations);
    489     ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
    490     LookupResult fastResult;
    491     if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
    492         ASSERT(fastResult.base());
    493         ASSERT(fastResult.value());
    494         ASSERT(!callFrame->hadException());
    495         *base = fastResult.base();
    496         return fastResult.value();
    497     }
    498 
    499     if (callFrame->hadException())
    500         return JSValue();
    501 
    502     PropertySlot slot;
    503     if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnBaseAndValue>(callFrame, identifier, slot, operations, putToBaseOperations, false)) {
    504         ASSERT(operations->size());
    505         JSValue value = slot.getValue(callFrame, identifier);
    506         if (callFrame->vm().exception)
    507             return JSValue();
    508 
    509         *base = propertyBase;
    510         return value;
    511     }
    512     ASSERT(operations->size());
    513 
    514     return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
    515 }
    516 
    517 JSValue JSScope::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations)
    518 {
    519     ASSERT(operations);
    520     LookupResult fastResult;
    521     if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
    522         ASSERT(fastResult.base());
    523         ASSERT(fastResult.value());
    524         ASSERT(!callFrame->hadException());
    525         *base = fastResult.base();
    526         return fastResult.value();
    527     }
    528 
    529     if (callFrame->hadException())
    530         return JSValue();
    531 
    532     PropertySlot slot;
    533     if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnThisAndValue>(callFrame, identifier, slot, operations, 0, false)) {
    534         ASSERT(operations->size());
    535         JSValue value = slot.getValue(callFrame, identifier);
    536         if (callFrame->vm().exception)
    537             return JSValue();
    538         ASSERT(value);
    539         *base = JSValue(propertyBase);
    540         return value;
    541     }
    542     ASSERT(operations->size());
    543 
    544     return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
    545 }
    546 
    547 void JSScope::resolvePut(CallFrame* callFrame, JSValue base, const Identifier& property, JSValue value, PutToBaseOperation* operation)
    548 {
    549     ASSERT_UNUSED(operation, operation);
    550     ASSERT(base);
    551     ASSERT(value);
    552     switch (operation->m_kind) {
    553     case PutToBaseOperation::Uninitialised:
    554         CRASH();
    555 
    556     case PutToBaseOperation::Readonly:
    557         return;
    558 
    559     case PutToBaseOperation::GlobalVariablePutChecked:
    560         if (*operation->m_predicatePointer)
    561             goto genericHandler;
    562     case PutToBaseOperation::GlobalVariablePut:
    563         if (operation->m_isDynamic) {
    564             JSObject* baseObject = jsCast<JSObject*>(base);
    565             if (baseObject != callFrame->lexicalGlobalObject()) {
    566                 if (baseObject->isGlobalObject())
    567                     ASSERT(!jsCast<JSGlobalObject*>(baseObject)->assertRegisterIsInThisObject(operation->m_registerAddress));
    568                 goto genericHandler;
    569             }
    570         }
    571         operation->m_registerAddress->set(callFrame->vm(), base.asCell(), value);
    572         return;
    573 
    574     case PutToBaseOperation::VariablePut: {
    575         if (operation->m_isDynamic) {
    576             JSObject* baseObject = jsCast<JSObject*>(base);
    577             if (baseObject->structure() != operation->m_structure.get())
    578                 goto genericHandler;
    579         }
    580         JSVariableObject* variableObject = jsCast<JSVariableObject*>(base);
    581         variableObject->registerAt(operation->m_offset).set(callFrame->vm(), variableObject, value);
    582         return;
    583     }
    584 
    585     case PutToBaseOperation::GlobalPropertyPut: {
    586         JSObject* object = jsCast<JSObject*>(base);
    587         if (operation->m_structure.get() != object->structure())
    588             break;
    589         object->putDirect(callFrame->vm(), operation->m_offset, value);
    590         return;
    591     }
    592 
    593     genericHandler:
    594     case PutToBaseOperation::Generic:
    595         PutPropertySlot slot(operation->m_isStrict);
    596         base.put(callFrame, property, value, slot);
    597         return;
    598     }
    599     ASSERT(operation->m_kind == PutToBaseOperation::GlobalPropertyPut);
    600     PutPropertySlot slot(operation->m_isStrict);
    601     base.put(callFrame, property, value, slot);
    602     if (!slot.isCacheable())
    603         return;
    604     if (callFrame->hadException())
    605         return;
    606     JSObject* baseObject = jsCast<JSObject*>(base);
    607     if (!baseObject->structure()->propertyAccessesAreCacheable())
    608         return;
    609     if (slot.base() != callFrame->lexicalGlobalObject())
    610         return;
    611     if (slot.base() != baseObject)
    612         return;
    613     ASSERT(!baseObject->hasInlineStorage());
    614     ConcurrentJITLocker locker(callFrame->codeBlock()->m_lock);
    615    
    616     operation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), baseObject->structure());
    617     setPutPropertyAccessOffset(operation, slot.cachedOffset());
    618     return;
    619 }
    620 
    621 JSValue JSScope::resolveGlobal(CallFrame* callFrame, const Identifier& identifier, JSGlobalObject* globalObject, ResolveOperation* resolveOperation)
    622 {
    623     ASSERT(resolveOperation);
    624     ASSERT(resolveOperation->m_operation == ResolveOperation::GetAndReturnGlobalProperty);
    625     ASSERT_UNUSED(globalObject, callFrame->lexicalGlobalObject() == globalObject);
    626 
    627     LookupResult fastResult;
    628     if (executeResolveOperations(callFrame, callFrame->scope(), identifier, resolveOperation, fastResult)) {
    629         ASSERT(fastResult.value());
    630         ASSERT(!callFrame->hadException());
    631         return fastResult.value();
    632     }
    633 
    634     if (callFrame->hadException())
    635         return JSValue();
    636 
    637     return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
    638 }
    639 
    640 
    641164} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.