专栏首页原创分享js引擎v8源码分析之Object(基于v8 0.1.5)

js引擎v8源码分析之Object(基于v8 0.1.5)

Object是所有js对象在c++层的基类。

class Object BASE_EMBEDDED {
 public:

  inline bool IsSmi();
  inline bool IsHeapObject();
  inline bool IsHeapNumber();
  inline bool IsString();
  inline bool IsSeqString();
  inline bool IsAsciiString();
  inline bool IsTwoByteString();
  inline bool IsConsString();
  inline bool IsSlicedString();
  inline bool IsExternalString();
  inline bool IsExternalAsciiString();
  inline bool IsExternalTwoByteString();
  inline bool IsShortString();
  inline bool IsMediumString();
  inline bool IsLongString();
  inline bool IsSymbol();
  inline bool IsNumber();
  inline bool IsByteArray();
  inline bool IsFailure();
  inline bool IsRetryAfterGC();
  inline bool IsException();
  inline bool IsJSObject();
  inline bool IsMap();
  inline bool IsFixedArray();
  inline bool IsDescriptorArray();
  inline bool IsContext();
  inline bool IsGlobalContext();
  inline bool IsJSFunction();
  inline bool IsCode();
  inline bool IsOddball();
  inline bool IsSharedFunctionInfo();
  inline bool IsJSValue();
  inline bool IsProxy();
  inline bool IsBoolean();
  inline bool IsJSArray();
  inline bool IsHashTable();
  inline bool IsDictionary();
  inline bool IsSymbolTable();
  inline bool IsPrimitive();
  inline bool IsGlobalObject();
  inline bool IsJSGlobalObject();
  inline bool IsJSBuiltinsObject();
  inline bool IsUndetectableObject();
  inline bool IsAccessCheckNeeded();

  // Returns true if this object is an instance of the specified
  // function template.
  bool IsInstanceOf(FunctionTemplateInfo* type);

  inline bool IsStruct();
  inline bool IsAccessorInfo(); 
  inline bool IsAccessCheckInfo(); 
  inline bool IsInterceptorInfo(); 
  inline bool IsCallHandlerInfo(); 
  inline bool IsFunctionTemplateInfo(); 
  inline bool IsObjectTemplateInfo(); 
  inline bool IsSignatureInfo(); 
  inline bool IsTypeSwitchInfo(); 
  inline bool IsDebugInfo(); 
  inline bool IsBreakPointInfo(); 
  inline bool IsScript();

  // Oddball testing.
  INLINE(bool IsUndefined());
  INLINE(bool IsTheHole());
  INLINE(bool IsNull());
  INLINE(bool IsTrue());
  INLINE(bool IsFalse());

  // Extract the number.
  inline double Number();

  Object* ToObject();             // ECMA-262 9.9.
  Object* ToBoolean();            // ECMA-262 9.2.

  Object* ToObject(Context* global_context);

  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);

  Object* GetPrototype();

  inline bool IsStringObjectWithCharacterAt(uint32_t index);

  static Object* cast(Object* value) { return value; }

  // 对象在内存的布局,基类没有属性,内存是0
  static const int kSize = 0;  // Object does not take up any space.

 private:
  // 禁止直接创建对象,复制函数,赋值函数
  DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
};

基类主要是提供一些公共的方式,比如判断类型,属性存取。类型转化。

1 c++对象的类型 1 v8的对象是4字节对齐的,用地址的低两位出来标记对象的类型。 2 堆对象(HeapObject)是Object的子类。Object里面的很多方法都是用于堆对象。堆对象有自己的一套对象类型判断方式。每个堆对象有一个map属性,他记录了堆对象的类型type,大小size。 1 type主要是用于记录c++对象的类型。 2 有一些单例的map对象,也是用于判断c++对象的类型的。 3 map的type属性的低八位是用来区分是不是字符串类型的。高位8位是1说明不是字符串,是0说明是字符串类型。低7位记录字符串的子类型。 下面是定义。

// 第7位(从0开始算)如果是1说明对象不是字符串类型,否则是 
const uint32_t kIsNotStringMask = 0x80;
const uint32_t kStringTag = 0x0;
const uint32_t kNotStringTag = 0x80;

