[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.15.1-1414-gc69ee75

ggaren at apple.com ggaren at apple.com
Thu Oct 29 20:46:32 UTC 2009


The following commit has been merged in the webkit-1.1 branch:
commit 3806e0fa98a4297fabf18c16f70c34a851dadc72
Author: ggaren at apple.com <ggaren at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Sat Oct 17 00:28:19 2009 +0000

    Fast for-in enumeration: Cache JSPropertyNameIterator; cache JSStrings
    in JSPropertyNameIterator; inline more code.
    
    Patch by Geoffrey Garen <ggaren at apple.com> on 2009-10-16
    Reviewed by Oliver Hunt.
    
    1.024x as fast on SunSpider (fasta: 1.43x as fast).
    
    * bytecode/CodeBlock.cpp:
    (JSC::CodeBlock::dump):
    * bytecode/Opcode.h:
    * bytecompiler/BytecodeGenerator.cpp:
    (JSC::BytecodeGenerator::emitGetPropertyNames):
    (JSC::BytecodeGenerator::emitNextPropertyName):
    * bytecompiler/BytecodeGenerator.h: Added a few extra operands to
    op_get_pnames and op_next_pname so that we can track iteration state
    in the register file instead of in the JSPropertyNameIterator. (To be
    cacheable, the JSPropertyNameIterator must be stateless.)
    
    * interpreter/Interpreter.cpp:
    (JSC::Interpreter::tryCachePutByID):
    (JSC::Interpreter::tryCacheGetByID): Updated for rename to
    "normalizePrototypeChain" and removal of "isCacheable".
    
    (JSC::Interpreter::privateExecute): Updated for in-RegisterFile
    iteration state tracking.
    
    * jit/JIT.cpp:
    (JSC::JIT::privateCompileMainPass):
    * jit/JIT.h:
    * jit/JITOpcodes.cpp:
    (JSC::JIT::emit_op_get_pnames): Updated for in-RegisterFile
    iteration state tracking.
    
    (JSC::JIT::emit_op_next_pname): Inlined code generation for op_next_pname.
    
    * jit/JITStubs.cpp:
    (JSC::JITThunks::tryCachePutByID):
    (JSC::JITThunks::tryCacheGetByID): Updated for rename to
    "normalizePrototypeChain" and removal of "isCacheable".
    
    (JSC::DEFINE_STUB_FUNCTION):
    * jit/JITStubs.h:
    (JSC::): Added has_property and to_object stubs. Removed op_next_pname
    stub, since has_property is all we need anymore.
    
    * parser/Nodes.cpp:
    (JSC::ForInNode::emitBytecode): Updated for in-RegisterFile
    iteration state tracking.
    
    * runtime/JSCell.h:
    * runtime/JSObject.cpp:
    (JSC::JSObject::getPropertyNames): Don't do caching at this layer
    anymore, since we don't create a JSPropertyNameIterator at this layer.
    
    * runtime/JSPropertyNameIterator.cpp:
    (JSC::JSPropertyNameIterator::create): Do do caching at this layer.
    (JSC::JSPropertyNameIterator::get):  Updated for in-RegisterFile
    iteration state tracking.
    (JSC::JSPropertyNameIterator::markChildren): Mark our JSStrings.
    
    * runtime/JSPropertyNameIterator.h:
    (JSC::JSPropertyNameIterator::size):
    (JSC::JSPropertyNameIterator::setCachedStructure):
    (JSC::JSPropertyNameIterator::cachedStructure):
    (JSC::JSPropertyNameIterator::setCachedPrototypeChain):
    (JSC::JSPropertyNameIterator::cachedPrototypeChain):
    (JSC::JSPropertyNameIterator::JSPropertyNameIterator):
    (JSC::Structure::setEnumerationCache): Don't store iteration state in
    a JSPropertyNameIterator. Do cache a JSPropertyNameIterator in a
    Structure.
    
    * runtime/JSValue.h:
    (JSC::asCell):
    * runtime/MarkStack.h: Make those mischievous #include gods happy.
    
    * runtime/ObjectConstructor.cpp:
    
    * runtime/Operations.h:
    (JSC::normalizePrototypeChain): Renamed countPrototypeChainEntriesAndCheckForProxies
    to normalizePrototypeChain, since it changes dictionary prototypes to
    non-dictionary objects.
    
    * runtime/PropertyNameArray.cpp:
    (JSC::PropertyNameArray::add):
    * runtime/PropertyNameArray.h:
    (JSC::PropertyNameArrayData::PropertyNameArrayData):
    (JSC::PropertyNameArray::data):
    (JSC::PropertyNameArray::size):
    (JSC::PropertyNameArray::begin):
    (JSC::PropertyNameArray::end): Simplified some code here to help with
    current and future refactoring.
    
    * runtime/Protect.h:
    * runtime/Structure.cpp:
    (JSC::Structure::~Structure):
    (JSC::Structure::addPropertyWithoutTransition):
    (JSC::Structure::removePropertyWithoutTransition): No need to clear
    the enumeration cache with adding / removing properties without
    transition. It is an error to add / remove properties without transition
    once an object has been observed, and we can ASSERT to catch that.
    
    * runtime/Structure.h:
    (JSC::Structure::enumerationCache): Changed the enumeration cache to
    hold a JSPropertyNameIterator.
    
    * runtime/StructureChain.cpp:
    * runtime/StructureChain.h:
    (JSC::StructureChain::head): Removed StructureChain::isCacheable because
    it was wrong-headed in two ways: (1) It gave up when a prototype was a
    dictionary, but instead we want un-dictionary heavily accessed
    prototypes; (2) It folded a test for hasDefaultGetPropertyNames() into
    a generic test for "cacheable-ness", but hasDefaultGetPropertyNames()
    is only releavant to for-in caching.
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@49717 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index c30a633..32ce1b2 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,119 @@
+2009-10-16  Geoffrey Garen  <ggaren at apple.com>
+
+        Reviewed by Oliver Hunt.
+        
+        Fast for-in enumeration: Cache JSPropertyNameIterator; cache JSStrings
+        in JSPropertyNameIterator; inline more code.
+
+        1.024x as fast on SunSpider (fasta: 1.43x as fast).
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dump):
+        * bytecode/Opcode.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitGetPropertyNames):
+        (JSC::BytecodeGenerator::emitNextPropertyName):
+        * bytecompiler/BytecodeGenerator.h: Added a few extra operands to
+        op_get_pnames and op_next_pname so that we can track iteration state
+        in the register file instead of in the JSPropertyNameIterator. (To be
+        cacheable, the JSPropertyNameIterator must be stateless.)
+
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::tryCachePutByID):
+        (JSC::Interpreter::tryCacheGetByID): Updated for rename to
+        "normalizePrototypeChain" and removal of "isCacheable". 
+
+        (JSC::Interpreter::privateExecute): Updated for in-RegisterFile
+        iteration state tracking.
+
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_get_pnames): Updated for in-RegisterFile
+        iteration state tracking.
+
+        (JSC::JIT::emit_op_next_pname): Inlined code generation for op_next_pname.
+
+        * jit/JITStubs.cpp:
+        (JSC::JITThunks::tryCachePutByID):
+        (JSC::JITThunks::tryCacheGetByID): Updated for rename to
+        "normalizePrototypeChain" and removal of "isCacheable". 
+
+        (JSC::DEFINE_STUB_FUNCTION):
+        * jit/JITStubs.h:
+        (JSC::): Added has_property and to_object stubs. Removed op_next_pname
+        stub, since has_property is all we need anymore.
+
+        * parser/Nodes.cpp:
+        (JSC::ForInNode::emitBytecode): Updated for in-RegisterFile
+        iteration state tracking.
+
+        * runtime/JSCell.h:
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::getPropertyNames): Don't do caching at this layer
+        anymore, since we don't create a JSPropertyNameIterator at this layer.
+
+        * runtime/JSPropertyNameIterator.cpp:
+        (JSC::JSPropertyNameIterator::create): Do do caching at this layer.
+        (JSC::JSPropertyNameIterator::get):  Updated for in-RegisterFile
+        iteration state tracking.
+        (JSC::JSPropertyNameIterator::markChildren): Mark our JSStrings.
+
+        * runtime/JSPropertyNameIterator.h:
+        (JSC::JSPropertyNameIterator::size):
+        (JSC::JSPropertyNameIterator::setCachedStructure):
+        (JSC::JSPropertyNameIterator::cachedStructure):
+        (JSC::JSPropertyNameIterator::setCachedPrototypeChain):
+        (JSC::JSPropertyNameIterator::cachedPrototypeChain):
+        (JSC::JSPropertyNameIterator::JSPropertyNameIterator):
+        (JSC::Structure::setEnumerationCache): Don't store iteration state in
+        a JSPropertyNameIterator. Do cache a JSPropertyNameIterator in a
+        Structure.
+
+        * runtime/JSValue.h:
+        (JSC::asCell):
+        * runtime/MarkStack.h: Make those mischievous #include gods happy.
+
+        * runtime/ObjectConstructor.cpp:
+
+        * runtime/Operations.h:
+        (JSC::normalizePrototypeChain): Renamed countPrototypeChainEntriesAndCheckForProxies
+        to normalizePrototypeChain, since it changes dictionary prototypes to
+        non-dictionary objects.
+
+        * runtime/PropertyNameArray.cpp:
+        (JSC::PropertyNameArray::add):
+        * runtime/PropertyNameArray.h:
+        (JSC::PropertyNameArrayData::PropertyNameArrayData):
+        (JSC::PropertyNameArray::data):
+        (JSC::PropertyNameArray::size):
+        (JSC::PropertyNameArray::begin):
+        (JSC::PropertyNameArray::end): Simplified some code here to help with
+        current and future refactoring.
+
+        * runtime/Protect.h:
+        * runtime/Structure.cpp:
+        (JSC::Structure::~Structure):
+        (JSC::Structure::addPropertyWithoutTransition):
+        (JSC::Structure::removePropertyWithoutTransition): No need to clear
+        the enumeration cache with adding / removing properties without
+        transition. It is an error to add / remove properties without transition
+        once an object has been observed, and we can ASSERT to catch that.
+
+        * runtime/Structure.h:
+        (JSC::Structure::enumerationCache): Changed the enumeration cache to
+        hold a JSPropertyNameIterator.
+
+        * runtime/StructureChain.cpp:
+        * runtime/StructureChain.h:
+        (JSC::StructureChain::head): Removed StructureChain::isCacheable because
+        it was wrong-headed in two ways: (1) It gave up when a prototype was a
+        dictionary, but instead we want un-dictionary heavily accessed
+        prototypes; (2) It folded a test for hasDefaultGetPropertyNames() into
+        a generic test for "cacheable-ness", but hasDefaultGetPropertyNames()
+        is only releavant to for-in caching.
+
 2009-10-16  Steve Falkenburg  <sfalken at apple.com>
 
         Reviewed by Adam Roben.
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp
index ef0054b..18ca2ae 100644
--- a/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1015,16 +1015,18 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             break;
         }
         case op_get_pnames: {
-            int r0 = (++it)->u.operand;
-            int r1 = (++it)->u.operand;
+            int r0 = it[0].u.operand;
+            int r1 = it[1].u.operand;
             printf("[%4d] get_pnames\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str());
+            it += OPCODE_LENGTH(op_get_pnames) - 1;
             break;
         }
         case op_next_pname: {
-            int dest = (++it)->u.operand;
-            int iter = (++it)->u.operand;
-            int offset = (++it)->u.operand;
+            int dest = it[0].u.operand;
+            int iter = it[4].u.operand;
+            int offset = it[5].u.operand;
             printf("[%4d] next_pname\t %s, %s, %d(->%d)\n", location, registerName(dest).c_str(), registerName(iter).c_str(), offset, location + offset);
+            it += OPCODE_LENGTH(op_next_pname) - 1;
             break;
         }
         case op_push_scope: {
diff --git a/JavaScriptCore/bytecode/Opcode.h b/JavaScriptCore/bytecode/Opcode.h
index c9196ce..8968252 100644
--- a/JavaScriptCore/bytecode/Opcode.h
+++ b/JavaScriptCore/bytecode/Opcode.h
@@ -152,8 +152,8 @@ namespace JSC {
         macro(op_strcat, 4) \
         macro(op_to_primitive, 3) \
         \
-        macro(op_get_pnames, 3) \
-        macro(op_next_pname, 4) \
+        macro(op_get_pnames, 6) \
+        macro(op_next_pname, 7) \
         \
         macro(op_push_scope, 2) \
         macro(op_pop_scope, 1) \
diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 780a52e..41b5c39 100644
--- a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -1794,12 +1794,28 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpScopes(Label* target, int targetSco
     return target;
 }
 
-RegisterID* BytecodeGenerator::emitNextPropertyName(RegisterID* dst, RegisterID* iter, Label* target)
+RegisterID* BytecodeGenerator::emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget)
+{
+    size_t begin = instructions().size();
+
+    emitOpcode(op_get_pnames);
+    instructions().append(dst->index());
+    instructions().append(base->index());
+    instructions().append(i->index());
+    instructions().append(size->index());
+    instructions().append(breakTarget->bind(begin, instructions().size()));
+    return dst;
+}
+
+RegisterID* BytecodeGenerator::emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target)
 {
     size_t begin = instructions().size();
 
     emitOpcode(op_next_pname);
     instructions().append(dst->index());
+    instructions().append(base->index());
+    instructions().append(i->index());
+    instructions().append(size->index());
     instructions().append(iter->index());
     instructions().append(target->bind(begin, instructions().size()));
     return dst;
diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 1a83ce9..61de173 100644
--- a/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -312,8 +312,8 @@ namespace JSC {
         PassRefPtr<Label> emitJumpSubroutine(RegisterID* retAddrDst, Label*);
         void emitSubroutineReturn(RegisterID* retAddrSrc);
 
-        RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base) { return emitUnaryOp(op_get_pnames, dst, base); }
-        RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* iter, Label* target);
+        RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget);
+        RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target);
 
         RegisterID* emitCatch(RegisterID*, Label* start, Label* end);
         void emitThrow(RegisterID* exc) { emitUnaryNoDstOp(op_throw, exc); }
diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp
index 74b4eb1..53964ad 100644
--- a/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/JavaScriptCore/interpreter/Interpreter.cpp
@@ -938,22 +938,20 @@ NEVER_INLINE void Interpreter::tryCachePutByID(CallFrame* callFrame, CodeBlock*
         return;
     }
 
-    StructureChain* protoChain = structure->prototypeChain(callFrame);
-    if (!protoChain->isCacheable()) {
-        vPC[0] = getOpcode(op_put_by_id_generic);
-        return;
-    }
-
     // Structure transition, cache transition info
     if (slot.type() == PutPropertySlot::NewProperty) {
         if (structure->isDictionary()) {
             vPC[0] = getOpcode(op_put_by_id_generic);
             return;
         }
+
+        // put_by_id_transition checks the prototype chain for setters.
+        normalizePrototypeChain(callFrame, baseCell);
+
         vPC[0] = getOpcode(op_put_by_id_transition);
         vPC[4] = structure->previousID();
         vPC[5] = structure;
-        vPC[6] = protoChain;
+        vPC[6] = structure->prototypeChain(callFrame);
         vPC[7] = slot.cachedOffset();
         codeBlock->refStructures(vPC);
         return;
@@ -1049,21 +1047,15 @@ NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock*
         return;
     }
 
