js引擎v8源碼解析之對象第一篇(基於v8 0.1.5)

  • 2019 年 11 月 23 日
  • 筆記

v8的對象基類是Object。我們先看一下他的類定義。下面只列出重要的函數。

// Object is the abstract superclass for all classes in the  // object hierarchy.  // Object does not use any virtual functions to avoid the  // allocation of the C++ vtable.  // Since Smi and Failure are subclasses of Object no  // data members can be present in Object.  class Object BASE_EMBEDDED {   public:    // Type testing.    inline bool IsSmi();    // 下面是一些列isXX的函數    // Extract the number.    inline double Number();      Object* ToObject();             // ECMA-262 9.9.    Object* ToBoolean();            // ECMA-262 9.2.      // Convert to a JSObject if needed.    // global_context is used when creating wrapper object.    Object* ToObject(Context* global_context);      // Converts this to a Smi if possible.    // Failure is returned otherwise.    inline Object* ToSmi();      void Lookup(String* name, LookupResult* result);      // Property access.    inline Object* GetProperty(String* key);    inline Object* GetProperty(String* key, PropertyAttributes* attributes);    Object* GetPropertyWithReceiver(Object* receiver,                                    String* key,                                    PropertyAttributes* attributes);    Object* GetProperty(Object* receiver,                        LookupResult* result,                        String* key,                        PropertyAttributes* attributes);    Object* GetPropertyWithCallback(Object* receiver,                                    Object* structure,                                    String* name,                                    Object* holder);      inline Object* GetElement(uint32_t index);    Object* GetElementWithReceiver(Object* receiver, uint32_t index);      // Return the object's prototype (might be Heap::null_value()).    Object* GetPrototype();      // Returns true if this is a JSValue containing a string and the index is    // < the length of the string.  Used to implement [] on strings.    inline bool IsStringObjectWithCharacterAt(uint32_t index);      // Casting: This cast is only needed to satisfy macros in objects-inl.h.    static Object* cast(Object* value) { return value; }      // Layout description.    static const int kSize = 0;  // Object does not take up any space.     private:    // 禁止對象在堆中創建    /*        宏展開是        Object();        Object(const TypeName&);        void operator=(const Object&)    */    DISALLOW_IMPLICIT_CONSTRUCTORS(Object);  };

我們看到類中有一個靜態屬性kSize,這個屬性是標記該類的對象,屬性需要佔據的記憶體位元組大小。下面我們看第一個繼承於Object的類Smi。Smi是表示小整形。我們看他的定義。