// If bit 7 is clear, bits 5 and 6 are the string's size (short, medium, or
// long).
// 对于字符串类型,5,6两位标记字符串的类型,短,中等,长三种类型
const uint32_t kStringSizeMask = 0x60;
const uint32_t kShortStringTag = 0x0;
const uint32_t kMediumStringTag = 0x20;
const uint32_t kLongStringTag = 0x40;

// If bit 7 is clear, bit 4 indicates that the string is a symbol (if set) or
// not (if cleared).
// 第四位标记字符串是不是symbol类型,1则表示是
const uint32_t kIsSymbolMask = 0x10;
const uint32_t kNotSymbolTag = 0x0;
const uint32_t kSymbolTag = 0x10;

// If bit 7 is clear, and the string representation is a sequential string,
// then bit 3 indicates whether the string consists of two-byte characters or
// one-byte characters.
// 第三位表示字符串是单字节字符还是双字节字符组成的
const uint32_t kStringEncodingMask = 0x8;
const uint32_t kTwoByteStringTag = 0x0;
const uint32_t kAsciiStringTag = 0x8;

// If bit 7 is clear, the low-order 3 bits indicate the representation
// of the string.
// 第0,1,2位表示字符串类型,四种
const uint32_t kStringRepresentationMask = 0x07;
enum StringRepresentationTag {
  kSeqStringTag = 0x0,
  kConsStringTag = 0x1,
  kSlicedStringTag = 0x2,
  kExternalStringTag = 0x3
};

enum InstanceType {
  // 下面都是字符串类型,根据上面的定义,下面的tag标记在8比特上各有自己的位置范围,所以相与的结果不会一样
  SHORT_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSeqStringTag,
  MEDIUM_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSeqStringTag,
  LONG_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kSeqStringTag,
  SHORT_ASCII_SYMBOL_TYPE = kShortStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
  MEDIUM_ASCII_SYMBOL_TYPE = kMediumStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
  LONG_ASCII_SYMBOL_TYPE = kLongStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
  SHORT_CONS_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kConsStringTag,
  MEDIUM_CONS_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kConsStringTag,
  LONG_CONS_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kConsStringTag,
  SHORT_CONS_ASCII_SYMBOL_TYPE = kShortStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
  MEDIUM_CONS_ASCII_SYMBOL_TYPE = kMediumStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
  LONG_CONS_ASCII_SYMBOL_TYPE = kLongStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
  SHORT_SLICED_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSlicedStringTag,
  MEDIUM_SLICED_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSlicedStringTag,
  LONG_SLICED_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kSlicedStringTag,
  SHORT_SLICED_ASCII_SYMBOL_TYPE = kShortStringTag | kAsciiStringTag | kSymbolTag | kSlicedStringTag,
  MEDIUM_SLICED_ASCII_SYMBOL_TYPE = kMediumStringTag | kAsciiStringTag | kSymbolTag | kSlicedStringTag,
  LONG_SLICED_ASCII_SYMBOL_TYPE = kLongStringTag | kAsciiStringTag | kSymbolTag | kSlicedStringTag,
  SHORT_EXTERNAL_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kExternalStringTag,
  MEDIUM_EXTERNAL_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kExternalStringTag,
  LONG_EXTERNAL_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kExternalStringTag,
  SHORT_EXTERNAL_ASCII_SYMBOL_TYPE = kShortStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
  MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE = kMediumStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
  LONG_EXTERNAL_ASCII_SYMBOL_TYPE = kLongStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
  SHORT_STRING_TYPE = kShortStringTag | kSeqStringTag,
  MEDIUM_STRING_TYPE = kMediumStringTag | kSeqStringTag,
  LONG_STRING_TYPE = kLongStringTag | kSeqStringTag,
  SHORT_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kSeqStringTag,
  MEDIUM_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kSeqStringTag,
  LONG_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kSeqStringTag,
  SHORT_CONS_STRING_TYPE = kShortStringTag | kConsStringTag,
  MEDIUM_CONS_STRING_TYPE = kMediumStringTag | kConsStringTag,
  LONG_CONS_STRING_TYPE = kLongStringTag | kConsStringTag,
  SHORT_CONS_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kConsStringTag,
  MEDIUM_CONS_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kConsStringTag,
  LONG_CONS_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kConsStringTag,
  SHORT_SLICED_STRING_TYPE = kShortStringTag | kSlicedStringTag,
  MEDIUM_SLICED_STRING_TYPE = kMediumStringTag | kSlicedStringTag,
  LONG_SLICED_STRING_TYPE = kLongStringTag | kSlicedStringTag,
  SHORT_SLICED_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kSlicedStringTag,
  MEDIUM_SLICED_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kSlicedStringTag,
  LONG_SLICED_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kSlicedStringTag,
  SHORT_EXTERNAL_STRING_TYPE = kShortStringTag | kExternalStringTag,
  MEDIUM_EXTERNAL_STRING_TYPE = kMediumStringTag | kExternalStringTag,
  LONG_EXTERNAL_STRING_TYPE = kLongStringTag | kExternalStringTag,
  SHORT_EXTERNAL_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kExternalStringTag,
  MEDIUM_EXTERNAL_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kExternalStringTag,
  LONG_EXTERNAL_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kExternalStringTag,
  LONG_PRIVATE_EXTERNAL_ASCII_STRING_TYPE = LONG_EXTERNAL_ASCII_STRING_TYPE,
  /*
    下面是所有类型都不属于字符串类型,kNotStringTag是128,即10000000,
    因为最高一位是0表示是字符串类型,所以10000000保证了大于所有字符串类型的值
  */
  MAP_TYPE = kNotStringTag,
  HEAP_NUMBER_TYPE,
  FIXED_ARRAY_TYPE,
  CODE_TYPE,
  ODDBALL_TYPE,
  PROXY_TYPE,
  BYTE_ARRAY_TYPE,
  FILLER_TYPE,
  SMI_TYPE,

