前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >js引擎v8源码解析之对象第一篇(基于v8 0.1.5)

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

作者头像
theanarkh
发布2019-11-23 20:51:27
9080
发布2019-11-23 20:51:27
举报
文章被收录于专栏:原创分享

v8的对象基类是Object。我们先看一下他的类定义。下面只列出重要的函数。

代码语言:javascript
复制
// 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是表示小整形。我们看他的定义。

代码语言:javascript
复制
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的实现。主要有两个函数。

代码语言:javascript
复制
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类是表示他的对象是在堆中分配内存的。下面是类定义。

代码语言:javascript
复制
// 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类的对象的内存布局。

代码语言:javascript
复制
 static const int kMapOffset = Object::kSize; // 0
 static const int kSize = kMapOffset + kPointerSize; // kPointerSize表示一个指针变量的大小

下面我们开始HeapObject的实现。从之前的分析我们知道,v8很多对象的属性不是和传统的C++那样,直接定义一个类型的。而且通过给属性分配字节数去控制的。所以分析之前我们要先了解一个东西,就是如何读写对象的一个属性。

代码语言:javascript
复制
// 获取对象某个属性的地址,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的实现。

代码语言:javascript
复制
// 堆对象的开始地址是一个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属性在所有对象中都是在第一个位置。

代码语言:javascript
复制
// 封装过的地址,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类。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档