js引擎v8源码解析之对象第二篇(基于v8 0.1.5)

  • 2019 年 11 月 23 日
  • 筆記

继续分析类。首先是HeadNumber。

1 HeadNumber

HeadNumber类的代码比较少。

// The HeapNumber class describes heap allocated numbers that cannot be  // represented in a Smi (small integer)  // 存储了数字的堆对象  class HeapNumber: public HeapObject {   public:    // [value]: number value.    inline double value();    inline void set_value(double value);      // Casting.    static inline HeapNumber* cast(Object* obj);      // Dispatched behavior.    Object* HeapNumberToBoolean();      // Layout description.    // kSize之前的空间存储map对象的指针    static const int kValueOffset = HeapObject::kSize;    // kValueOffset - kSize之间存储数字的值    static const int kSize = kValueOffset + kDoubleSize;     private:    DISALLOW_IMPLICIT_CONSTRUCTORS(HeapNumber);  };

我们看看他的实现。

// 读出double类型的值  #define READ_DOUBLE_FIELD(p, offset)     (*reinterpret_cast<double*>(FIELD_ADDR(p, offset)))    // 写入double类型的值  #define WRITE_DOUBLE_FIELD(p, offset, value)     (*reinterpret_cast<double*>(FIELD_ADDR(p, offset)) = value)    // 读写属性的值  double HeapNumber::value() {    return READ_DOUBLE_FIELD(this, kValueOffset);  }    // 写double值到对象  void HeapNumber::set_value(double value) {    WRITE_DOUBLE_FIELD(this, kValueOffset, value);  }    Object* HeapNumber::HeapNumberToBoolean() {    // NaN, +0, and -0 should return the false object    switch (fpclassify(value())) {      case FP_NAN:  // fall through      case FP_ZERO: return Heap::false_value();      default: return Heap::true_value();    }  }

还有一个函数就是cast,实现如下:

CAST_ACCESSOR(HeapNumber)  #define CAST_ACCESSOR(type)                         type* type::cast(Object* object) {                  ASSERT(object->Is##type());                       return reinterpret_cast<type*>(object);         CAST_ACCESSOR(HeapNumber);  宏展开后  HeapNumber* HeapNumber::cast(Object* object) {                  ASSERT(object->IsHeapNumber());                       return reinterpret_cast<HeapNumber*>(object);     

至此HeapNumber分析完了。接着看下一个类Array。

2 Array

// Abstract super class arrays. It provides length behavior.  class Array: public HeapObject {   public:    // [length]: length of the array.    inline int length();    inline void set_length(int value);      // Convert an object to an array index.    // Returns true if the conversion succeeded.    static inline bool IndexFromObject(Object* object, uint32_t* index);      // Layout descriptor.    static const int kLengthOffset = HeapObject::kSize;    static const int kHeaderSize = kLengthOffset + kIntSize;     private:    DISALLOW_IMPLICIT_CONSTRUCTORS(Array);  };

我们发现数组的对象内存布局中,只有一个属性。就是保存length大小的。首先看看读写length属性的实现。

#define INT_ACCESSORS(holder, name, offset)                                 int holder::name() { return READ_INT_FIELD(this, offset); }               void holder::set_##name(int value) { WRITE_INT_FIELD(this, offset, value); }    // 定义数组的length和set_length函数,属性在对象的偏移的kLengthOffset,紧跟着map指针  INT_ACCESSORS(Array, length, kLengthOffset);

再继续看IndexFromObject的实现。

bool Array::IndexFromObject(Object* object, uint32_t* index) {    if (object->IsSmi()) {      int value = Smi::cast(object)->value();      if (value < 0) return false;      *index = value;      return true;    }    if (object->IsHeapNumber()) {      double value = HeapNumber::cast(object)->value();      uint32_t uint_value = static_cast<uint32_t>(value);      if (value == static_cast<double>(uint_value)) {        *index = uint_value;        return true;      }    }    return false;  }

该函数就是把一个对象(底层是表示数字的)转成一个数组索引。数组类也分析完了。我们继续。

3 ByteArray