  ACCESSOR_INFO_TYPE,
  ACCESS_CHECK_INFO_TYPE,
  INTERCEPTOR_INFO_TYPE,
  SHARED_FUNCTION_INFO_TYPE,
  CALL_HANDLER_INFO_TYPE,
  FUNCTION_TEMPLATE_INFO_TYPE,
  OBJECT_TEMPLATE_INFO_TYPE,
  SIGNATURE_INFO_TYPE,
  TYPE_SWITCH_INFO_TYPE,
  DEBUG_INFO_TYPE,
  BREAK_POINT_INFO_TYPE,
  SCRIPT_TYPE,

  JS_OBJECT_TYPE,
  JS_GLOBAL_OBJECT_TYPE,
  JS_BUILTINS_OBJECT_TYPE,
  JS_VALUE_TYPE,
  JS_ARRAY_TYPE,

  JS_FUNCTION_TYPE,

  // Pseudo-types
  FIRST_NONSTRING_TYPE = MAP_TYPE,
  FIRST_TYPE = 0x0,
  LAST_TYPE = JS_FUNCTION_TYPE,

  FIRST_JS_OBJECT_TYPE = JS_OBJECT_TYPE,
  LAST_JS_OBJECT_TYPE = JS_ARRAY_TYPE
}

示例图如下

在这里插入图片描述 我们对c++对象的类型大概有了一个印象,下面看一下Object类的定义。下面是几个宏,用来判断c++对象的类型的。

#define HAS_SMI_TAG(value) ((reinterpret_cast<int>(value) & kSmiTagMask) == kSmiTag)

#define HAS_FAILURE_TAG(value) ((reinterpret_cast<int>(value) & kFailureTagMask) == kFailureTag)

#define HAS_HEAP_OBJECT_TAG(value) ((reinterpret_cast<int>(value) & kHeapObjectTagMask) == kHeapObjectTag

下面是一些有代表性的isType函数的定义。

// 地址的低位是否是0
bool Object::IObject::isSmi() {
  return HAS_SMI_TAG(this);
}

// 低两位是01
bool Object::IsHeapObject() {
  return HAS_HEAP_OBJECT_TAG(this);
}

// 类型判断,在map里标记
bool Object::IsHeapNumber() {
  return Object::IsHeapObject()
    && HeapObject::cast(this)->map()->instance_type() == HEAP_NUMBER_TYPE;
}

