JavaScriptCore:
2008-10-03 Maciej Stachowiak <mjs@apple.com>
Reviewed by Cameron Zwarich.
- "this" object in methods called on primitives should be wrapper object
https://bugs.webkit.org/show_bug.cgi?id=21362
I changed things so that functions which use "this" do a fast
version of toThisObject conversion if needed. Currently we miss
the conversion entirely, at least for primitive types. Using
TypeInfo and the primitive check, I made the fast case bail out
pretty fast.
This is inexplicably an 1.007x SunSpider speedup (and a wash on V8 benchmarks).
Also renamed some opcodes for clarity:
init ==> enter
init_activation ==> enter_with_activation
* VM/CTI.cpp:
(JSC::CTI::privateCompileMainPass):
(JSC::CTI::privateCompileSlowCases):
* VM/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* VM/CodeGenerator.cpp:
(JSC::CodeGenerator::generate):
(JSC::CodeGenerator::CodeGenerator):
* VM/Machine.cpp:
(JSC::Machine::privateExecute):
(JSC::Machine::cti_op_convert_this):
* VM/Machine.h:
* VM/Opcode.h:
* kjs/JSActivation.cpp:
(JSC::JSActivation::JSActivation):
* kjs/JSActivation.h:
(JSC::JSActivation::createStructureID):
* kjs/JSCell.h:
(JSC::JSValue::needsThisConversion):
* kjs/JSGlobalData.cpp:
(JSC::JSGlobalData::JSGlobalData):
* kjs/JSGlobalData.h:
* kjs/JSNumberCell.h:
(JSC::JSNumberCell::createStructureID):
* kjs/JSStaticScopeObject.h:
(JSC::JSStaticScopeObject::JSStaticScopeObject):
(JSC::JSStaticScopeObject::createStructureID):
* kjs/JSString.h:
(JSC::JSString::createStructureID):
* kjs/JSValue.h:
* kjs/TypeInfo.h:
(JSC::TypeInfo::needsThisConversion):
* kjs/nodes.h:
(JSC::ScopeNode::usesThis):
WebCore:
2008-10-03 Maciej Stachowiak <mjs@apple.com>
Reviewed by Cameron Zwarich.
- "this" object in methods called on primitives should be wrapper object
https://bugs.webkit.org/show_bug.cgi?id=21362
Updated so toThis conversion for the split window is handled properly.
* bindings/scripts/CodeGeneratorJS.pm:
LayoutTests:
2008-10-03 Maciej Stachowiak <mjs@apple.com>
Reviewed by Cameron Zwarich.
- test case for: "this" object in methods called on primitives should be wrapper object
* fast/js/primitive-method-this-expected.txt: Added.
* fast/js/primitive-method-this.html: Added.
* fast/js/resources/primitive-method-this.js: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@37285 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index c8dea35..9dc019a 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,58 @@
+2008-10-03 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by Cameron Zwarich.
+
+ - "this" object in methods called on primitives should be wrapper object
+ https://bugs.webkit.org/show_bug.cgi?id=21362
+
+ I changed things so that functions which use "this" do a fast
+ version of toThisObject conversion if needed. Currently we miss
+ the conversion entirely, at least for primitive types. Using
+ TypeInfo and the primitive check, I made the fast case bail out
+ pretty fast.
+
+ This is inexplicably an 1.007x SunSpider speedup (and a wash on V8 benchmarks).
+
+ Also renamed some opcodes for clarity:
+
+ init ==> enter
+ init_activation ==> enter_with_activation
+
+ * VM/CTI.cpp:
+ (JSC::CTI::privateCompileMainPass):
+ (JSC::CTI::privateCompileSlowCases):
+ * VM/CodeBlock.cpp:
+ (JSC::CodeBlock::dump):
+ * VM/CodeGenerator.cpp:
+ (JSC::CodeGenerator::generate):
+ (JSC::CodeGenerator::CodeGenerator):
+ * VM/Machine.cpp:
+ (JSC::Machine::privateExecute):
+ (JSC::Machine::cti_op_convert_this):
+ * VM/Machine.h:
+ * VM/Opcode.h:
+ * kjs/JSActivation.cpp:
+ (JSC::JSActivation::JSActivation):
+ * kjs/JSActivation.h:
+ (JSC::JSActivation::createStructureID):
+ * kjs/JSCell.h:
+ (JSC::JSValue::needsThisConversion):
+ * kjs/JSGlobalData.cpp:
+ (JSC::JSGlobalData::JSGlobalData):
+ * kjs/JSGlobalData.h:
+ * kjs/JSNumberCell.h:
+ (JSC::JSNumberCell::createStructureID):
+ * kjs/JSStaticScopeObject.h:
+ (JSC::JSStaticScopeObject::JSStaticScopeObject):
+ (JSC::JSStaticScopeObject::createStructureID):
+ * kjs/JSString.h:
+ (JSC::JSString::createStructureID):
+ * kjs/JSValue.h:
+ * kjs/TypeInfo.h:
+ (JSC::TypeInfo::needsThisConversion):
+ * kjs/nodes.h:
+ (JSC::ScopeNode::usesThis):
+
2008-10-03 Cameron Zwarich <zwarich@apple.com>
Reviewed by Maciej Stachowiak.
diff --git a/JavaScriptCore/VM/CTI.cpp b/JavaScriptCore/VM/CTI.cpp
index 7cb7106..3702946 100644
--- a/JavaScriptCore/VM/CTI.cpp
+++ b/JavaScriptCore/VM/CTI.cpp
@@ -1964,7 +1964,7 @@
i += 3;
break;
}
- case op_init: {
+ case op_enter: {
// Even though CTI doesn't use them, we initialize our constant
// registers to zap stale pointers, to avoid unnecessarily prolonging
// object lifetime and increasing GC pressure.
@@ -1975,7 +1975,7 @@
i+= 1;
break;
}
- case op_init_activation: {
+ case op_enter_with_activation: {
emitCall(i, Machine::cti_op_push_activation);
// Even though CTI doesn't use them, we initialize our constant
@@ -1993,6 +1993,17 @@
i += 1;
break;
}
+ case op_convert_this: {
+ emitGetArg(instruction[i + 1].u.operand, X86::eax);
+
+ emitJumpSlowCaseIfNotJSCell(X86::eax, i);
+ m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::eax, X86::edx);
+ m_jit.testl_i32m(NeedsThisConversion, OBJECT_OFFSET(StructureID, m_typeInfo.m_flags), X86::edx);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJnz(), i));
+
+ i += 2;
+ break;
+ }
case op_get_array_length:
case op_get_by_id_chain:
case op_get_by_id_generic:
@@ -2037,6 +2048,15 @@
for (Vector<SlowCaseEntry>::iterator iter = m_slowCases.begin(); iter != m_slowCases.end(); ++iter) {
unsigned i = iter->to;
switch (m_machine->getOpcodeID(instruction[i].u.opcode)) {
+ case op_convert_this: {
+ m_jit.link(iter->from, m_jit.label());
+ m_jit.link((++iter)->from, m_jit.label());
+ emitPutArg(X86::eax, 0);
+ emitCall(i, Machine::cti_op_convert_this);
+ emitPutResult(instruction[i + 1].u.operand);
+ i += 2;
+ break;
+ }
case op_add: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
diff --git a/JavaScriptCore/VM/CodeBlock.cpp b/JavaScriptCore/VM/CodeBlock.cpp
index eaf94e2..dc17fc1 100644
--- a/JavaScriptCore/VM/CodeBlock.cpp
+++ b/JavaScriptCore/VM/CodeBlock.cpp
@@ -348,18 +348,23 @@
{
int location = it - begin;
switch (exec->machine()->getOpcodeID(it->u.opcode)) {
- case op_init: {
- printf("[%4d] init\n", location);
+ case op_enter: {
+ printf("[%4d] enter\n", location);
break;
}
- case op_init_activation: {
- printf("[%4d] init_activation\n", location);
+ case op_enter_with_activation: {
+ printf("[%4d] enter_with_activation\n", location);
break;
}
case op_init_arguments: {
printf("[%4d] init_arguments\n", location);
break;
}
+ case op_convert_this: {
+ int r0 = (++it)->u.operand;
+ printf("[%4d] convert_this %s\n", location, registerName(r0).c_str());
+ break;
+ }
case op_unexpected_load: {
int r0 = (++it)->u.operand;
int k0 = (++it)->u.operand;
diff --git a/JavaScriptCore/VM/CodeGenerator.cpp b/JavaScriptCore/VM/CodeGenerator.cpp
index 0234a74..bb39592 100644
--- a/JavaScriptCore/VM/CodeGenerator.cpp
+++ b/JavaScriptCore/VM/CodeGenerator.cpp
@@ -137,8 +137,8 @@
m_scopeNode->emitCode(*this);
if (m_codeType == FunctionCode && m_codeBlock->needsFullScopeChain) {
- ASSERT(globalData()->machine->getOpcodeID(m_codeBlock->instructions[0].u.opcode) == op_init);
- m_codeBlock->instructions[0] = globalData()->machine->getOpcode(op_init_activation);
+ ASSERT(globalData()->machine->getOpcodeID(m_codeBlock->instructions[0].u.opcode) == op_enter);
+ m_codeBlock->instructions[0] = globalData()->machine->getOpcode(op_enter_with_activation);
}
#ifndef NDEBUG
@@ -216,7 +216,7 @@
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
- emitOpcode(op_init);
+ emitOpcode(op_enter);
codeBlock->globalData = m_globalData;
// FIXME: Move code that modifies the global object to Machine::execute.
@@ -289,7 +289,7 @@
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
- emitOpcode(op_init);
+ emitOpcode(op_enter);
codeBlock->globalData = m_globalData;
bool usesArguments = functionBody->usesArguments();
@@ -321,6 +321,11 @@
m_thisRegister.setIndex(m_nextParameter);
++m_nextParameter;
++m_codeBlock->numParameters;
+
+ if (functionBody->usesThis()) {
+ emitOpcode(op_convert_this);
+ instructions().append(m_thisRegister.index());
+ }
for (size_t i = 0; i < parameterCount; ++i)
addParameter(parameters[i]);
@@ -342,7 +347,7 @@
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
- emitOpcode(op_init);
+ emitOpcode(op_enter);
codeBlock->globalData = m_globalData;
m_codeBlock->numParameters = 1; // Allocate space for "this"
diff --git a/JavaScriptCore/VM/Machine.cpp b/JavaScriptCore/VM/Machine.cpp
index a14ee1b..ed40eb5 100644
--- a/JavaScriptCore/VM/Machine.cpp
+++ b/JavaScriptCore/VM/Machine.cpp
@@ -3375,7 +3375,7 @@
NEXT_OPCODE;
}
- BEGIN_OPCODE(op_init) {
+ BEGIN_OPCODE(op_enter) {
size_t i = 0;
CodeBlock* codeBlock = this->codeBlock(r);
@@ -3388,7 +3388,7 @@
++vPC;
NEXT_OPCODE;
}
- BEGIN_OPCODE(op_init_activation) {
+ BEGIN_OPCODE(op_enter_with_activation) {
size_t i = 0;
CodeBlock* codeBlock = this->codeBlock(r);
@@ -3405,6 +3405,15 @@
++vPC;
NEXT_OPCODE;
}
+ BEGIN_OPCODE(op_convert_this) {
+ int thisRegister = (++vPC)->u.operand;
+ JSValue* thisVal = r[thisRegister].getJSValue();
+ if (thisVal->needsThisConversion())
+ r[thisRegister] = thisVal->toThisObject(exec);
+
+ ++vPC;
+ NEXT_OPCODE;
+ }
BEGIN_OPCODE(op_init_arguments) {
JSValue* activation = r[RegisterFile::OptionalCalleeActivation].getJSValue();
Arguments* arguments;
@@ -4200,6 +4209,17 @@
} \
} while (0)
+
+JSValue* Machine::cti_op_convert_this(CTI_ARGS)
+{
+ JSValue* v1 = ARG_src1;
+ ExecState* exec = ARG_exec;
+
+ JSObject* result = v1->toThisObject(exec);
+ VM_CHECK_EXCEPTION_AT_END();
+ return result;
+}
+
void Machine::cti_op_end(CTI_ARGS)
{
Register* r = ARG_r;
diff --git a/JavaScriptCore/VM/Machine.h b/JavaScriptCore/VM/Machine.h
index 4f2057d..c2a9d36 100644
--- a/JavaScriptCore/VM/Machine.h
+++ b/JavaScriptCore/VM/Machine.h
@@ -143,6 +143,7 @@
static void SFX_CALL cti_timeout_check(CTI_ARGS);
+ static JSValue* SFX_CALL cti_op_convert_this(CTI_ARGS);
static void SFX_CALL cti_op_end(CTI_ARGS);
static JSValue* SFX_CALL cti_op_add(CTI_ARGS);
static JSValue* SFX_CALL cti_op_pre_inc(CTI_ARGS);
diff --git a/JavaScriptCore/VM/Opcode.h b/JavaScriptCore/VM/Opcode.h
index 425d3c3..f1c9029 100644
--- a/JavaScriptCore/VM/Opcode.h
+++ b/JavaScriptCore/VM/Opcode.h
@@ -40,9 +40,10 @@
#define DUMP_OPCODE_STATS 0
#define FOR_EACH_OPCODE_ID(macro) \
- macro(op_init) \
- macro(op_init_activation) \
+ macro(op_enter) \
+ macro(op_enter_with_activation) \
macro(op_init_arguments) \
+ macro(op_convert_this) \
\
macro(op_unexpected_load) \
macro(op_new_object) \
diff --git a/JavaScriptCore/kjs/JSActivation.cpp b/JavaScriptCore/kjs/JSActivation.cpp
index b83a370..2905510 100644
--- a/JavaScriptCore/kjs/JSActivation.cpp
+++ b/JavaScriptCore/kjs/JSActivation.cpp
@@ -40,7 +40,7 @@
const ClassInfo JSActivation::info = { "JSActivation", 0, 0, 0 };
JSActivation::JSActivation(ExecState* exec, PassRefPtr<FunctionBodyNode> functionBody, Register* registers)
- : Base(exec->globalData().nullProtoStructureID, new JSActivationData(functionBody, registers))
+ : Base(exec->globalData().activationStructureID, new JSActivationData(functionBody, registers))
{
}
diff --git a/JavaScriptCore/kjs/JSActivation.h b/JavaScriptCore/kjs/JSActivation.h
index 7361a96..ef45f27 100644
--- a/JavaScriptCore/kjs/JSActivation.h
+++ b/JavaScriptCore/kjs/JSActivation.h
@@ -64,6 +64,8 @@
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
+ static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(ObjectType, NeedsThisConversion)); }
+
private:
struct JSActivationData : public JSVariableObjectData {
JSActivationData(PassRefPtr<FunctionBodyNode> functionBody, Register* registers)
diff --git a/JavaScriptCore/kjs/JSCell.h b/JavaScriptCore/kjs/JSCell.h
index 6b300bd..bced0cd 100644
--- a/JavaScriptCore/kjs/JSCell.h
+++ b/JavaScriptCore/kjs/JSCell.h
@@ -299,6 +299,14 @@
return asCell()->toThisObject(exec);
}
+ inline bool JSValue::needsThisConversion() const
+ {
+ if (UNLIKELY(JSImmediate::isImmediate(this)))
+ return true;
+
+ return asCell()->structureID()->typeInfo().needsThisConversion();
+ }
+
inline UString JSValue::toThisString(ExecState* exec) const
{
return JSImmediate::isImmediate(this) ? JSImmediate::toString(this) : asCell()->toThisString(exec);
diff --git a/JavaScriptCore/kjs/JSGlobalData.cpp b/JavaScriptCore/kjs/JSGlobalData.cpp
index 4636086..7633731 100644
--- a/JavaScriptCore/kjs/JSGlobalData.cpp
+++ b/JavaScriptCore/kjs/JSGlobalData.cpp
@@ -31,8 +31,10 @@
#include "ArgList.h"
#include "CommonIdentifiers.h"
+#include "JSActivation.h"
#include "JSClassRef.h"
#include "JSLock.h"
+#include "JSStaticScopeObject.h"
#include "Machine.h"
#include "Parser.h"
#include "collector.h"
@@ -76,6 +78,8 @@
, stringTable(&JSC::stringTable)
#endif
, nullProtoStructureID(JSObject::createStructureID(jsNull()))
+ , activationStructureID(JSActivation::createStructureID(jsNull()))
+ , staticScopeStructureID(JSStaticScopeObject::createStructureID(jsNull()))
, stringStructureID(JSString::createStructureID(jsNull()))
, numberStructureID(JSNumberCell::createStructureID(jsNull()))
, identifierTable(createIdentifierTable())
diff --git a/JavaScriptCore/kjs/JSGlobalData.h b/JavaScriptCore/kjs/JSGlobalData.h
index be6fa97..937a785 100644
--- a/JavaScriptCore/kjs/JSGlobalData.h
+++ b/JavaScriptCore/kjs/JSGlobalData.h
@@ -73,6 +73,8 @@
const HashTable* stringTable;
RefPtr<StructureID> nullProtoStructureID;
+ RefPtr<StructureID> activationStructureID;
+ RefPtr<StructureID> staticScopeStructureID;
RefPtr<StructureID> stringStructureID;
RefPtr<StructureID> numberStructureID;
diff --git a/JavaScriptCore/kjs/JSNumberCell.h b/JavaScriptCore/kjs/JSNumberCell.h
index ab257d2..1116651 100644
--- a/JavaScriptCore/kjs/JSNumberCell.h
+++ b/JavaScriptCore/kjs/JSNumberCell.h
@@ -82,7 +82,7 @@
#endif
}
- static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(NumberType)); }
+ static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(NumberType, NeedsThisConversion)); }
private:
JSNumberCell(JSGlobalData* globalData, double value)
diff --git a/JavaScriptCore/kjs/JSStaticScopeObject.h b/JavaScriptCore/kjs/JSStaticScopeObject.h
index cc3bf02..b152862 100644
--- a/JavaScriptCore/kjs/JSStaticScopeObject.h
+++ b/JavaScriptCore/kjs/JSStaticScopeObject.h
@@ -44,7 +44,7 @@
public:
JSStaticScopeObject(ExecState* exec, const Identifier& ident, JSValue* value, unsigned attributes)
- : JSVariableObject(exec->globalData().nullProtoStructureID, new JSStaticScopeObjectData())
+ : JSVariableObject(exec->globalData().staticScopeStructureID, new JSStaticScopeObjectData())
{
d()->registerStore = value;
symbolTable().add(ident.ustring().rep(), SymbolTableEntry(-1, attributes));
@@ -58,6 +58,8 @@
virtual void put(ExecState*, const Identifier&, JSValue*, PutPropertySlot&);
void putWithAttributes(ExecState*, const Identifier&, JSValue*, unsigned attributes);
+ static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(ObjectType, NeedsThisConversion)); }
+
private:
JSStaticScopeObjectData* d() { return static_cast<JSStaticScopeObjectData*>(JSVariableObject::d); }
};
diff --git a/JavaScriptCore/kjs/JSString.h b/JavaScriptCore/kjs/JSString.h
index e502c11..d7c37bf 100644
--- a/JavaScriptCore/kjs/JSString.h
+++ b/JavaScriptCore/kjs/JSString.h
@@ -90,7 +90,7 @@
bool canGetIndex(unsigned i) { return i < static_cast<unsigned>(m_value.size()); }
JSString* getIndex(JSGlobalData*, unsigned);
- static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(StringType)); }
+ static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(StringType, NeedsThisConversion)); }
private:
enum VPtrStealingHackType { VPtrStealingHack };
diff --git a/JavaScriptCore/kjs/JSValue.h b/JavaScriptCore/kjs/JSValue.h
index d901c0b..d0e4513 100644
--- a/JavaScriptCore/kjs/JSValue.h
+++ b/JavaScriptCore/kjs/JSValue.h
@@ -137,6 +137,7 @@
bool deleteProperty(ExecState*, const Identifier& propertyName);
bool deleteProperty(ExecState*, unsigned propertyName);
+ bool needsThisConversion() const;
JSObject* toThisObject(ExecState*) const;
UString toThisString(ExecState*) const;
JSString* toThisJSString(ExecState*);
diff --git a/JavaScriptCore/kjs/TypeInfo.h b/JavaScriptCore/kjs/TypeInfo.h
index ac86d9d..477e0f2 100644
--- a/JavaScriptCore/kjs/TypeInfo.h
+++ b/JavaScriptCore/kjs/TypeInfo.h
@@ -35,6 +35,7 @@
static const unsigned MasqueradesAsUndefined = 1;
static const unsigned ImplementsHasInstance = 1 << 1;
static const unsigned OverridesHasInstance = 1 << 2;
+ static const unsigned NeedsThisConversion = 1 << 3;
class TypeInfo {
friend class CTI;
@@ -46,6 +47,7 @@
bool masqueradesAsUndefined() const { return m_flags & MasqueradesAsUndefined; }
bool implementsHasInstance() const { return m_flags & ImplementsHasInstance; }
bool overridesHasInstance() const { return m_flags & OverridesHasInstance; }
+ bool needsThisConversion() const { return m_flags & NeedsThisConversion; }
unsigned flags() const { return m_flags; }
private:
diff --git a/JavaScriptCore/kjs/nodes.h b/JavaScriptCore/kjs/nodes.h
index 8162993..fc5e5a7 100644
--- a/JavaScriptCore/kjs/nodes.h
+++ b/JavaScriptCore/kjs/nodes.h
@@ -2192,6 +2192,7 @@
bool containsClosures() const { return m_features & ClosureFeature; }
bool usesArguments() const { return m_features & ArgumentsFeature; }
void setUsesArguments() { m_features |= ArgumentsFeature; }
+ bool usesThis() const { return m_features & ThisFeature; }
VarStack& varStack() { return m_varStack; }
FunctionStack& functionStack() { return m_functionStack; }