// ByteArray represents fixed sized byte arrays.  Used by the outside world,  // such as PCRE, and also by the memory allocator and garbage collector to  // fill in free blocks in the heap.  class ByteArray: public Array {   public:    // Setter and getter.    inline byte get(int index);    inline void set(int index, byte value);      // Treat contents as an int array.    inline int get_int(int index);    /*      ByteArray类没有定义自己的属性,他是根据length算出对象的大小,      然后在分配内存的时候,多分配一块存储数组元素的内存      const int kObjectAlignmentBits = 2;      const int kObjectAlignmentMask = (1 << kObjectAlignmentBits) - 1;      #define OBJECT_SIZE_ALIGN(value)  ((value + kObjectAlignmentMask) & ~kObjectAlignmentMask)      由此可知,按四个字节对齐。OBJECT_SIZE_ALIGN的作用的是不够4字节的,会多分配几个字节,使得按四字节对齐。~kObjectAlignmentMask是低两位是0,即按四字节对齐。比如value已经4字节对齐了,则(4 + 0 +3) & ~3 =4,如果value没有对齐,假设是5,则(4 + 1 +3) & ~3 = 8;如果value等于6,(4 + 2 + 3) & ~3 = 8;以此类推。    */    static int SizeFor(int length) {      return kHeaderSize + OBJECT_SIZE_ALIGN(length);    }    // We use byte arrays for free blocks in the heap.  Given a desired size in    // bytes that is a multiple of the word size and big enough to hold a byte    // array, this function returns the number of elements a byte array should    // have.    static int LengthFor(int size_in_bytes) {      ASSERT(IsAligned(size_in_bytes, kPointerSize));      ASSERT(size_in_bytes >= kHeaderSize);      return size_in_bytes - kHeaderSize;    }      // Returns data start address.    inline Address GetDataStartAddress();      // Returns a pointer to the ByteArray object for a given data start address.    static inline ByteArray* FromDataStartAddress(Address address);      // Casting.    static inline ByteArray* cast(Object* obj);      // Dispatched behavior.    int ByteArraySize() { return SizeFor(length()); }     private:    DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArray);  };

在分析实现之前我们先看一下ByteArray的对象是怎么被分配的。

Handle<ByteArray> Factory::NewByteArray(int length) {    ASSERT(0 <= length);    CALL_HEAP_FUNCTION(Heap::AllocateByteArray(length), ByteArray);  }    Object* Heap::AllocateByteArray(int length) {    int size = ByteArray::SizeFor(length);    AllocationSpace space = size > MaxHeapObjectSize() ? LO_SPACE : NEW_SPACE;      Object* result = AllocateRaw(size, space);    if (result->IsFailure()) return result;      reinterpret_cast<Array*>(result)->set_map(byte_array_map());    reinterpret_cast<Array*>(result)->set_length(length);    return result;  }

我们看到,首先通过ByteArray::SizeFor算出对象所需的内存大小size。然后分配一块大小为size的内存。然后返回这块内存的地址。这时候我们就可以使用这块内存。我们看看怎么使用的。内存布局如下。

byte ByteArray::get(int index) {    ASSERT(index >= 0 && index < this->length());    // 根据索引返回数组中对应元素的值,kHeaderSize是第一个元素的地址,kCharSize是1,即一个字节    return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);  }      void ByteArray::set(int index, byte value) {    ASSERT(index >= 0 && index < this->length());    WRITE_BYTE_FIELD(this, kHeaderSize + index * kCharSize, value);  }    // 把四个元素(四个字节)的内容作为一个值。即ByteArray变成IntArray  int ByteArray::get_int(int index) {    ASSERT(index >= 0 && (index * kIntSize) < this->length());    return READ_INT_FIELD(this, kHeaderSize + index * kIntSize);  }      ByteArray* ByteArray::FromDataStartAddress(Address address) {    ASSERT_TAG_ALIGNED(address);    return reinterpret_cast<ByteArray*>(address - kHeaderSize + kHeapObjectTag);  }    // 返回数组元素的首地址,地址的低位是用作标记,要先减掉。kHeaderSize是第一个元素在对象内存空间的偏移  Address ByteArray::GetDataStartAddress() {    /*      typedef uint8_t byte;      typedef byte* Address;    */    return reinterpret_cast<Address>(this) - kHeapObjectTag + kHeaderSize;  }