bool Object::IsString() {
  return Object::IsHeapObject()
    && HeapObject::cast(this)->map()->instance_type() < FIRST_NONSTRING_TYPE;
}

bool Object::IsSeqString() {
  return IsString()
    && (String::cast(this)->representation_tag() == kSeqStringTag);
}

bool Object::IsByteArray() {
  return Object::IsHeapObject()
    && HeapObject::cast(this)->map()->instance_type() == BYTE_ARRAY_TYPE;
}

bool Object::IsJSObject() {
  return IsHeapObject()
    && HeapObject::cast(this)->map()->instance_type() >= JS_OBJECT_TYPE;
}
// 根据单例map对象判断类型
bool Object::IsContext() {
  return Object::IsHeapObject()
    && (HeapObject::cast(this)->map() == Heap::context_map() ||
        HeapObject::cast(this)->map() == Heap::global_context_map());
}

bool Object::IsUndefined() {
  return this == Heap::undefined_value();
}


bool Object::IsTheHole() {
  return this == Heap::the_hole_value();
}

下面继续看其他函数的定义。

1 解包对象里的数字。smi是小整形,在v8中表示整形。长度是31位。

double Object::Number() {
  return IsSmi()
    ? static_cast<double>(reinterpret_cast<Smi*>(this)->value())
    : reinterpret_cast<HeapNumber*>(this)->value();
}

2 转成smi对象

Object* Object::ToSmi() {
  // 已经是,直接返回
  if (IsSmi()) return this;
  // 是堆对象
  if (IsHeapNumber()) {
    double value = HeapNumber::cast(this)->value();
    // double to int类型
    int int_value = FastD2I(value);
    if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
      return Smi::FromInt(int_value);
    }
  }
  return Failure::Exception();
}

3 判断index是不是字符串的有效长度

bool Object::IsStringObjectWithCharacterAt(uint32_t index) {
  if (!this->IsJSValue()) return false;

  JSValue* js_value = JSValue::cast(this);
  if (!js_value->value()->IsString()) return false;

  String* str = String::cast(js_value->value());
  if (index >= (uint32_t)str->length()) return false;

  return true;
}

4 参考js的IsInstanceOf。很多类型后面的时候分析。

bool Object::IsInstanceOf(FunctionTemplateInfo* expected) {
  // js对象的基类
  if (!this->IsJSObject()) return false;

  Object* cons_obj = JSObject::cast(this)->map()->constructor();
  if (!cons_obj->IsJSFunction()) return false;
  JSFunction* fun = JSFunction::cast(cons_obj);

  for (Object* type = fun->shared()->function_data();type->IsFunctionTemplateInfo(); type = FunctionTemplateInfo::cast(type)->parent_template()) {
    if (type == expected) return true;
  }
  // Didn't find the required type in the inheritance chain.
  return false;
}

5 ToObject,其他类型转成对象类型。js的原生类型需要转成对象的时候。具体的在分析子类时再详细分析。

static Object* CreateJSValue(JSFunction* constructor, Object* value) {
  // 分类一个以constructor为构造函数的对象
  Object* result = Heap::AllocateJSObject(constructor);
  if (result->IsFailure()) return result;
  JSValue::cast(result)->set_value(value);
  return result;
}


Object* Object::ToObject(Context* global_context) {
  if (IsNumber()) {
    return CreateJSValue(global_context->number_function(), this);
  } else if (IsBoolean()) {
    return CreateJSValue(global_context->boolean_function(), this);
  } else if (IsString()) {
    return CreateJSValue(global_context->string_function(), this);
  }
  ASSERT(IsJSObject());
  return this;
}


Object* Object::ToObject() {
  Context* global_context = Top::context()->global_context();
  if (IsJSObject()) {
    return this;
  } else if (IsNumber()) {
    return CreateJSValue(global_context->number_function(), this);
  } else if (IsBoolean()) {
    return CreateJSValue(global_context->boolean_function(), this);
  } else if (IsString()) {
    return CreateJSValue(global_context->string_function(), this);
  }

  // Throw a type error.
  return Failure::InternalError();
}

6 ToBoolean,判断js变量是true或false的时候使用。

