我正在参加「掘金·启航计划」
补充说明
上一篇文章讲了如何在LLDB中查看JS对象的属性值,了解了Butterfly等概念。
有一个点之前没有提到,对于一个非array的JS对象,如果其属性数量较少,则JSC并不会为其分配相应的Butterfly对象,而是直接存在对象尾部(叫做inline property),存到Butterfly处的则称做out-of-line property。
注:看起来属性offset在64以下的属性都是用inline存储(非array的情况):
static constexpr PropertyOffset firstOutOfLineOffset = 64;
Structure
本节主要讲Structure。
在 JavaScriptCore 的概念中,Structure对应的就是JS对象的Prototype(原型)。每个JS对象都有一个Structure,Structure之间可以有继承关系,当JS对象的结构(也即prototype)发生变化时,JS对象会生成一个新的(或复用一个已有的)Stucture。一个对象中有哪些属性,以及这些属性对应到对象内存中的偏移都存在Structure及其相应ClassInfo对象中。
从上一篇对JS对象内存布局的描述可知,JS对象中存储了StructureID的值,是一个int值,那么如何从StructureID得到真正的Structure对象呢?
以下代码片段描述了StuctureID类的部分定义:
constexpr uintptr_t structureHeapAddressSize = 4 * GB;
class StructureID {
public:
static constexpr uint32_t nukedStructureIDBit = 1;
static constexpr CPURegister structureIDMask = structureHeapAddressSize - 1;
...
StructureID decontaminate() const { return StructureID(m_bits & ~nukedStructureIDBit); }
inline Structure* decode() const;
...
private:
explicit StructureID(uint32_t bits) : m_bits(bits) { }
uint32_t m_bits { 0 };
};
在StructureID内部存储了一个uint32_t,其代表Structure表内的偏移,真正的Structure数据是存在Structure表中。
StrctureID有个inline成员函数decode(),此方法即实现了从StructureID到Structure的转换:
ALWAYS_INLINE Structure* StructureID::decode() const
{
// Take care to only use the bits from m_bits in the structure's address reservation.
ASSERT(decontaminate());
return reinterpret_cast<Structure*>((static_cast<uintptr_t>(decontaminate().m_bits) & structureIDMask) + g_jscConfig.startOfStructureHeap);
}
从上面的代码片段可以看出,Structure的具体位置是某个全局变量(g_jscConfig.startOfStructureHeap)加上 StructureID 作为偏移。
下一个问题就是:如何在运行时找到g_jscConfig?
看一下g_jscConfig及其相关的关键对象的定义:
#define g_jscConfig (*bitwise_cast<JSC::Config*>(&g_wtfConfig.spaceForExtensions))
//注:12 * sizeof(void *) == 12 * 8 == 0x60
#define g_wtfConfig (*bitwise_cast<WTF::Config*>(&WebConfig::g_config[WTF::startSlotOfWTFConfig]))
其中:
constexpr size_t reservedSlotsForGigacageConfig = 12;
constexpr size_t startSlotOfWTFConfig = Gigacage::reservedSlotsForGigacageConfig;
也即,如果知道g_config,即可通过以下方式得到g_jscConfig及其startOfStructureHeap:
&g_wtfConfig == &g_config + 0x60 //g_config->wtfConfig
&g_jscConfig == &g_wtfConfig + 0x140 //g_wtfConfig->spaceForExtensions
g_jscConfig->startOfStructureHeap == *(&g_jscConfig + 0x30) //g_jscConfig->startOfStructureHeap
看了一下JavasScriptCore的导出符号,其中包含了g_config,使用
dlsym(RTLD_DEFAULT, "g_config")
即可取到。
在lldb中,也可以直接print全局变量g_config的地址:
(lldb) p &g_config
下一篇,我们接着看Structure的具体内容。