[領域]javascript hacking guide 第5部分

mapJSObject的一個重要屬性,存放一個對象的所有的屬性的入口。要想了解map,就需要打開jsobj.h文件,看裏面的定義。

struct JSObject {

    JSObjectMap *map;

    jsval       *slots;

};

 

很自然的,我們還要找到JSObjectMap的定義,它也在jsobj.h文件中

struct JSObjectMap {

    jsrefcount  nrefs;          /* count of all referencing objects */

    JSObjectOps *ops;           /* high level object operation vtable */

    uint32      nslots;         /* length of obj->slots vector */

    uint32      freeslot;       /* index of next free obj->slots element */

};

在這個定義中,最重要的信息隱含在註釋中,第一個是對ops的註釋,我們會看到ops其實是一個virtual table。這表示,對象的所有的屬性名,都存放在這個vtable中;第2個信息是對nslots的註釋,它提及obj->slots其實是一個vector

這如同在告訴我們,我們在定義一個對象的屬性時,屬性的名稱和類型等信息,會通過ops增加到屬性表中,屬性的值會對應放到slots這個vector中。例如:

var obj = new Object();

obj.a = 5;

 那麼“a”這個屬性的類型是整型的,slots中增加了個新的值5。仔細看去,opsJSObjectOps這個結構體中,我們可以在jsapi.h中找到這個結構體的定義:

struct JSObjectOps {

    /* Mandatory non-null function pointer members. */

    JSNewObjectMapOp    newObjectMap;

    JSObjectMapOp       destroyObjectMap;

    JSLookupPropOp      lookupProperty;

    JSDefinePropOp      defineProperty;

    JSPropertyIdOp      getProperty;

    JSPropertyIdOp      setProperty;

    JSAttributesOp      getAttributes;

    JSAttributesOp      setAttributes;

    JSPropertyIdOp      deleteProperty;

    JSConvertOp         defaultValue;

    JSNewEnumerateOp    enumerate;

    JSCheckAccessIdOp   checkAccess;

 

    /* Optionally non-null members start here. */

    JSObjectOp          thisObject;

    JSPropertyRefOp     dropProperty;

    JSNative            call;

    JSNative            construct;

    JSXDRObjectOp       xdrObject;

    JSHasInstanceOp     hasInstance;

    JSSetObjectSlotOp   setProto;

    JSSetObjectSlotOp   setParent;

    JSMarkOp            mark;

    JSFinalizeOp        clear;

    JSGetRequiredSlotOp getRequiredSlot;

    JSSetRequiredSlotOp setRequiredSlot;

};

 

我們可以簡單的瀏覽一下其中的函數指針名稱,創建objectmap,定義屬性,刪除屬性,給屬性設置值或者獲取屬性值。。。。閉上眼睛想一想,這其中的函數指針是如此之多,所以一個對象的屬性的類型判斷(比如是整型還是String還是其他的)或者是屬性名或者屬性值的存放,都是通過預先定義好的指針函數來完成。

我們甚至可以用一個新的示意圖來表示這一切:

 

我們可以這樣去想,有一個table,裏面存放對象的屬性列表。根據ruby hacking guide的說法,我們可以想像,有一張大的表,存放所有的對象的屬性,也可能每個對象有自己的屬性表。但,這屬於具體實現的範疇,我們只要明白每個對象有屬於自己的屬性表,對錶格中的行或者列進行操作時,是由JSObjectOps這個結構體中的指針函數來完成的就可以了。

/*

     * Share proto's map only if it has the same JSObjectOps, and only if

     * proto's class has the same private and reserved slots as obj's map

     * and class have.  We assume that if prototype and object are of the

     * same class, they always have the same number of computed reserved

     * slots (returned via clasp->reserveSlots); otherwise, prototype and

     * object classes must have the same (null or not) reserveSlots hook.

     */

    if (proto &&

        (map = proto->map)->ops == ops &&

        ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||

         (!((protoclasp->flags ^ clasp->flags) &

            (JSCLASS_HAS_PRIVATE |

             (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&

          protoclasp->reserveSlots == clasp->reserveSlots)))

    {

        /*

         * Default parent to the parent of the prototype, which was set from

         * the parent of the prototype's constructor.

         */

        if (!parent)

            parent = OBJ_GET_PARENT(cx, proto);

 

        /* Share the given prototype's map. */

        obj->map = js_HoldObjectMap(cx, map);

 

        /* Ensure that obj starts with the minimum slots for clasp. */

        nslots = JS_INITIAL_NSLOTS;

    } else {

        /* Leave parent alone.  Allocate a new map for obj. */

        map = ops->newObjectMap(cx, 1, ops, clasp, obj);

        if (!map)

            goto bad;

        obj->map = map;

 

        /* Let ops->newObjectMap set nslots so as to reserve slots. */

        nslots = map->nslots;

    }

Ok,我們在上回書說到有若干的判斷條件,就是爲了創建一個map,並設置爲obj->map的屬性――不管是用parent的原型來創建,還是單獨創建新的mapif 。。。else 部分),最終都是爲了這個目的。

 

/* Allocate a slots vector, with a -1'st element telling its length. */

    newslots = AllocSlots(cx, NULL, nslots);

    if (!newslots) {

        js_DropObjectMap(cx, obj->map, obj);

        obj->map = NULL;

        goto bad;

    }

 

    /* Set the proto, parent, and class properties. */

    newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);

    newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);

    newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);

 

    /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */

    for (i = JSSLOT_CLASS + 1; i < nslots; i++)

        newslots[i] = JSVAL_VOID;

 

    /* Store newslots after initializing all of 'em, just in case. */

    obj->slots = newslots;

再看newObject中剩餘的代碼,真是容易多了,既然創建了map來存放屬性名了,那麼我們就要對應屬性名來存放存放初始的屬性值了。我們可以看到,每個創建的GC對象,都會被預先設置3個內置的屬性,protoparentclass。然後,對剩餘的slot,會設置undefinedJSVal_VOID會被解釋成Undefined)。

 

到現在,我們的Global內置對象就已經初步創建完成了。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章