JSShape定义
JSShape在内存中的分布如下图
代码定义如下:
#define JS_PROP_INITIAL_SIZE 2
#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
#define JS_ARRAY_INITIAL_SIZE 2
typedef struct JSShapeProperty {
uint32_t hash_next : 26; /* 0 if last in list */
uint32_t flags : 6; /* JS_PROP_XXX */
JSAtom atom; /* JS_ATOM_NULL = free property entry */
} JSShapeProperty;
struct JSShape {
/*
请注意读下面这句话:
hash table of size hash_mask + 1 before the start of the
structure (see prop_hash_end()).
*/
JSGCObjectHeader header;
/* true if the shape is inserted in the shape hash table. If not,
JSShape.hash is not valid */
//如果它的值为true,说明它被插入了rt->shape_hash表中了,如果false,下面的hash是无效的
uint8_t is_hashed;
/* If true, the shape may have small array index properties 'n' with 0
<= n <= 2^31-1. If false, the shape is guaranteed not to have
small array index properties */
uint8_t has_small_array_index;
uint32_t hash; /* current hash value */
uint32_t prop_hash_mask;//属性 hash_mask,用于计算属性的hash
int prop_size; /* allocated properties 属性尺寸*/
int prop_count; /* include deleted properties 属性大小*/
int deleted_prop_count;//删除的属性大小
JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list 在rt->shape_hash中的下一个JSShape */
JSObject *proto;//对象的__proto__对象
JSShapeProperty prop[0]; /* prop_size elements 属性数组*/
};
typedef enum {
JS_GC_OBJ_TYPE_JS_OBJECT,
JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
JS_GC_OBJ_TYPE_SHAPE,
JS_GC_OBJ_TYPE_VAR_REF,
JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
JS_GC_OBJ_TYPE_JS_CONTEXT,
} JSGCObjectTypeEnum;
/* header for GC objects. GC objects are C data structures with a
reference count that can reference other GC objects. JS Objects are
a particular type of GC object. */
struct JSGCObjectHeader {
int ref_count; /* must come first, 32-bit */
JSGCObjectTypeEnum gc_obj_type : 4;
uint8_t mark : 4; /* used by the GC */
uint8_t dummy1; /* not used by the GC */
uint16_t dummy2; /* not used by the GC */
struct list_head link;
};
JSShape在代码中的定义如上。JSShape可以看作是JSObject的模板。JSObject的属性和原型对象信息都保存在JSShape中。每一个JSObject都持有一个JSShape,但是某一个JSShape可能被多个JSObject持有。每个JSShape被创建时,都将会计算它的hash,计算方法是调用 shape_initial_hash 函数。JSShape的hash和创建它时,它持有的proto对象有关系。所有的JSShape都存储在 rt->shape_hash 数组中。几个具有相同hash的JSShape组成了一个单向链表,它的 shape_hash_next 字段指向下一个具有相同hash的JSShape。JSShape对存储在其中的JSShapeProperty也进行了hash。目的是便于快速的寻找JSObject的属性。
JSShape 与 rt->shape_hash 的关系
一个JSShape创建之后,会被以它的hash为索引,存储在 rt->shape_hash 中。同时,为了解决hash碰撞问题,它使用了拉链法,为相同hash的JSShape创建了一个链表。具体实现就是为每一个JSShape设置一个 shape_hash_next 字段,它指向下一个具有相同hash的JSShape。
几个基础的函数
先看几个与JSShape相关的基础函数
init_shape_hash
// 初始化 rt->shape_hash
static int init_shape_hash(JSRuntime *rt)
{
rt->shape_hash_bits = 4; /* 16 shapes */
rt->shape_hash_size = 1 << rt->shape_hash_bits;
rt->shape_hash_count = 0;
rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
rt->shape_hash_size);
if (!rt->shape_hash)
return -1;
return 0;
}
JSShape创建之后,被存储在 rt->shape_hash 中。该函数作用就是为 rt->shape_hash 分配内存,并初始化一些用于存储或标记JSShape的信息。
shape_hash
// 将两个数字想加然后乘以魔法数得到hash
/* same magic hash multiplier as the Linux kernel */
static uint32_t shape_hash(uint32_t h, uint32_t val)
{
return (h + val) * 0x9e370001;
}
函数的作用就是根据两个数组计算JSShape的hash
shape_initial_hash
// 根据原型对象计算hash
// 原理就是将proto指针,作为一个普通的数字计算它的hash
static uint32_t shape_initial_hash(JSObject *proto)
{
uint32_t h;
// 将1和proto的地址想加然后乘以魔法数得到hash
h = shape_hash(1, (uintptr_t)proto);
// 如果proto的大小超过4字节,然后将proto指针的高32位与刚得到的hash重新计算一个新的hash
if (sizeof(proto) > 4)
h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
return h;
}
函数的作用是根据JSShape持有的__proto__对象计算JSShape的初始的hash
get_shape_hash
/* truncate the shape hash to 'hash_bits' bits */
// 截断shape的hash成 "hash位"
static uint32_t get_shape_hash(uint32_t h, int hash_bits)
{
return h >> (32 - hash_bits);
}
配合上面的 shape_initial_hash 函数一起使用,目的是将JSShape的hash截断,以使它的值不会超过指定的大小。
get_shape_size
/* Shape support */
//获取shape的大小,注意,入参是hash_size和prop_size,申请的大小是:hash_size * 4 + sizeof(JSShape) + prop_size * sizeof(JSShapeProperty)
static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
{
return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
prop_size * sizeof(JSShapeProperty);
}
该函数的作用是获取shape的内存占用大小。入参是hash_size和prop_size。代码比较简单,就是将hash_size * sizeof(uint32_t) 加上 prop_size * sizeof(JSShapeProperty) 以及 sizeof(JSShape)。可以看出来, 某一个JSShape在内存中占用的大小绝不仅仅是 JSShape结构体的大小与属性大小的和,它还包含了一个 hash_size * sizeof(uint32_t) 大小的内存块。这个内存块的作用是 以某一个 JSShapeProperty 的hash做索引,存储它在 JSShape->prop 中的索引。
get_shape_from_alloc
// 从申请的内存中获取JSShape
// 创建一个新的JSShape时,申请的内存大小是 get_shape_size 计算出来的
static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
{
return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
}
该函数的作用根据申请的内存得到JSShape。为什么需要这个函数?从文章开始的图来看,在内存中,JSShape的开始位置的前面是存在一块内存,用于支持它的属性的hash查找。因此,我们创建内存时,申请的大小是包含这一部分的。得到内存之后,需要得到指向JSShape结构体的指针,因此这个指针就是使用这个函数来得到。
prop_hash_end
// 将JSShape转为内存指针
static inline uint32_t *prop_hash_end(JSShape *sh)
{
return (uint32_t *)sh;
}
获取JSShape在内存中的指针,将JSShape强转为内存指针。
get_alloc_from_shape
// 根据JSShape获取创建它时,它申请的内存起始地址
static inline void *get_alloc_from_shape(JSShape *sh)
{
// 获取起始地址
return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
}
根据JSShape获取创建它时,它申请的内存起始地址。
get_shape_prop
// 获取JSShape中保存的JSObject的属性的地址
static inline JSShapeProperty *get_shape_prop(JSShape *sh)
{
return sh->prop;
}
获取JSShape中保存的JSObject的属性的地址。
resize_shape_hash
// 根据新的 shape_hash_bits 申请新的shape_hash
/* 1. 重新申请内存当做新的shape_hash
2. 将老的shape_hash内的JSShape拷贝到新的shape_hash中(这一步,将会重新计算hash的shape)
*/
static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
{
int new_shape_hash_size, i;
uint32_t h;
JSShape **new_shape_hash, *sh, *sh_next;
// 2^n 新 rt->shape_hash 的大小
new_shape_hash_size = 1 << new_shape_hash_bits;
// 申请的新 rt->shape_hash 内存
new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
new_shape_hash_size);
if (!new_shape_hash)
return -1;
// 将 rt->shape_hash 中的数据迁移到新的 shape_hash 中
for(i = 0; i < rt->shape_hash_size; i++) {
// 抓住一个shape,并遍历所有和这个shape的hash相同的shape,将其复制到新的数组中
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
sh_next = sh->shape_hash_next;
// 根据新的shape_hash_bits 计算新的hash
h = get_shape_hash(sh->hash, new_shape_hash_bits);
// 重建链表
sh->shape_hash_next = new_shape_hash[h];
// 将当前shape存放在新的shape_hash表中
new_shape_hash[h] = sh;
}
}
// 迁移结束之后,释放原来的shape
js_free_rt(rt, rt->shape_hash);
// 更新hash_bit
rt->shape_hash_bits = new_shape_hash_bits;
// 更新hash_size
rt->shape_hash_size = new_shape_hash_size;
// 指向新的 shape_hash
rt->shape_hash = new_shape_hash;
return 0;
}
js_shape_hash_link 与 js_shape_hash_unlink
// 将 JSShape 链入 rt->shape_hash
static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
{
uint32_t h;
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
// 不管rt->shape_hash[h]是否存有JSShape,都将其赋值给要插入的JSShape的next中
sh->shape_hash_next = rt->shape_hash[h];
// 将JSShape 放入 rt->shape_hash[h]中
rt->shape_hash[h] = sh;
// 统计加一
rt->shape_hash_count++;
}
// 将JSShape从rt->shape_hash中移除
static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
{
uint32_t h;
JSShape **psh;
//找到 rt->shape_hash 中相同要移除的shape
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
psh = &rt->shape_hash[h];
while (*psh != sh)
psh = &(*psh)->shape_hash_next;
// 找到之后,将其赋值为shape->shape_hash_next
*psh = sh->shape_hash_next;
// 减少rt->shape_hash中的shape的计数
rt->shape_hash_count--;
}
JSShape的创建
JSShape与JSObject是绑定在一起的。而JSObject需要根据JSShape来创建。创建JSShape的代码如下:
/* create a new empty shape with prototype 'proto' */
static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
int hash_size, int prop_size)
{
JSRuntime *rt = ctx->rt;
void *sh_alloc;
JSShape *sh;
/* resize the shape hash table if necessary */
if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
resize_shape_hash(rt, rt->shape_hash_bits + 1);
}
//申请内存时,申请的大小是hash_size和prop_size之和
sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
if (!sh_alloc)
return NULL;
// 从申请的内存基础地址 + hash_size 转化成 shape
// 这句代码很有意思,换个思路,说明每一个JSShape之前都有一段 hash_size 大小的内存与 JSShape有关系,这段内存用于存储属性的hash
sh = get_shape_from_alloc(sh_alloc, hash_size);
sh->header.ref_count = 1;
add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
if (proto)
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
sh->proto = proto;
// 接着上面说的,JSShape前面空出来的内存先清空
memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
hash_size);
// 设置prop_hash_mask,这个参数用于计算JSShape的属性的hash
sh->prop_hash_mask = hash_size - 1;
sh->prop_size = prop_size;// 属性尺寸
sh->prop_count = 0;// 记录属性的数量
sh->deleted_prop_count = 0; // 已经删除了的属性的数量
// 将JSShape插入到 rt->shape_hash 表中
/* insert in the hash table */
// 先根据proto计算hash
sh->hash = shape_initial_hash(proto);
sh->is_hashed = TRUE;
sh->has_small_array_index = FALSE;
// 将shape链入rt->shape_hash中
js_shape_hash_link(ctx->rt, sh);
return sh;
}
static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
{
return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
JS_PROP_INITIAL_SIZE);
}
JSShape的克隆与添加引用
/* The shape is cloned. The new shape is not inserted in the shape
hash table */
static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
{
JSShape *sh;
void *sh_alloc, *sh_alloc1;
size_t size;
JSShapeProperty *pr;
uint32_t i, hash_size;
hash_size = sh1->prop_hash_mask + 1;
size = get_shape_size(hash_size, sh1->prop_size);
sh_alloc = js_malloc(ctx, size);
if (!sh_alloc)
return NULL;
sh_alloc1 = get_alloc_from_shape(sh1);
memcpy(sh_alloc, sh_alloc1, size);
sh = get_shape_from_alloc(sh_alloc, hash_size);
sh->header.ref_count = 1;
add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
sh->is_hashed = FALSE;
if (sh->proto) {
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
}
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
JS_DupAtom(ctx, pr->atom);
}
return sh;
}
// 添加引用系数
static JSShape *js_dup_shape(JSShape *sh)
{
sh->header.ref_count++;
return sh;
}
JSShape的释放
// 释放shape
static void js_free_shape0(JSRuntime *rt, JSShape *sh)
{
uint32_t i;
JSShapeProperty *pr;
assert(sh->header.ref_count == 0);
// 如果shape加入了rt->shape_hash,需要先从列表中移除
if (sh->is_hashed)
js_shape_hash_unlink(rt, sh);
if (sh->proto != NULL) {
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
}
pr = get_shape_prop(sh);
for(i = 0; i < sh->prop_count; i++) {
JS_FreeAtomRT(rt, pr->atom);
pr++;
}
// 移除引用
remove_gc_object(&sh->header);
// 释放掉创建shape时,申请的内存
js_free_rt(rt, get_alloc_from_shape(sh));
}
static void js_free_shape(JSRuntime *rt, JSShape *sh)
{
if (unlikely(--sh->header.ref_count <= 0)) {
js_free_shape0(rt, sh);
}
}
//判断shape是否是空的,然后释放
static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
{
if (sh)
js_free_shape(rt, sh);
}
JSShape属性数组扩展
/* make space to hold at least 'count' properties */
/**
更改shape属性数组的大小
*/
static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
JSObject *p, uint32_t count)
{
JSShape *sh;
uint32_t new_size, new_hash_size, new_hash_mask, i;
JSShapeProperty *pr;
void *sh_alloc;
intptr_t h;
sh = *psh;
// 计算需要分配的prop大小
new_size = max_int(count, sh->prop_size * 3 / 2);
/* Reallocate prop array first to avoid crash or size inconsistency
in case of memory allocation failure */
// 先申请prop数组,以免申请内存失败
if (p) {
JSProperty *new_prop;
new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
if (unlikely(!new_prop))
return -1;
p->prop = new_prop;
}
//新hash的大小
new_hash_size = sh->prop_hash_mask + 1;
while (new_hash_size < new_size)
new_hash_size = 2 * new_hash_size;
// 判断是否需要重新分配属性的 hash table
if (new_hash_size != (sh->prop_hash_mask + 1)) {
JSShape *old_sh;
/* resize the hash table and the properties */
old_sh = sh;
//重新申请内存用于存放shape
sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
if (!sh_alloc)
return -1;
//
sh = get_shape_from_alloc(sh_alloc, new_hash_size);
list_del(&old_sh->header.link);
/* copy all the fields and the properties */
// 复制所有的字段和属性
memcpy(sh, old_sh,
sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
new_hash_mask = new_hash_size - 1;
sh->prop_hash_mask = new_hash_mask;
memset(prop_hash_end(sh) - new_hash_size, 0,
sizeof(prop_hash_end(sh)[0]) * new_hash_size);
for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
if (pr->atom != JS_ATOM_NULL) {
// 先获取shape的prop的atom,然后和 new_hash_mash 做与操作得到 h
// 和new_hash_mask进行"与"操作的目的使得到的值小于 new_hash_mash
h = ((uintptr_t)pr->atom & new_hash_mask);
// 将JSShape前面一段内存作为数组,以 -h-1 为索引获取指定位置的值,赋值给当前prop的hash_next
pr->hash_next = prop_hash_end(sh)[-h - 1];
// 将上述索引指向的位置赋值为 i+1
prop_hash_end(sh)[-h - 1] = i + 1;
}
}
// 释放无用的shape
js_free(ctx, get_alloc_from_shape(old_sh));
} else {
/* only resize the properties */
list_del(&sh->header.link);
sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
get_shape_size(new_hash_size, new_size));
if (unlikely(!sh_alloc)) {
/* insert again in the GC list */
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
return -1;
}
sh = get_shape_from_alloc(sh_alloc, new_hash_size);
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
}
*psh = sh;
sh->prop_size = new_size;
return 0;
}
JSShape添加属性
// 给shape添加属性
static int add_shape_property(JSContext *ctx, JSShape **psh,
JSObject *p, JSAtom atom, int prop_flags)
{
JSRuntime *rt = ctx->rt;
JSShape *sh = *psh;
JSShapeProperty *pr, *prop;
uint32_t hash_mask, new_shape_hash = 0;
intptr_t h;
/* update the shape hash */
if (sh->is_hashed) {
js_shape_hash_unlink(rt, sh);
new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
}
// 判断是否需要重新申请属性数组,如果不够,shape->prop将会被扩展
if (unlikely(sh->prop_count >= sh->prop_size)) {
if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
/* in case of error, reinsert in the hash table.
sh is still valid if resize_properties() failed */
if (sh->is_hashed)
js_shape_hash_link(rt, sh);
return -1;
}
sh = *psh;
}
if (sh->is_hashed) {
sh->hash = new_shape_hash;
js_shape_hash_link(rt, sh);
}
/* Initialize the new shape property.
The object property at p->prop[sh->prop_count] is uninitialized */
prop = get_shape_prop(sh);
pr = &prop[sh->prop_count++];
pr->atom = JS_DupAtom(ctx, atom);
pr->flags = prop_flags;
sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
/* add in hash table */
hash_mask = sh->prop_hash_mask;
// 根据属性名称的 atom 和 hash_mask 计算 h
h = atom & hash_mask;
// 建立属性的hash链,注意,JSShapeProperty->hash_next 存储的是指向 JSShape->prop 的索引
pr->hash_next = prop_hash_end(sh)[-h - 1];
// 将属性计数放入JSShape首地址的 -h-1 处
prop_hash_end(sh)[-h - 1] = sh->prop_count;
return 0;
}
JSShape查找属性
static force_inline JSShapeProperty *find_own_property1(JSObject *p,
JSAtom atom)
{
JSShape *sh;
JSShapeProperty *pr, *prop;
intptr_t h;
sh = p->shape;
// 根据属性名称的atom和prop_hash_mask 计算 h
h = (uintptr_t)atom & sh->prop_hash_mask;
// 根据h,获取属性在JSShape->prop中的索引
h = prop_hash_end(sh)[-h - 1];
// 获取JSShape->prop
prop = get_shape_prop(sh);
while (h) {
// 根据索引,获取指定的JSShapeProperty
pr = &prop[h - 1];
// 判断JSShapeProperty的atom是不是和目标atom相等
if (likely(pr->atom == atom)) {
// 如果相等,则返回目标JSShapeProperty
return pr;
}
//不想等就继续遍历
h = pr->hash_next;
}
return NULL;
}
// 和上面类似,参数多了一个ppr,将获取的属性保存在ppr中
static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
JSObject *p,
JSAtom atom)
{
JSShape *sh;
JSShapeProperty *pr, *prop;
intptr_t h;
sh = p->shape;
h = (uintptr_t)atom & sh->prop_hash_mask;
h = prop_hash_end(sh)[-h - 1];
prop = get_shape_prop(sh);
while (h) {
pr = &prop[h - 1];
if (likely(pr->atom == atom)) {
*ppr = &p->prop[h - 1];
/* the compiler should be able to assume that pr != NULL here */
return pr;
}
h = pr->hash_next;
}
*ppr = NULL;
return NULL;
}
JSShape删除属性
static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
{
JSShape *sh;
JSShapeProperty *pr, *lpr, *prop;
JSProperty *pr1;
uint32_t lpr_idx;
intptr_t h, h1;
redo:
sh = p->shape;
h1 = atom & sh->prop_hash_mask;
h = prop_hash_end(sh)[-h1 - 1];
prop = get_shape_prop(sh);
lpr = NULL;
lpr_idx = 0; /* prevent warning */
while (h != 0) {
pr = &prop[h - 1];
if (likely(pr->atom == atom)) {
/* found ! */
if (!(pr->flags & JS_PROP_CONFIGURABLE))
return FALSE;
/* realloc the shape if needed */
if (lpr)
lpr_idx = lpr - get_shape_prop(sh);
if (js_shape_prepare_update(ctx, p, &pr))
return -1;
sh = p->shape;
/* remove property */
if (lpr) {
lpr = get_shape_prop(sh) + lpr_idx;
lpr->hash_next = pr->hash_next;
} else {
prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
}
sh->deleted_prop_count++;
/* free the entry */
pr1 = &p->prop[h - 1];
free_property(ctx->rt, pr1, pr->flags);
JS_FreeAtom(ctx, pr->atom);
/* put default values */
pr->flags = 0;
pr->atom = JS_ATOM_NULL;
pr1->u.value = JS_UNDEFINED;
/* compact the properties if too many deleted properties */
if (sh->deleted_prop_count >= 8 &&
sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
compact_properties(ctx, p);
}
return TRUE;
}
lpr = pr;
h = pr->hash_next;
}
if (p->is_exotic) {
if (p->fast_array) {
uint32_t idx;
if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
idx < p->u.array.count) {
if (p->class_id == JS_CLASS_ARRAY ||
p->class_id == JS_CLASS_ARGUMENTS) {
/* Special case deleting the last element of a fast Array */
if (idx == p->u.array.count - 1) {
JS_FreeValue(ctx, p->u.array.u.values[idx]);
p->u.array.count = idx;
return TRUE;
}
if (convert_fast_array_to_array(ctx, p))
return -1;
goto redo;
} else {
return FALSE;
}
}
} else {
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
if (em && em->delete_property) {
return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
}
}
}
/* not found */
return TRUE;
}
/* remove the deleted properties. */
static int compact_properties(JSContext *ctx, JSObject *p)
{
JSShape *sh, *old_sh;
void *sh_alloc;
intptr_t h;
uint32_t new_hash_size, i, j, new_hash_mask, new_size;
JSShapeProperty *old_pr, *pr;
JSProperty *prop, *new_prop;
sh = p->shape;
assert(!sh->is_hashed);
new_size = max_int(JS_PROP_INITIAL_SIZE,
sh->prop_count - sh->deleted_prop_count);
assert(new_size <= sh->prop_size);
new_hash_size = sh->prop_hash_mask + 1;
while ((new_hash_size / 2) >= new_size)
new_hash_size = new_hash_size / 2;
new_hash_mask = new_hash_size - 1;
/* resize the hash table and the properties */
old_sh = sh;
sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
if (!sh_alloc)
return -1;
sh = get_shape_from_alloc(sh_alloc, new_hash_size);
list_del(&old_sh->header.link);
memcpy(sh, old_sh, sizeof(JSShape));
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
memset(prop_hash_end(sh) - new_hash_size, 0,
sizeof(prop_hash_end(sh)[0]) * new_hash_size);
j = 0;
old_pr = old_sh->prop;
pr = sh->prop;
prop = p->prop;
for(i = 0; i < sh->prop_count; i++) {
if (old_pr->atom != JS_ATOM_NULL) {
pr->atom = old_pr->atom;
pr->flags = old_pr->flags;
h = ((uintptr_t)old_pr->atom & new_hash_mask);
pr->hash_next = prop_hash_end(sh)[-h - 1];
prop_hash_end(sh)[-h - 1] = j + 1;
prop[j] = prop[i];
j++;
pr++;
}
old_pr++;
}
assert(j == (sh->prop_count - sh->deleted_prop_count));
sh->prop_hash_mask = new_hash_mask;
sh->prop_size = new_size;
sh->deleted_prop_count = 0;
sh->prop_count = j;
p->shape = sh;
js_free(ctx, get_alloc_from_shape(old_sh));
/* reduce the size of the object properties */
new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
if (new_prop)
p->prop = new_prop;
return 0;
}
查找JSShape
/* find a hashed empty shape matching the prototype. Return NULL if
not found
从rt->shape_hash中寻找与proto相同的JSShape
*/
static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
{
JSShape *sh1;
uint32_t h, h1;
// 获取shape初始hash
h = shape_initial_hash(proto);
// 将hash截断,保证得到的hash小于 rt->shape_hash_bits
h1 = get_shape_hash(h, rt->shape_hash_bits);
// 首先以h1作为索引,从 rt->shape_hash 中获取一个JSShape
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
// 因为可能存在多个JSShape具有相同的hash,判断下是否是同一个
if (sh1->hash == h &&
sh1->proto == proto &&
sh1->prop_count == 0) {
return sh1;
}
//如果不是,根据JSShape的链表, 遍历下一个
}
return NULL;
}
/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
not found */
// 查找存在与给定属性的名称和属性标识都相同的shape
static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
JSAtom atom, int prop_flags)
{
JSShape *sh1;
uint32_t h, h1, i, n;
h = sh->hash;
h = shape_hash(h, atom);
h = shape_hash(h, prop_flags);
h1 = get_shape_hash(h, rt->shape_hash_bits);
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
/* we test the hash first so that the rest is done only if the
shapes really match */
if (sh1->hash == h &&
sh1->proto == sh->proto &&
sh1->prop_count == ((n = sh->prop_count) + 1)) {
for(i = 0; i < n; i++) {
if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
unlikely(sh1->prop[i].flags != sh->prop[i].flags))
goto next;
}
if (unlikely(sh1->prop[n].atom != atom) ||
unlikely(sh1->prop[n].flags != prop_flags))
goto next;
return sh1;
}
next: ;
}
return NULL;
}
从 rt->shape_hash 中查找出包含给定的属性名称和标识的JSShape。两个函数,分别使用原型对象和对象属性的维度去查询想要的JSShape。
JSObject与JSShape
//使用给定shape创建对象
static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
{
JSObject *p;
js_trigger_gc(ctx->rt, sizeof(JSObject));
//1. 申请内存
p = js_malloc(ctx, sizeof(JSObject));
if (unlikely(!p))
goto fail;
p->class_id = class_id;
p->extensible = TRUE;
p->free_mark = 0;
p->is_exotic = 0;
p->fast_array = 0;
p->is_constructor = 0;
p->is_uncatchable_error = 0;
p->tmp_mark = 0;
p->is_HTMLDDA = 0;
p->first_weak_ref = NULL;
p->u.opaque = NULL;
p->shape = sh;
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
if (unlikely(!p->prop)) {
js_free(ctx, p);
fail:
js_free_shape(ctx->rt, sh);
return JS_EXCEPTION;
}
switch(class_id) {
case JS_CLASS_OBJECT:
break;
case JS_CLASS_ARRAY:
{
JSProperty *pr;
p->is_exotic = 1;
p->fast_array = 1;
p->u.array.u.values = NULL;
p->u.array.count = 0;
p->u.array.u1.size = 0;
/* the length property is always the first one */
if (likely(sh == ctx->array_shape)) {
pr = &p->prop[0];
} else {
/* only used for the first array */
/* cannot fail */
pr = add_property(ctx, p, JS_ATOM_length,
JS_PROP_WRITABLE | JS_PROP_LENGTH);
}
pr->u.value = JS_NewInt32(ctx, 0);
}
break;
case JS_CLASS_C_FUNCTION:
p->prop[0].u.value = JS_UNDEFINED;
break;
case JS_CLASS_ARGUMENTS:
case JS_CLASS_UINT8C_ARRAY:
case JS_CLASS_INT8_ARRAY:
case JS_CLASS_UINT8_ARRAY:
case JS_CLASS_INT16_ARRAY:
case JS_CLASS_UINT16_ARRAY:
case JS_CLASS_INT32_ARRAY:
case JS_CLASS_UINT32_ARRAY:
#ifdef CONFIG_BIGNUM
case JS_CLASS_BIG_INT64_ARRAY:
case JS_CLASS_BIG_UINT64_ARRAY:
#endif
case JS_CLASS_FLOAT32_ARRAY:
case JS_CLASS_FLOAT64_ARRAY:
p->is_exotic = 1;
p->fast_array = 1;
p->u.array.u.ptr = NULL;
p->u.array.count = 0;
break;
case JS_CLASS_DATAVIEW:
p->u.array.u.ptr = NULL;
p->u.array.count = 0;
break;
case JS_CLASS_NUMBER:
case JS_CLASS_STRING:
case JS_CLASS_BOOLEAN:
case JS_CLASS_SYMBOL:
case JS_CLASS_DATE:
#ifdef CONFIG_BIGNUM
case JS_CLASS_BIG_INT:
case JS_CLASS_BIG_FLOAT:
case JS_CLASS_BIG_DECIMAL:
#endif
p->u.object_data = JS_UNDEFINED;
goto set_exotic;
case JS_CLASS_REGEXP:
p->u.regexp.pattern = NULL;
p->u.regexp.bytecode = NULL;
goto set_exotic;
default:
set_exotic:
if (ctx->rt->class_array[class_id].exotic) {
p->is_exotic = 1;
}
break;
}
p->header.ref_count = 1;
add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
return JS_MKPTR(JS_TAG_OBJECT, p);
}
// 将JSValue转化成JSObject
static JSObject *get_proto_obj(JSValueConst proto_val)
{
if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
return NULL;
else
return JS_VALUE_GET_OBJ(proto_val);
}
/* WARNING: proto must be an object or JS_NULL */
// 使用给定原型创建对象
JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
JSClassID class_id)
{
JSShape *sh;
JSObject *proto;
// 将JSValue转化成JSObject
proto = get_proto_obj(proto_val);
//
sh = find_hashed_shape_proto(ctx->rt, proto);
if (likely(sh)) {
sh = js_dup_shape(sh);
} else {
sh = js_new_shape(ctx, proto);
if (!sh)
return JS_EXCEPTION;
}
return JS_NewObjectFromShape(ctx, sh, class_id);
}
JSShape像是JSObject的模板。JSShape保存着JSObject的属性信息。每一个JSObject都持有一个JSShape,反之则不然。同时,JSShape持有着JSObject的原型对象。 JavaScript的继承属性便是由JSShape持有的原型对象实现的。