-    size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
+    size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase());
     if (!count) {
         vPC[0] = getOpcode(op_get_by_id_generic);
         return;
     }
 
-    StructureChain* protoChain = structure->prototypeChain(callFrame);
-    if (!protoChain->isCacheable()) {
-        vPC[0] = getOpcode(op_get_by_id_generic);
-        return;
-    }
-
     vPC[0] = getOpcode(op_get_by_id_chain);
     vPC[4] = structure;
-    vPC[5] = protoChain;
+    vPC[5] = structure->prototypeChain(callFrame);
     vPC[6] = count;
     vPC[7] = slot.cachedOffset();
     codeBlock->refStructures(vPC);
@@ -3502,41 +3494,63 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
         NEXT_INSTRUCTION();
     }
     DEFINE_OPCODE(op_get_pnames) {
-        /* get_pnames dst(r) base(r)
+        /* get_pnames dst(r) base(r) i(n) size(n) breakTarget(offset)
 
            Creates a property name list for register base and puts it
-           in register dst. This is not a true JavaScript value, just
-           a synthetic value used to keep the iteration state in a
-           register.
+           in register dst, initializing i and size for iteration. If
+           base is undefined or null, jumps to breakTarget.
         */
         int dst = vPC[1].u.operand;
         int base = vPC[2].u.operand;
+        int i = vPC[3].u.operand;
+        int size = vPC[4].u.operand;
+        int breakTarget = vPC[5].u.operand;
 
-        callFrame->r(dst) = JSPropertyNameIterator::create(callFrame, callFrame->r(base).jsValue());
+        JSValue v = callFrame->r(base).jsValue();
+        if (v.isUndefinedOrNull()) {
+            vPC += breakTarget;
+            NEXT_INSTRUCTION();
+        }
+
+        JSObject* o = v.toObject(callFrame);
+        Structure* structure = o->structure();
+        JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache();
+        if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(callFrame))
+            jsPropertyNameIterator = JSPropertyNameIterator::create(callFrame, o);
+
+        callFrame->r(dst) = jsPropertyNameIterator;
+        callFrame->r(base) = JSValue(o);
+        callFrame->r(i) = Register::withInt(0);
+        callFrame->r(size) = Register::withInt(jsPropertyNameIterator->size());
         vPC += OPCODE_LENGTH(op_get_pnames);
         NEXT_INSTRUCTION();
     }
     DEFINE_OPCODE(op_next_pname) {
-        /* next_pname dst(r) iter(r) target(offset)
+        /* next_pname dst(r) base(r) i(n) size(n) iter(r) target(offset)
 
-           Tries to copies the next name from property name list in
-           register iter. If there are names left, then copies one to
-           register dst, and jumps to offset target. If there are none
-           left, invalidates the iterator and continues to the next
+           Copies the next name from the property name list in
+           register iter to dst, then jumps to offset target. If there are no
+           names left, invalidates the iterator and continues to the next
            instruction.
         */
         int dst = vPC[1].u.operand;
-        int iter = vPC[2].u.operand;
-        int target = vPC[3].u.operand;
+        int base = vPC[2].u.operand;
+        int i = vPC[3].u.operand;
+        int size = vPC[4].u.operand;
+        int iter = vPC[5].u.operand;
+        int target = vPC[6].u.operand;
 
         JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator();
-        if (JSValue temp = it->next(callFrame)) {
-            CHECK_FOR_TIMEOUT();
-            callFrame->r(dst) = JSValue(temp);
-            vPC += target;
-            NEXT_INSTRUCTION();
+        while (callFrame->r(i).i() != callFrame->r(size).i()) {
+            JSValue key = it->get(callFrame, asObject(callFrame->r(base).jsValue()), callFrame->r(i).i());
+            callFrame->r(i) = Register::withInt(callFrame->r(i).i() + 1);
+            if (key) {
+                CHECK_FOR_TIMEOUT();
+                callFrame->r(dst) = key;
+                vPC += target;
+                NEXT_INSTRUCTION();
+            }
         }
-        it->invalidate();
 
         vPC += OPCODE_LENGTH(op_next_pname);
         NEXT_INSTRUCTION();
diff --git a/JavaScriptCore/jit/JIT.cpp b/JavaScriptCore/jit/JIT.cpp
index 5cead1d..fa0ac2e 100644
--- a/JavaScriptCore/jit/JIT.cpp
+++ b/JavaScriptCore/jit/JIT.cpp
@@ -202,7 +202,6 @@ void JIT::privateCompileMainPass()
         DEFINE_BINARY_OP(op_less)
         DEFINE_BINARY_OP(op_lesseq)
         DEFINE_BINARY_OP(op_urshift)
-        DEFINE_UNARY_OP(op_get_pnames)
         DEFINE_UNARY_OP(op_is_boolean)
         DEFINE_UNARY_OP(op_is_function)
         DEFINE_UNARY_OP(op_is_number)
@@ -241,6 +240,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_val)
         DEFINE_OP(op_get_global_var)
+        DEFINE_OP(op_get_pnames)
         DEFINE_OP(op_get_scoped_var)
         DEFINE_OP(op_instanceof)
         DEFINE_OP(op_jeq_null)
diff --git a/JavaScriptCore/jit/JIT.h b/JavaScriptCore/jit/JIT.h
index 0712743..23e8883 100644
--- a/JavaScriptCore/jit/JIT.h
+++ b/JavaScriptCore/jit/JIT.h
@@ -713,6 +713,7 @@ namespace JSC {
         void emit_op_new_func_exp(Instruction*);
         void emit_op_new_object(Instruction*);
         void emit_op_new_regexp(Instruction*);
+        void emit_op_get_pnames(Instruction*);
         void emit_op_next_pname(Instruction*);
         void emit_op_not(Instruction*);
         void emit_op_nstricteq(Instruction*);
diff --git a/JavaScriptCore/jit/JITOpcodes.cpp b/JavaScriptCore/jit/JITOpcodes.cpp
index c793f90..59bc403 100644
--- a/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/JavaScriptCore/jit/JITOpcodes.cpp
@@ -33,6 +33,7 @@
 #include "JSArray.h"
 #include "JSCell.h"
 #include "JSFunction.h"
+#include "JSPropertyNameIterator.h"
 #include "LinkBuffer.h"
 
 namespace JSC {
@@ -1195,23 +1196,109 @@ void JIT::emit_op_throw(Instruction* currentInstruction)
 #endif
 }
 
+void JIT::emit_op_get_pnames(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int base = currentInstruction[2].u.operand;
+    int i = currentInstruction[3].u.operand;
+    int size = currentInstruction[4].u.operand;
+    int breakTarget = currentInstruction[5].u.operand;
+
+    JumpList isNotObject;
+
+    emitLoad(base, regT1, regT0);
+    if (!m_codeBlock->isKnownNotImmediate(base))
+        isNotObject.append(branch32(NotEqual, regT1, Imm32(JSValue::CellTag)));
+    if (base != m_codeBlock->thisRegister()) {
+        loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+        isNotObject.append(branch32(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType)));
+    }
+
+    // We could inline the case where you have a valid cache, but
+    // this call doesn't seem to be hot.
+    Label isObject(this);
+    JITStubCall getPnamesStubCall(this, cti_op_get_pnames);
+    getPnamesStubCall.addArgument(regT0);
+    getPnamesStubCall.call(dst);
+    load32(Address(regT0, OBJECT_OFFSETOF(JSPropertyNameIterator, m_jsStringsSize)), regT3);
+    store32(Imm32(0), addressFor(i));
+    store32(regT3, addressFor(size));
+    Jump end = jump();
+
+    isNotObject.link(this);
+    addJump(branch32(Equal, regT1, Imm32(JSValue::NullTag)), breakTarget);
+    addJump(branch32(Equal, regT1, Imm32(JSValue::UndefinedTag)), breakTarget);
+    JITStubCall toObjectStubCall(this, cti_to_object);
+    toObjectStubCall.addArgument(regT1, regT0);
+    toObjectStubCall.call(base);
+    jump().linkTo(isObject, this);
+    
+    end.link(this);
+}
+
 void JIT::emit_op_next_pname(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
-    int iter = currentInstruction[2].u.operand;
-    int target = currentInstruction[3].u.operand;
+    int base = currentInstruction[2].u.operand;
+    int i = currentInstruction[3].u.operand;
+    int size = currentInstruction[4].u.operand;
+    int it = currentInstruction[5].u.operand;
+    int target = currentInstruction[6].u.operand;
+    
+    JumpList callHasProperty;
+
+    Label begin(this);
+    load32(addressFor(i), regT0);
+    Jump end = branch32(Equal, regT0, addressFor(size));
+
+    // Grab key @ i
+    loadPtr(addressFor(it), regT1);
+    loadPtr(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_jsStrings)), regT2);
+    load32(BaseIndex(regT2, regT0, TimesEight), regT2);
+    store32(Imm32(JSValue::CellTag), tagFor(dst));
+    store32(regT2, payloadFor(dst));
+
+    // Increment i
+    add32(Imm32(1), regT0);
+    store32(regT0, addressFor(i));
+
+    // Verify that i is valid:
+    loadPtr(addressFor(base), regT0);
 