Object* Object::ToBoolean() {
  if (IsTrue()) return Heap::true_value();
  if (IsFalse()) return Heap::false_value();
  // 数字0是false
  if (IsSmi()) {
    return Heap::ToBoolean(Smi::cast(this)->value() != 0);
  }
  // null或undefined是false
  if (IsUndefined() || IsNull()) return Heap::false_value();
  // Undetectable object is false
  if (IsUndetectableObject()) {
    return Heap::false_value();
  }
  // 空字符串是false
  if (IsString()) {
    return Heap::ToBoolean(String::cast(this)->length() != 0);
  }
  // 
  if (IsHeapNumber()) {
    return HeapNumber::cast(this)->HeapNumberToBoolean();
  }
  return Heap::true_value();
}

7 属性查找,使用具体类型的查找函数

void Object::Lookup(String* name, LookupResult* result) {
  if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
  Object* holder = NULL;
  Context* global_context = Top::context()->global_context();
  if (IsString()) {
    holder = global_context->string_function()->instance_prototype();
  } else if (IsNumber()) {
    holder = global_context->number_function()->instance_prototype();
  } else if (IsBoolean()) {
    holder = global_context->boolean_function()->instance_prototype();
  }
  ASSERT(holder != NULL);  // cannot handle null or undefined.
  JSObject::cast(holder)->Lookup(name, result);
}

8 查找原型对象

Object* Object::GetPrototype() {
  // 对象的原型对象存在map对象里
  if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
  Context* context = Top::context()->global_context();

  if (IsNumber()) return context->number_function()->instance_prototype();
  if (IsString()) return context->string_function()->instance_prototype();
  if (IsBoolean()) {
    return context->boolean_function()->instance_prototype();
  } else {
    return Heap::null_value();
  }
}

还有几个属性查找的函数,依赖一些子类,分析完子类再分析。

本文分享自微信公众号 - 编程杂技(theanarkh),作者:theanarkh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

    我们看到类中有一个静态属性kSize,这个属性是标记该类的对象,属性需要占据的内存字节大小。下面我们看第一个继承于Object的类Smi。Smi是表示小整形。我...

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

    我们从代码可以知道,这几个类没有太多的逻辑,都有一系列的属性和对应的读写函数。对号入座就行。

    theanarkh
  • v8的堆内存初始化

    我们知道v8的堆是分为新生代,老生代,大对象等区域,从代码中我们也看到内存是分为几个部分,我们一个个来看。首先看NewSpace。

    theanarkh
  • 一条空间不足报警的分析(r7笔记第1天)

    今天下午收到一条报警邮件 ZABBIX-监控系统: ------------------------------------ 报警内容: Free disk ...

    jeanron100
  • 34款Firefox渗透测试插件

    工欲善必先利其器,firefox一直是各位渗透师必备的利器,小编这里推荐34款firefox渗透测试辅助插件,其中包含渗透测试、信息收集、代理、加密解密等功能。...

    joshua317
  • 初识Django

    框架,即framework,特制为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统。

    超蛋lhy
  • 大咖丁奇:索引存储顺序和order by不一致,怎么办?

    点击上方蓝字每天学习数据库 我是林晓斌,今天作为【迪B课堂】的客串嘉宾来跟大家分享:当索引存储顺序和order by不一致,该怎么办? ? 林晓斌 林晓斌,...

    腾讯云数据库 TencentDB
  • 【关关的刷题日记57】Leetcode 101. Symmetric Tree

    关关的刷题日记57 – Leetcode 101. Symmetric Tree 题目 ? 题目的意思是判断一棵树是否是镜像的,此处镜像指的是中心对称的树。 思...

    WZEARW
  • Leetcode题解——941/1013

    山脉数组就是前期是递增序列,后期是递减序列的数组,所以刚开始可以设置两个标志符,分别表示上升和下降。

    出其东门
  • Beego Models之四模型定义

    使用orm定义,然后使用cmd方式,自动建表,不过在实际生产中还是直接使用sql操作的,这种模型定义在生产环境中定义的比较少,基本上都是直接使用基本类型,一些特...

    若与

扫码关注云+社区

领取腾讯云代金券