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类。