-    load32(Address(callFrameRegister, (iter * sizeof(Register))), regT0);
+    // Test base's structure
+    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+    callHasProperty.append(branchPtr(NotEqual, regT2, Address(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure)))));
+
+    // Test base's prototype chain
+    loadPtr(Address(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedPrototypeChain))), regT3);
+    loadPtr(Address(regT3, OBJECT_OFFSETOF(StructureChain, m_vector)), regT3);
+    addJump(branchTestPtr(Zero, Address(regT3)), target);
+
+    Label checkPrototype(this);
+    callHasProperty.append(branch32(Equal, Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), Imm32(JSValue::NullTag)));
+    loadPtr(Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT2);
+    loadPtr(Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+    callHasProperty.append(branchPtr(NotEqual, regT2, Address(regT3)));
+    addPtr(Imm32(sizeof(Structure*)), regT3);
+    branchTestPtr(NonZero, Address(regT3)).linkTo(checkPrototype, this);
 
-    JITStubCall stubCall(this, cti_op_next_pname);
+    // Continue loop.
+    addJump(jump(), target);
+
+    // Slow case: Ask the object if i is valid.
+    callHasProperty.link(this);
+    loadPtr(addressFor(dst), regT1);
+    JITStubCall stubCall(this, cti_has_property);
     stubCall.addArgument(regT0);
+    stubCall.addArgument(regT1);
     stubCall.call();
 
-    Jump endOfIter = branchTestPtr(Zero, regT0);
-    emitStore(dst, regT1, regT0);
-    map(m_bytecodeIndex + OPCODE_LENGTH(op_next_pname), dst, regT1, regT0);
-    addJump(jump(), target);
-    endOfIter.link(this);
+    // Test for valid key.
+    addJump(branchTest32(NonZero, regT0), target);
+    jump().linkTo(begin, this);
+
+    // End of loop.
+    end.link(this);
 }
 
 void JIT::emit_op_push_scope(Instruction* currentInstruction)
@@ -2372,15 +2459,110 @@ void JIT::emit_op_throw(Instruction* currentInstruction)
 #endif
 }
 
+void JIT::emit_op_get_pnames(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int base = currentInstruction[2].u.operand;
+    int i = currentInstruction[3].u.operand;
+    int size = currentInstruction[4].u.operand;
+    int breakTarget = currentInstruction[5].u.operand;
+
+    JumpList isNotObject;
+
+    emitGetVirtualRegister(base, regT0);
+    if (!m_codeBlock->isKnownNotImmediate(base))
+        isNotObject.append(emitJumpIfNotJSCell(regT0));
+    if (base != m_codeBlock->thisRegister()) {
+        loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+        isNotObject.append(branch32(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType)));
+    }
+
+    // We could inline the case where you have a valid cache, but
+    // this call doesn't seem to be hot.
+    Label isObject(this);
+    JITStubCall getPnamesStubCall(this, cti_op_get_pnames);
+    getPnamesStubCall.addArgument(regT0);
+    getPnamesStubCall.call(dst);
+    load32(Address(regT0, OBJECT_OFFSETOF(JSPropertyNameIterator, m_jsStringsSize)), regT3);
+    store32(Imm32(0), addressFor(i));
+    store32(regT3, addressFor(size));
+    Jump end = jump();
+
+    isNotObject.link(this);
+    move(regT0, regT1);
+    and32(Imm32(~JSImmediate::ExtendedTagBitUndefined), regT1);
+    addJump(branch32(Equal, regT1, Imm32(JSImmediate::FullTagTypeNull)), breakTarget);
+
+    JITStubCall toObjectStubCall(this, cti_to_object);
+    toObjectStubCall.addArgument(regT0);
+    toObjectStubCall.call(base);
+    jump().linkTo(isObject, this);
+    
+    end.link(this);
+}
+
 void JIT::emit_op_next_pname(Instruction* currentInstruction)
 {
-    JITStubCall stubCall(this, cti_op_next_pname);
-    stubCall.addArgument(currentInstruction[2].u.operand, regT2);
+    int dst = currentInstruction[1].u.operand;
+    int base = currentInstruction[2].u.operand;
+    int i = currentInstruction[3].u.operand;
+    int size = currentInstruction[4].u.operand;
+    int it = currentInstruction[5].u.operand;
+    int target = currentInstruction[6].u.operand;
+    
+    JumpList callHasProperty;
+
+    Label begin(this);
+    load32(addressFor(i), regT0);
+    Jump end = branch32(Equal, regT0, addressFor(size));
+
+    // Grab key @ i
+    loadPtr(addressFor(it), regT1);
+    loadPtr(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_jsStrings)), regT2);
+    loadPtr(BaseIndex(regT2, regT0, TimesEight), regT2);
+    emitPutVirtualRegister(dst, regT2);
+
+    // Increment i
+    add32(Imm32(1), regT0);
+    store32(regT0, addressFor(i));
+
+    // Verify that i is valid:
+    emitGetVirtualRegister(base, regT0);
+
+    // Test base's structure
+    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+    callHasProperty.append(branchPtr(NotEqual, regT2, Address(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure)))));
+
+    // Test base's prototype chain
+    loadPtr(Address(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedPrototypeChain))), regT3);
+    loadPtr(Address(regT3, OBJECT_OFFSETOF(StructureChain, m_vector)), regT3);
+    addJump(branchTestPtr(Zero, Address(regT3)), target);
+
+    Label checkPrototype(this);
+    loadPtr(Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype)), regT2);
+    callHasProperty.append(emitJumpIfNotJSCell(regT2));
+    loadPtr(Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
+    callHasProperty.append(branchPtr(NotEqual, regT2, Address(regT3)));
+    addPtr(Imm32(sizeof(Structure*)), regT3);
+    branchTestPtr(NonZero, Address(regT3)).linkTo(checkPrototype, this);
+
+    // Continue loop.
+    addJump(jump(), target);
+
+    // Slow case: Ask the object if i is valid.
+    callHasProperty.link(this);
+    emitGetVirtualRegister(dst, regT1);
+    JITStubCall stubCall(this, cti_has_property);
+    stubCall.addArgument(regT0);
+    stubCall.addArgument(regT1);
     stubCall.call();
-    Jump endOfIter = branchTestPtr(Zero, regT0);
-    emitPutVirtualRegister(currentInstruction[1].u.operand);
-    addJump(jump(), currentInstruction[3].u.operand);
-    endOfIter.link(this);
+
+    // Test for valid key.
+    addJump(branchTest32(NonZero, regT0), target);
+    jump().linkTo(begin, this);
+
+    // End of loop.
+    end.link(this);
 }
 
 void JIT::emit_op_push_scope(Instruction* currentInstruction)
