[領域]javascript hacking guide part 4

孫向暉 sunshineormer at hotmail.com

在正式開始之前,先說點關於RHG的話題,我的JHG系列,基本是在仿照着RHG的風格在寫作。RHG的述事風格很有條理性,並且會指導你跳過跳過再跳過一些無關痛癢的程序片段。這是對的,在一大堆程序中,能夠快速準確的定位,不是件很容易的事情。

從今天開始,我將講述我對蜘蛛猴源代碼的查找過程:

在js.c 找到main裏面的調用過程,注意,下面的函數都是精簡過的,而且是平級的關係:

rt = JS_NewRuntime(...

cx = JS_NewContext(rt,...

glob = JS_NewObject(cx, &global_class, NULL, NULL);

JS_InitStandardClasses(cx, glob)

JS_DefineFunctions(cx, glob, shell_functions)

 

ok,現在來看一下,運行時和上下文環境是解釋引擎的初始化,可以先跳過,哈,我們真正關心的肯定是從glob開始的那3句了:globECMA262中要求的內置全局對象,這裏有我們比較感興趣的NewObject方法;緊接其後的,將是對其他內置對象的創建過程(JS_InitStandardClasses;最後,我們可以看看全局對象global都有哪些方法。

 

JS_NewObject的函數聲明,被定義在jsobj.h中,函數定義在jsobj.c中:

extern JSObject *

js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);

記住,因爲是要初始化glob對象,我們在傳遞參數是,protoparent都傳遞了NULL。這樣,有效傳遞到函數體中的,就只有剛剛創建的上下文環境(cx)和clasp了。clasp的值被賦予了global_class,這是個全局變量,定義在js.c

 

JSClass global_class = {

    "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,

    JS_PropertyStub,  JS_PropertyStub,

    JS_PropertyStub,  JS_PropertyStub,

    global_enumerate, (JSResolveOp) global_resolve,

    JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};

 

在這個變量中,我們發現了“global”這個單詞,結合JSClass這名字,再根據MFC等框架的經驗,很容易的我們就可以聯想到,這是用來做動態生成和RTTI的類名。

要想看懂這個結構體,一點也不難,只需到jsapi.h中找JSClass的定義即可。裏面也沒有什麼太多的玩意,只是有一堆的函數指針,我們只需簡單留言其中的強制非空部分即可。

 

struct JSClass {

    const char          *name;

    uint32              flags;

 

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

    JSPropertyOp        addProperty;

    JSPropertyOp        delProperty;

    JSPropertyOp        getProperty;

    JSPropertyOp        setProperty;

    JSEnumerateOp       enumerate;

    JSResolveOp         resolve;

    JSConvertOp         convert;

    JSFinalizeOp        finalize;

 

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

    JSGetObjectOps      getObjectOps;

    JSCheckAccessOp     checkAccess;

    JSNative            call;

    JSNative            construct;

    JSXDRObjectOp       xdrObject;

    JSHasInstanceOp     hasInstance;

    JSMarkOp            mark;

    JSReserveSlotsOp    reserveSlots;

};

放眼望去,偶的老天,原來類的屬性操作(增加屬性,刪除屬性,查詢和設置屬性都是在這定義的啊)。要知道,在javascript中,所有的attributes和方法都是屬性啊。按下不表。

還是跳回到newObject

 

/* Bootstrap the ur-object, and make it the default prototype object. */

    if (!proto) {

        if (!js_GetClassId(cx, clasp, &id))

            return NULL;

        if (!js_GetClassPrototype(cx, parent, id, &proto))

            return NULL;

        if (!proto &&

            !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),

                                  &proto)) {

            return NULL;

        }

    }

先看這段代碼,簡而言之,想做的事情是,找到要創建對象的原型,如果實在找不到,就使用缺省的原型對象。跳過。

接下來的代碼,

/* Always call the class's getObjectOps hook if it has one. */

    ops = clasp->getObjectOps

          ? clasp->getObjectOps(cx, clasp)

          : &js_ObjectOps;

只是想找一個對象的操作方法集,因爲我們在定義global_class全局對象時,使用了JSCLASS_NO_OPTIONAL_MEMBERS宏,可以很輕鬆的判定這個表達式如同:

ops = &js_ObjectOps;

 

繼續向後:

/*

     * Allocate a zeroed object from the GC heap.  Do this *after* any other

     * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps,

     * to avoid displacing the newborn root for obj.

     */

    obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));

    if (!obj)

        return NULL;

從註釋中,我們可以輕鬆的看出,按照ECMAScript的說法,對象被分配到heap中,已經得到了實現了。

被定義在了jsgc.c中,我們不想太多的去看堆的alloc分配,只要知道這裏生成了一個空的JSObject對象即可。

void *

js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)

再接着,是一大段的判斷條件,想法很簡單,既而是已經在heap中創建了對象,就要給對象的mapslot進行賦值了:一種做法是判斷了若干條件後,用原型的map;另一種可能,是創建新的map

 

你會問map裏面都存放了什麼?呵呵。好問題。且聽下回分解吧。

 

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