class Smi: public Object {   public:    // Returns the integer value.    inline int value();      // Convert a value to a Smi object.    static inline Smi* FromInt(int value);      // Returns whether value can be represented in a Smi.    static inline bool IsValid(int value);      // Casting.    static inline Smi* cast(Object* object);    // Min and max limits for Smi values.    static const int kMinValue = -(1 << (kBitsPerPointer - (kSmiTagSize + 1)));    static const int kMaxValue = (1 << (kBitsPerPointer - (kSmiTagSize + 1))) - 1;     private:    DISALLOW_IMPLICIT_CONSTRUCTORS(Smi);  };

Smi的實現。主要有兩個函數。

int Smi::value() {    return reinterpret_cast<int>(this) >> kSmiTagSize;  }      Smi* Smi::FromInt(int value) {    ASSERT(Smi::IsValid(value));    // kSmiTagSize是類型標記,表示是小整形。值是1.kSmiTag是0    return reinterpret_cast<Smi*>((value << kSmiTagSize) | kSmiTag);  }

我們看到Smi的實現比較簡單。我們接著看繼承關係中的下一個類HeapObject。HeapObject類是表示他的對象是在堆中分配記憶體的。下面是類定義。

// HeapObject is the superclass for all classes describing heap allocated  // objects.  class HeapObject: public Object {   public:    // [map]: contains a Map which contains the objects reflective information.    inline Map* map();    inline void set_map(Map* value);      // Converts an address to a HeapObject pointer.    // 對象的地址+對象標記    static inline HeapObject* FromAddress(Address address);      // Returns the address of this HeapObject.    // 對象的真正地址    inline Address address();      // Iterates over pointers contained in the object (including the Map)    void Iterate(ObjectVisitor* v);      // Iterates over all pointers contained in the object except the    // first map pointer.  The object type is given in the first    // parameter. This function does not access the map pointer in the    // object, and so is safe to call while the map pointer is modified.    void IterateBody(InstanceType type, int object_size, ObjectVisitor* v);      // This method only applies to struct objects.  Iterates over all the fields    // of this struct.    void IterateStructBody(int object_size, ObjectVisitor* v);      // Copy the body from the 'from' object to this.    // Please note the two object must have the same map prior to the call.    inline void CopyBody(JSObject* from);      // Returns the heap object's size in bytes    inline int Size();      // Given a heap object's map pointer, returns the heap size in bytes    // Useful when the map pointer field is used for other purposes.    // GC internal.    inline int SizeFromMap(Map* map);      static inline Object* GetHeapObjectField(HeapObject* obj, int index);      // Casting.    static inline HeapObject* cast(Object* obj);      // Dispatched behavior.    void HeapObjectShortPrint(StringStream* accumulator);      // Layout description.    // First field in a heap object is map.    static const int kMapOffset = Object::kSize;    static const int kSize = kMapOffset + kPointerSize;     protected:    // helpers for calling an ObjectVisitor to iterate over pointers in the    // half-open range [start, end) specified as integer offsets    inline void IteratePointers(ObjectVisitor* v, int start, int end);    // as above, for the single element at "offset"    inline void IteratePointer(ObjectVisitor* v, int offset);      // Computes the object size from the map.    // Should only be used from SizeFromMap.    int SlowSizeFromMap(Map* map);     private:    DISALLOW_IMPLICIT_CONSTRUCTORS(HeapObject);  };

我們先看一下HeapObject類的對象的記憶體布局。

 static const int kMapOffset = Object::kSize; // 0   static const int kSize = kMapOffset + kPointerSize; // kPointerSize表示一個指針變數的大小

下面我們開始HeapObject的實現。從之前的分析我們知道,v8很多對象的屬性不是和傳統的C++那樣,直接定義一個類型的。而且通過給屬性分配位元組數去控制的。所以分析之前我們要先了解一個東西,就是如何讀寫對象的一個屬性。

// 獲取對象某個屬性的地址,p是對象的首地址,offset是偏移,kHeapObjectTag是對象的標記,算地址的時候需要減掉  #define FIELD_ADDR(p, offset)     (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)    // 讀取對象中某個屬性的值,指向對象地址空間的某個地址,轉成對象指針  #define READ_FIELD(p, offset)     (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)))    // 給對象的某個屬性賦值  #define WRITE_FIELD(p, offset, value)     (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)

然後我們接著看HeapObject的實現。

// 堆對象的開始地址是一個Map對象  Map* HeapObject::map() {    return reinterpret_cast<Map*> READ_FIELD(this, kMapOffset);  }    // 設置堆對象的map對象  void HeapObject::set_map(Map* value) {    WRITE_FIELD(this, kMapOffset, value);  }

上面就是讀寫對象的某個屬性的例子(heapObject只有一個map屬性)。首先根據屬性在對象記憶體布局中的偏移找到屬性的地址,然後把他轉成Object對象(基類),然後把value寫進去,這裡是一個Map對象。讀取的時候也是先轉成Object對象。然後再轉成Map對象。map屬性在所有對象中都是在第一個位置。

// 封裝過的地址,kHeapObjectTag表示是一個堆對象  HeapObject* HeapObject::FromAddress(Address address) {    ASSERT_TAG_ALIGNED(address);    return reinterpret_cast<HeapObject*>(address + kHeapObjectTag);  }    // 對象的真正地址  Address HeapObject::address() {    return reinterpret_cast<Address>(this) - kHeapObjectTag;  }

上面是對對象地址的封裝,低一位表示類型。即堆對象。這篇先分析到這裡,下一篇分析完Map類後再繼續分析HeapObject類的實現,因為他用到了Map類。