diff --git a/JavaScriptCore/jit/JITStubs.cpp b/JavaScriptCore/jit/JITStubs.cpp
index ccdde37..90ea807 100644
--- a/JavaScriptCore/jit/JITStubs.cpp
+++ b/JavaScriptCore/jit/JITStubs.cpp
@@ -700,11 +700,15 @@ NEVER_INLINE void JITThunks::tryCachePutByID(CallFrame* callFrame, CodeBlock* co
 
     // Structure transition, cache transition info
     if (slot.type() == PutPropertySlot::NewProperty) {
-        StructureChain* prototypeChain = structure->prototypeChain(callFrame);
-        if (!prototypeChain->isCacheable() || structure->isDictionary()) {
+        if (structure->isDictionary()) {
             ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_put_by_id_generic));
             return;
         }
+
+        // put_by_id_transition checks the prototype chain for setters.
+        normalizePrototypeChain(callFrame, baseCell);
+
+        StructureChain* prototypeChain = structure->prototypeChain(callFrame);
         stubInfo->initPutByIdTransition(structure->previousID(), structure, prototypeChain);
         JIT::compilePutByIdTransition(callFrame->scopeChain()->globalData, codeBlock, stubInfo, structure->previousID(), structure, slot.cachedOffset(), prototypeChain, returnAddress);
         return;
@@ -780,17 +784,13 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
         return;
     }
 
-    size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
+    size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase());
     if (!count) {
         stubInfo->accessType = access_get_by_id_generic;
         return;
     }
 
     StructureChain* prototypeChain = structure->prototypeChain(callFrame);
-    if (!prototypeChain->isCacheable()) {
-        ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
-        return;
-    }
     stubInfo->initGetByIdChain(structure, prototypeChain);
     JIT::compileGetByIdChain(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, prototypeChain, count, slot.cachedOffset(), returnAddress);
 }
@@ -1332,15 +1332,11 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
 
         if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
             ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_list_full));
-    } else if (size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot)) {
-        StructureChain* protoChain = structure->prototypeChain(callFrame);
-        if (!protoChain->isCacheable()) {
-            ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
-            return JSValue::encode(result);
-        }
-        
+    } else if (size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase())) {
         int listIndex;
         PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex);
+
+        StructureChain* protoChain = structure->prototypeChain(callFrame);
         JIT::compileGetByIdChainList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, protoChain, count, slot.cachedOffset());
 
         if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
@@ -2671,18 +2667,22 @@ DEFINE_STUB_FUNCTION(JSPropertyNameIterator*, op_get_pnames)
 {
     STUB_INIT_STACK_FRAME(stackFrame);
 
-    return JSPropertyNameIterator::create(stackFrame.callFrame, stackFrame.args[0].jsValue());
+    CallFrame* callFrame = stackFrame.callFrame;
+    JSObject* o = stackFrame.args[0].jsObject();
+    Structure* structure = o->structure();
+    JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache();
+    if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(callFrame))
+        jsPropertyNameIterator = JSPropertyNameIterator::create(callFrame, o);
+    return jsPropertyNameIterator;
 }
 
-DEFINE_STUB_FUNCTION(EncodedJSValue, op_next_pname)
+DEFINE_STUB_FUNCTION(int, has_property)
 {
     STUB_INIT_STACK_FRAME(stackFrame);
 
-    JSPropertyNameIterator* it = stackFrame.args[0].propertyNameIterator();
-    JSValue temp = it->next(stackFrame.callFrame);
-    if (!temp)
-        it->invalidate();
-    return JSValue::encode(temp);
+    JSObject* base = stackFrame.args[0].jsObject();
+    JSString* property = stackFrame.args[1].jsString();
+    return base->hasProperty(stackFrame.callFrame, Identifier(stackFrame.callFrame, property->value()));
 }
 
 DEFINE_STUB_FUNCTION(JSObject*, op_push_scope)
@@ -3023,6 +3023,14 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, vm_throw)
     return JSValue::encode(exceptionValue);
 }
 
+DEFINE_STUB_FUNCTION(EncodedJSValue, to_object)
+{
+    STUB_INIT_STACK_FRAME(stackFrame);
+
+    CallFrame* callFrame = stackFrame.callFrame;
+    return JSValue::encode(stackFrame.args[0].jsValue().toObject(callFrame));
+}
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)
diff --git a/JavaScriptCore/jit/JITStubs.h b/JavaScriptCore/jit/JITStubs.h
index daae043..ccbcd2a 100644
--- a/JavaScriptCore/jit/JITStubs.h
+++ b/JavaScriptCore/jit/JITStubs.h
@@ -63,6 +63,7 @@ namespace JSC {
         int32_t asInt32;
 
         JSValue jsValue() { return JSValue::decode(asEncodedJSValue); }
+        JSObject* jsObject() { return static_cast<JSObject*>(asPointer); }
         Identifier& identifier() { return *static_cast<Identifier*>(asPointer); }
         int32_t int32() { return asInt32; }
         CodeBlock* codeBlock() { return static_cast<CodeBlock*>(asPointer); }
@@ -285,7 +286,6 @@ extern "C" {
     EncodedJSValue JIT_STUB cti_op_mod(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_mul(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_negate(STUB_ARGS_DECLARATION);
-    EncodedJSValue JIT_STUB cti_op_next_pname(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_not(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_nstricteq(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_post_dec(STUB_ARGS_DECLARATION);
@@ -307,6 +307,7 @@ extern "C" {
     EncodedJSValue JIT_STUB cti_op_typeof(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_urshift(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_vm_throw(STUB_ARGS_DECLARATION);
+    EncodedJSValue JIT_STUB cti_to_object(STUB_ARGS_DECLARATION);
     JSObject* JIT_STUB cti_op_construct_JSConstruct(STUB_ARGS_DECLARATION);
     JSObject* JIT_STUB cti_op_new_array(STUB_ARGS_DECLARATION);
     JSObject* JIT_STUB cti_op_new_error(STUB_ARGS_DECLARATION);
@@ -332,6 +333,7 @@ extern "C" {
     int JIT_STUB cti_op_loop_if_lesseq(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_op_loop_if_true(STUB_ARGS_DECLARATION);
     int JIT_STUB cti_timeout_check(STUB_ARGS_DECLARATION);
+    int JIT_STUB cti_has_property(STUB_ARGS_DECLARATION);
     void JIT_STUB cti_op_create_arguments(STUB_ARGS_DECLARATION);
     void JIT_STUB cti_op_create_arguments_no_params(STUB_ARGS_DECLARATION);
     void JIT_STUB cti_op_debug(STUB_ARGS_DECLARATION);
diff --git a/JavaScriptCore/parser/Nodes.cpp b/JavaScriptCore/parser/Nodes.cpp
index 3bd318a..b1e317e 100644
--- a/JavaScriptCore/parser/Nodes.cpp
+++ b/JavaScriptCore/parser/Nodes.cpp
@@ -1468,14 +1468,16 @@ RegisterID* ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds
     if (!m_lexpr->isLocation())
         return emitThrowError(generator, ReferenceError, "Left side of for-in statement is not a reference.");
 
-    RefPtr<Label> continueTarget = generator.newLabel(); 
-
     generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine());
 
     if (m_init)
         generator.emitNode(generator.ignoredResult(), m_init);
-    RegisterID* forInBase = generator.emitNode(m_expr);
-    RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), forInBase);
+
+    RefPtr<RegisterID> base = generator.newTemporary();
+    generator.emitNode(base.get(), m_expr);
+    RefPtr<RegisterID> i = generator.newTemporary();
+    RefPtr<RegisterID> size = generator.newTemporary();
+    RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), base.get(), i.get(), size.get(), scope->breakTarget());
     generator.emitJump(scope->continueTarget());
 
     RefPtr<Label> loopStart = generator.newLabel();
@@ -1517,7 +1519,7 @@ RegisterID* ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds
     generator.emitNode(dst, m_statement);
 
     generator.emitLabel(scope->continueTarget());
-    generator.emitNextPropertyName(propertyName, iter.get(), loopStart.get());
+    generator.emitNextPropertyName(propertyName, base.get(), i.get(), size.get(), iter.get(), loopStart.get());
     generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine());
     generator.emitLabel(scope->breakTarget());
     return dst;
diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h
index 63c58ac..16a5131 100644
--- a/JavaScriptCore/runtime/JSCell.h
+++ b/JavaScriptCore/runtime/JSCell.h
@@ -112,14 +112,6 @@ namespace JSC {
         Structure* m_structure;
     };
 
-    // FIXME: We should deprecate this and just use JSValue::asCell() instead.
-    JSCell* asCell(JSValue);
-
-    inline JSCell* asCell(JSValue value)
-    {
-        return value.asCell();
-    }
-
     inline JSCell::JSCell(Structure* structure)
         : m_structure(structure)
     {
diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp
index b24af32..6932ded 100644
--- a/JavaScriptCore/runtime/JSObject.cpp
+++ b/JavaScriptCore/runtime/JSObject.cpp
@@ -443,45 +443,22 @@ bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyNa
 
 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
-    bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_structure->isDictionary());
-
-    if (shouldCache) {
-        if (PropertyNameArrayData* data = m_structure->enumerationCache()) {
-            if (data->cachedPrototypeChain() == m_structure->prototypeChain(exec)) {
-                propertyNames.setData(data);
-                return;
-            }
-
-            m_structure->clearEnumerationCache();
-        }
-    }
-
     getOwnPropertyNames(exec, propertyNames);
 
-    if (prototype().isObject()) {
-        propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
-        JSObject* prototype = asObject(this->prototype());
-        while(1) {
-            if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
-                prototype->getPropertyNames(exec, propertyNames);
-                break;
-            }
-            prototype->getOwnPropertyNames(exec, propertyNames);
-            JSValue nextProto = prototype->prototype();
-            if (!nextProto.isObject())
-                break;
-            prototype = asObject(nextProto);
-        }
-    }
+    if (prototype().isNull())
+        return;
 
-    if (shouldCache) {
-        StructureChain* protoChain = m_structure->prototypeChain(exec);
-        if (!protoChain->isCacheable())
-            return;
-        RefPtr<PropertyNameArrayData> data = propertyNames.data();
-        data->setCachedPrototypeChain(protoChain);
-        data->setCachedStructure(m_structure);
-        m_structure->setEnumerationCache(data.release());
+    JSObject* prototype = asObject(this->prototype());
+    while(1) {
+        if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
+            prototype->getPropertyNames(exec, propertyNames);
+            break;
+        }
+        prototype->getOwnPropertyNames(exec, propertyNames);
+        JSValue nextProto = prototype->prototype();
+        if (nextProto.isNull())
+            break;
+        prototype = asObject(nextProto);
     }
 }
 
diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
index e08a3d9..2cd9f75 100644
--- a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
+++ b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
@@ -29,26 +29,56 @@
 #include "config.h"
 #include "JSPropertyNameIterator.h"
 
+#include "JSGlobalObject.h"
+
 namespace JSC {
 
 ASSERT_CLASS_FITS_IN_CELL(JSPropertyNameIterator);
 
-JSPropertyNameIterator::~JSPropertyNameIterator()
+JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject* o)
 {
+    ASSERT(!o->structure()->enumerationCache() ||
+            o->structure()->enumerationCache()->cachedStructure() != o->structure() ||
+            o->structure()->enumerationCache()->cachedPrototypeChain() != o->structure()->prototypeChain(exec));
+
+    PropertyNameArray propertyNames(exec);
+    o->getPropertyNames(exec, propertyNames);
+    JSPropertyNameIterator* jsPropertyNameIterator = new (exec) JSPropertyNameIterator(exec, propertyNames.data());
+
+    if (o->structure()->isDictionary())
+        return jsPropertyNameIterator;
+
+    if (o->structure()->typeInfo().overridesGetPropertyNames())
+        return jsPropertyNameIterator;
+    
+    size_t count = normalizePrototypeChain(exec, o);
+    StructureChain* structureChain = o->structure()->prototypeChain(exec);
+    RefPtr<Structure>* structure = structureChain->head();
+    for (size_t i = 0; i < count; ++i) {
+        if (structure[i]->typeInfo().overridesGetPropertyNames())
+            return jsPropertyNameIterator;
+    }
+
+    jsPropertyNameIterator->setCachedPrototypeChain(structureChain);
+    jsPropertyNameIterator->setCachedStructure(o->structure());
+    o->structure()->setEnumerationCache(jsPropertyNameIterator);
+    return jsPropertyNameIterator;
 }
 
-void JSPropertyNameIterator::markChildren(MarkStack& markStack)
+JSValue JSPropertyNameIterator::get(ExecState* exec, JSObject* base, size_t i)
 {
-    JSCell::markChildren(markStack);
-    if (m_object)
-        markStack.append(m_object);
+    JSValue& identifier = m_jsStrings[i];
+    if (m_cachedStructure == base->structure() && m_cachedPrototypeChain == base->structure()->prototypeChain(exec))
+        return identifier;
+
+    if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value())))
+        return JSValue();
+    return identifier;
 }
 
-void JSPropertyNameIterator::invalidate()
+void JSPropertyNameIterator::markChildren(MarkStack& markStack)
 {
-    ASSERT(m_position == m_end);
-    m_object = 0;
-    m_data.clear();
+    markStack.appendValues(m_jsStrings.get(), m_jsStringsSize, MayContainNullValues);
 }
 
 } // namespace JSC
diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.h b/JavaScriptCore/runtime/JSPropertyNameIterator.h
index 2b66d2b..0559e0b 100644
--- a/JavaScriptCore/runtime/JSPropertyNameIterator.h
+++ b/JavaScriptCore/runtime/JSPropertyNameIterator.h
@@ -31,6 +31,7 @@
 
 #include "JSObject.h"
 #include "JSString.h"
+#include "Operations.h"
 #include "PropertyNameArray.h"
 
 namespace JSC {
@@ -39,73 +40,51 @@ namespace JSC {
     class JSObject;
 
     class JSPropertyNameIterator : public JSCell {
-    public:
-        static JSPropertyNameIterator* create(ExecState*, JSValue);
-
-        virtual ~JSPropertyNameIterator();
-
-        virtual void markChildren(MarkStack&);
+        friend class JIT;
 
-        JSValue next(ExecState*);
-        void invalidate();
+    public:
+        static JSPropertyNameIterator* create(ExecState*, JSObject*);
         
         static PassRefPtr<Structure> createStructure(JSValue prototype)
         {
             return Structure::create(prototype, TypeInfo(CompoundType, OverridesMarkChildren));
         }
+
+        virtual void markChildren(MarkStack&);
+
+        JSValue get(ExecState*, JSObject*, size_t i);
+        size_t size() { return m_jsStringsSize; }
+
+        void setCachedStructure(Structure* structure) { m_cachedStructure = structure; }
+        Structure* cachedStructure() { return m_cachedStructure; }
+
+        void setCachedPrototypeChain(NonNullPassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; }
+        StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); }
+
     private:
-        JSPropertyNameIterator(ExecState*);
-        JSPropertyNameIterator(ExecState*, JSObject*, PassRefPtr<PropertyNameArrayData> propertyNameArrayData);
+        JSPropertyNameIterator(ExecState*, PropertyNameArrayData* propertyNameArrayData);
 
-        JSObject* m_object;
-        RefPtr<PropertyNameArrayData> m_data;
-        PropertyNameArrayData::const_iterator m_position;
-        PropertyNameArrayData::const_iterator m_end;
+        Structure* m_cachedStructure;
+        RefPtr<StructureChain> m_cachedPrototypeChain;
+        size_t m_jsStringsSize;
+        OwnArrayPtr<JSValue> m_jsStrings;
     };
 
-inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec)
+inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, PropertyNameArrayData* propertyNameArrayData)
     : JSCell(exec->globalData().propertyNameIteratorStructure.get())
-    , m_object(0)
-    , m_position(0)
-    , m_end(0)
+    , m_cachedStructure(0)
+    , m_jsStringsSize(propertyNameArrayData->propertyNameVector().size())
+    , m_jsStrings(new JSValue[m_jsStringsSize])
 {
+    PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArrayData->propertyNameVector();
+    for (size_t i = 0; i < m_jsStringsSize; ++i)
+        m_jsStrings[i] = jsOwnedString(exec, propertyNameVector[i].ustring());
 }
 
-inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, JSObject* object, PassRefPtr<PropertyNameArrayData> propertyNameArrayData)
-    : JSCell(exec->globalData().propertyNameIteratorStructure.get())
-    , m_object(object)
-    , m_data(propertyNameArrayData)
-    , m_position(m_data->begin())
-    , m_end(m_data->end())
-{
-}
-
-inline JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSValue v)
+inline void Structure::setEnumerationCache(JSPropertyNameIterator* enumerationCache)
 {
-    if (v.isUndefinedOrNull())
-        return new (exec) JSPropertyNameIterator(exec);
-
-    JSObject* o = v.toObject(exec);
-    PropertyNameArray propertyNames(exec);
-    o->getPropertyNames(exec, propertyNames);
-    return new (exec) JSPropertyNameIterator(exec, o, propertyNames.releaseData());
-}
-
-inline JSValue JSPropertyNameIterator::next(ExecState* exec)
-{
-    if (m_position == m_end)
-        return JSValue();
-
-    if (m_data->cachedStructure() == m_object->structure() && m_data->cachedPrototypeChain() == m_object->structure()->prototypeChain(exec))
-        return jsOwnedString(exec, (*m_position++).ustring());
-
-    do {
-        if (m_object->hasProperty(exec, *m_position))
-            return jsOwnedString(exec, (*m_position++).ustring());
-        m_position++;
-    } while (m_position != m_end);
-
-    return JSValue();
+    ASSERT(!isDictionary());
+    m_enumerationCache = enumerationCache;
 }
 
 } // namespace JSC
diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h
index 3c511d8..1063cdc 100644
--- a/JavaScriptCore/runtime/JSValue.h
+++ b/JavaScriptCore/runtime/JSValue.h
@@ -373,6 +373,14 @@ namespace JSC {
         return static_cast<uint32_t>(val);
     }
 
+    // FIXME: We should deprecate this and just use JSValue::asCell() instead.
+    JSCell* asCell(JSValue);
+
+    inline JSCell* asCell(JSValue value)
+    {
+        return value.asCell();
+    }
+
     ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const
     {
         if (isInt32())
diff --git a/JavaScriptCore/runtime/MarkStack.h b/JavaScriptCore/runtime/MarkStack.h
index ba00057..ea09f54 100644
--- a/JavaScriptCore/runtime/MarkStack.h
+++ b/JavaScriptCore/runtime/MarkStack.h
@@ -47,7 +47,7 @@ namespace JSC {
         }
 
         ALWAYS_INLINE void append(JSValue);
-        ALWAYS_INLINE void append(JSCell*);
+        void append(JSCell*);
         
         ALWAYS_INLINE void appendValues(Register* values, size_t count, MarkSetProperties properties = NoNullValues)
         {
diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp
index a456423..837d5a6 100644
--- a/JavaScriptCore/runtime/ObjectConstructor.cpp
+++ b/JavaScriptCore/runtime/ObjectConstructor.cpp
@@ -125,6 +125,7 @@ JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec,
     return description;
 }
 
+// FIXME: Use the enumeration cache.
 JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args)
 {
     if (!args.at(0).isObject())
diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h
index 5da9e38..1aa68b3 100644
--- a/JavaScriptCore/runtime/Operations.h
+++ b/JavaScriptCore/runtime/Operations.h
@@ -224,15 +224,15 @@ namespace JSC {
         return jsAddSlowCase(callFrame, v1, v2);
     }
 
-    inline size_t countPrototypeChainEntriesAndCheckForProxies(CallFrame* callFrame, JSValue baseValue, const PropertySlot& slot)
+    inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase)
     {
-        JSCell* cell = asCell(baseValue);
+        JSCell* cell = asCell(base);
         size_t count = 0;
 
-        while (slot.slotBase() != cell) {
+        while (slotBase != cell) {
             JSValue v = cell->structure()->prototypeForLookup(callFrame);
 
-            // If we didn't find slotBase in baseValue's prototype chain, then baseValue
+            // If we didn't find slotBase in base's prototype chain, then base
             // must be a proxy for another object.
 
             if (v.isNull())
@@ -252,6 +252,25 @@ namespace JSC {
         return count;
     }
 
+    inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
+    {
+        size_t count = 0;
+        while (1) {
+            JSValue v = base->structure()->prototypeForLookup(callFrame);
+            if (v.isNull())
+                return count;
+
+            base = asCell(v);
+
+            // Since we're accessing a prototype in a loop, it's a good bet that it
+            // should not be treated as a dictionary.
+            if (base->structure()->isDictionary())
+                asObject(base)->setStructure(Structure::fromDictionaryTransition(base->structure()));
+
+            ++count;
+        }
+    }
+
     ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
     {
         ScopeChainIterator iter = scopeChain->begin();
diff --git a/JavaScriptCore/runtime/PropertyNameArray.cpp b/JavaScriptCore/runtime/PropertyNameArray.cpp
index cff2605..c28b6a4 100644
--- a/JavaScriptCore/runtime/PropertyNameArray.cpp
+++ b/JavaScriptCore/runtime/PropertyNameArray.cpp
@@ -47,7 +47,7 @@ void PropertyNameArray::add(UString::Rep* identifier)
             return;
     }
 
-    m_data->propertyNameVector().append(Identifier(m_globalData, identifier));
+    addKnownUnique(identifier);
 }
 
 } // namespace JSC
diff --git a/JavaScriptCore/runtime/PropertyNameArray.h b/JavaScriptCore/runtime/PropertyNameArray.h
index 7d6edd0..3dbcc9d 100644
--- a/JavaScriptCore/runtime/PropertyNameArray.h
+++ b/JavaScriptCore/runtime/PropertyNameArray.h
@@ -24,6 +24,7 @@
 #include "CallFrame.h"
 #include "Identifier.h"
 #include <wtf/HashSet.h>
+#include <wtf/OwnArrayPtr.h>
 #include <wtf/Vector.h>
 
 namespace JSC {
@@ -31,39 +32,26 @@ namespace JSC {
     class Structure;
     class StructureChain;
 
+    // FIXME: Rename to PropertyNameArray.
     class PropertyNameArrayData : public RefCounted<PropertyNameArrayData> {
     public:
         typedef Vector<Identifier, 20> PropertyNameVector;
-        typedef PropertyNameVector::const_iterator const_iterator;
 
         static PassRefPtr<PropertyNameArrayData> create() { return adoptRef(new PropertyNameArrayData); }
 
-        const_iterator begin() const { return m_propertyNameVector.begin(); }
-        const_iterator end() const { return m_propertyNameVector.end(); }
-
         PropertyNameVector& propertyNameVector() { return m_propertyNameVector; }
 
-        void setCachedStructure(Structure* structure) { m_cachedStructure = structure; }
-        Structure* cachedStructure() const { return m_cachedStructure; }
-
-        void setCachedPrototypeChain(NonNullPassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; }
-        StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); }
-
     private:
         PropertyNameArrayData()
-            : m_cachedStructure(0)
         {
         }
 
         PropertyNameVector m_propertyNameVector;
-        Structure* m_cachedStructure;
-        RefPtr<StructureChain> m_cachedPrototypeChain;
     };
 
+    // FIXME: Rename to PropertyNameArrayBuilder.
     class PropertyNameArray {
     public:
-        typedef PropertyNameArrayData::const_iterator const_iterator;
-
         PropertyNameArray(JSGlobalData* globalData)
             : m_data(PropertyNameArrayData::create())
             , m_globalData(globalData)
@@ -84,21 +72,18 @@ namespace JSC {
         void add(UString::Rep*);
         void addKnownUnique(UString::Rep* identifier) { m_data->propertyNameVector().append(Identifier(m_globalData, identifier)); }
 
-        size_t size() const { return m_data->propertyNameVector().size(); }
-
         Identifier& operator[](unsigned i) { return m_data->propertyNameVector()[i]; }
         const Identifier& operator[](unsigned i) const { return m_data->propertyNameVector()[i]; }
 
-        const_iterator begin() const { return m_data->begin(); }
-        const_iterator end() const { return m_data->end(); }
-
         void setData(PassRefPtr<PropertyNameArrayData> data) { m_data = data; }
         PropertyNameArrayData* data() { return m_data.get(); }
-
         PassRefPtr<PropertyNameArrayData> releaseData() { return m_data.release(); }
 
-        void setShouldCache(bool shouldCache) { m_shouldCache = shouldCache; }
-        bool shouldCache() const { return m_shouldCache; }
+        // FIXME: Remove these functions.
+        typedef PropertyNameArrayData::PropertyNameVector::const_iterator const_iterator;
+        size_t size() const { return m_data->propertyNameVector().size(); }
+        const_iterator begin() const { return m_data->propertyNameVector().begin(); }
+        const_iterator end() const { return m_data->propertyNameVector().end(); }
 
     private:
         typedef HashSet<UString::Rep*, PtrHash<UString::Rep*> > IdentifierSet;
diff --git a/JavaScriptCore/runtime/Protect.h b/JavaScriptCore/runtime/Protect.h
index 224164d..a0d5443 100644
--- a/JavaScriptCore/runtime/Protect.h
+++ b/JavaScriptCore/runtime/Protect.h
@@ -22,8 +22,8 @@
 #ifndef Protect_h
 #define Protect_h
 
-#include "JSCell.h"
 #include "Collector.h"
+#include "JSValue.h"
 
 namespace JSC {
 
diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp
index 643a49a..a11050f 100644
--- a/JavaScriptCore/runtime/Structure.cpp
+++ b/JavaScriptCore/runtime/Structure.cpp
@@ -28,9 +28,10 @@
 
 #include "Identifier.h"
 #include "JSObject.h"
+#include "JSPropertyNameIterator.h"
+#include "Lookup.h"
 #include "PropertyNameArray.h"
 #include "StructureChain.h"
-#include "Lookup.h"
 #include <wtf/RefCountedLeakCounter.h>
 #include <wtf/RefPtr.h>
 
@@ -159,9 +160,9 @@ Structure::~Structure()
             m_previous->table.removeAnonymousSlotTransition(m_anonymousSlotsInPrevious);
 
     }
-
-    if (m_cachedPropertyNameArrayData)
-        m_cachedPropertyNameArrayData->setCachedStructure(0);
+    
+    if (m_enumerationCache)
+        m_enumerationCache->setCachedStructure(0);
 
     if (m_propertyTable) {
         unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
@@ -282,13 +283,6 @@ void Structure::materializePropertyMap()
     }
 }
 
-void Structure::clearEnumerationCache()
-{
-    if (m_cachedPropertyNameArrayData)
-        m_cachedPropertyNameArrayData->setCachedStructure(0);
-    m_cachedPropertyNameArrayData.clear();
-}
-
 void Structure::growPropertyStorageCapacity()
 {
     if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
@@ -552,25 +546,25 @@ PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
 
 size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
 {
+    ASSERT(!m_enumerationCache);
     materializePropertyMapIfNecessary();
 
     m_isPinnedPropertyTable = true;
     size_t offset = put(propertyName, attributes, specificValue);
     if (propertyStorageSize() > propertyStorageCapacity())
         growPropertyStorageCapacity();
-    clearEnumerationCache();
     return offset;
 }
 
 size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
 {
     ASSERT(isUncacheableDictionary());
+    ASSERT(!m_enumerationCache);
 
     materializePropertyMapIfNecessary();
 
     m_isPinnedPropertyTable = true;
     size_t offset = remove(propertyName);
-    clearEnumerationCache();
     return offset;
 }
 
diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h
index a027b1a..2496c1b 100644
--- a/JavaScriptCore/runtime/Structure.h
+++ b/JavaScriptCore/runtime/Structure.h
@@ -31,6 +31,7 @@
 #include "JSValue.h"
 #include "PropertyMapHashTable.h"
 #include "PropertyNameArray.h"
+#include "Protect.h"
 #include "StructureChain.h"
 #include "StructureTransitionTable.h"
 #include "JSTypeInfo.h"
@@ -123,9 +124,8 @@ namespace JSC {
         JSCell* specificValue() { return m_specificValueInPrevious; }
         void despecifyDictionaryFunction(const Identifier& propertyName);
 
-        void setEnumerationCache(PassRefPtr<PropertyNameArrayData> data) { m_cachedPropertyNameArrayData = data; }
-        PropertyNameArrayData* enumerationCache() { return m_cachedPropertyNameArrayData.get(); }
-        void clearEnumerationCache();
+        void setEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
+        JSPropertyNameIterator* enumerationCache() { return m_enumerationCache.get(); }
         void getEnumerablePropertyNames(PropertyNameArray&);
 
     private:
@@ -186,7 +186,7 @@ namespace JSC {
 
         StructureTransitionTable table;
 
-        RefPtr<PropertyNameArrayData> m_cachedPropertyNameArrayData;
+        ProtectedPtr<JSPropertyNameIterator> m_enumerationCache;
 
         PropertyMapHashTable* m_propertyTable;
 
diff --git a/JavaScriptCore/runtime/StructureChain.cpp b/JavaScriptCore/runtime/StructureChain.cpp
index 0a9bc69..085876c 100644
--- a/JavaScriptCore/runtime/StructureChain.cpp
+++ b/JavaScriptCore/runtime/StructureChain.cpp
@@ -46,18 +46,4 @@ StructureChain::StructureChain(Structure* head)
     m_vector[i] = 0;
 }
 
-bool StructureChain::isCacheable() const
-{
-    uint32_t i = 0;
-    
-    while (m_vector[i]) {
-        // Both classes of dictionary structure may change arbitrarily so we can't cache them
-        if (m_vector[i]->isDictionary())
-            return false;
-        if (m_vector[i++]->typeInfo().overridesGetPropertyNames())
-            return false;
-    }
-    return true;
-}
-
 } // namespace JSC
diff --git a/JavaScriptCore/runtime/StructureChain.h b/JavaScriptCore/runtime/StructureChain.h
index c48749d..816b66d 100644
--- a/JavaScriptCore/runtime/StructureChain.h
+++ b/JavaScriptCore/runtime/StructureChain.h
@@ -36,10 +36,11 @@ namespace JSC {
     class Structure;
 
     class StructureChain : public RefCounted<StructureChain> {
+        friend class JIT;
+
     public:
         static PassRefPtr<StructureChain> create(Structure* head) { return adoptRef(new StructureChain(head)); }
         RefPtr<Structure>* head() { return m_vector.get(); }
-        bool isCacheable() const;
 
     private:
         StructureChain(Structure* head);

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list