首页
社区
课程
招聘
[原创] CVE-2023-4069:Maglev图建立阶段的一个漏洞
发表于: 2025-7-7 16:56 3249

[原创] CVE-2023-4069:Maglev图建立阶段的一个漏洞

2025-7-7 16:56
3249

issue链接:6f3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6M7%4y4#2k6i4y4Q4x3X3g2U0K9s2u0G2L8h3W2#2L8g2)9J5k6h3!0J5k6#2)9J5c8X3W2K6M7%4g2W2M7#2)9J5c8U0b7H3x3o6j5%4y4e0x3H3

863K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0K9s2u0G2L8h3W2#2L8g2)9J5k6s2u0W2N6X3W2W2N6#2)9J5k6h3N6G2L8$3N6D9k6i4y4G2N6i4u0U0k6g2)9J5k6h3y4G2L8g2)9J5c8X3y4Q4x3V1k6$3z5q4)9J5c8Y4j5^5i4K6u0r3i4K6u0n7i4K6u0r3y4o6j5&6y4o6l9H3y4H3`.`.

revision commit hash : 5efK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0K9s2u0G2L8h3W2#2L8g2)9J5k6h3N6G2L8$3N6D9k6i4y4G2N6i4u0U0k6g2)9J5k6h3y4G2L8g2)9J5c8Y4j5^5i4K6u0r3N6U0S2Q4x3V1k6Q4x3V1u0Q4x3V1k6W2k6o6V1K6j5X3g2X3y4$3q4T1y4K6R3$3k6o6f1K6y4U0N6U0x3X3q4W2y4K6R3^5x3U0V1J5x3X3x3J5x3$3q4S2x3r3g2V1j5e0j5@1

diff链接:

26bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0K9s2u0G2L8h3W2#2L8g2)9J5k6h3N6G2L8$3N6D9k6i4y4G2N6i4u0U0k6g2)9J5k6h3y4G2L8g2)9J5c8Y4j5^5i4K6u0r3N6U0S2Q4x3V1k6Q4x3V1u0Q4x3V1k6W2k6o6V1K6j5X3g2X3y4$3q4T1y4K6R3$3k6o6f1K6y4U0N6U0x3X3q4W2y4K6R3^5x3U0V1J5x3X3x3J5x3$3q4S2x3r3g2V1j5e0j5@1i4K6t1#2y4f1g2Q4x3U0f1J5x3g2)9J5c8R3`.`.

Reflect.construct()

函数原型

参数

使用实例

FastNewObject的实现代码,位于src/builtins/builtins-constructor-gen.cc

首先获取上下文环境,其中包括target和new_target,接着定义一个call_runtime的label,如果快速路径分配失败,则会通过这个label跳转到慢速路径进行对象分配。

调用FastNewObject进入快速路径,如果快速路径分配失败,则会跳转到BIND(&call_runtime),然后执行Runtime::kNewObject,进入慢速路径的分配

对于慢速路径的runtime函数可以在vscode里全局搜索RUNTIME_FUNCTION(Runtime_xxxxx,这里的xxxxx代表函数名,上方的kNewObject,那么就用这里的函数名应该是NewObject,所以就可以搜索RUNTIME_FUNCTION(Runtime_NewObject)

这里是全部完整的代码,位于src/builtins/builtins-constructor-gen.cc

分开看,首先判断new_target_func是不是JSFunction,不是就进入call_runtime的逻辑,和前面定义的Label call_runtime(this)对应上了,这里对应的是慢速路径

接着先获取new_target的inital map,然后判断new_target_func的initial_map是否是smi,因为如果是map类型,那么必然不会是smi,所以如果是smi,就会进入call_runtime的逻辑,然后将initial_map_or_proto转换为HeapObject判断是否为map类型,如果不是,那么就进入call_runtime的逻辑,这里验证的这个initial map是否真的存在

GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime) 等价于if( TaggedIsSmi(initial_map_or_proto) ){call_runtime}
简化一下就是GotoIf(A,B);{C};等价于if(A){B}else{C};

从initial map上load constructor,然后判断new_target的constructor和target是否一致

TVARIABLE(HeapObject, properties),这里的T代表Turbofan ,后面VARIABLE表示变量声明,类型是HeapObject,变量名称是properties

下面的逻辑会根据map的不同类型进行跳转

如果map的类型是DictionaryMap,那么会直接跳转到BIND(&allocate_properties),然后V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL编译选项是否开启,为properties采用不同的内存分配,接着会跳转到BIND(&instantiate_map),为map进行内存分配

如果map的类型不是DictionaryMap,那么会直接为properties分配,接着跳转到BIND(&instantiate_map)处,为map进行内存分配

下面总结一下,成功进行快速对象分配的条件是

当快速路径分配失败的时候,会通过Label call_runtime(this)跳转到BIND(&call_runtime),也就是执行这个语句TailCallRuntime(Runtime::kNewObject, context, target, new_target);

Runtime::kNewObject 是一个枚举值,定义在 src/runtime/runtime.h 中。这个枚举值对应的是 Runtime_NewObject 函数,定义在 src/runtime/runtime-object.cc 中。

这个函数的流程比较简单,获取当前的隔离实例,检查参数个数,获取target和new_target,最后将参数传递并调用JSObject::New

JSObject::New() 的源码位于src/objects/js-objects.cc,函数体开始的注释是对于new_target的所有可能性的说明,接着是一些DCHECK,可以看到target被命名成了constructor,new_target还是没有变化

调用JSFunction::GetDerivedMap来获取initial_map,然后根据V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL编译选项设置initial_capacity,最后调用NewFastOrSlowJSObjectFromMap分配对象,同时设置分配类型是kYoung,意味着可以被gc回收

接着来看一下JSFunction::GetDerivedMap的实现,位于src>objects>js-function.cc

首先检查一下constructor的initial map是否存在,接着获取constructor的initial map,判断如果new_target和constructor一样,那么就直接返回constructor_initial_map,这里对应了直接new object的情况,也是最常用的情况

这里是说只有当满足三个条件的时候,才会进入的逻辑,分别是new.target为JSFunction、new.target为constructor的子类 、map可缓存时。接着确保new.target存在initial map(没有的话会尝试分配),然后直接就返回new.target的initial map

看一下FastInitializeDerivedMap的实现,位于src>objects>js-function.cc

这里先check了new_target是否有prototype,也就是在原本的JSCFuntcion的基础上继续判断

接着如果new_target存在initial map,并且对应的构造函数和target一致,那么就直接返回true,进行快速对象的分配

这里快速对象分配还遵守着target和new_target的constructor得一致

其实漏洞时出现在快速对象分配的,下面的与慢速分配相关,所以笔者就不接着分析了

demo

./d8 --allow-natives-syntax --maglev --print-maglev-graphs ./DebugMaglev.js 执行这个命令,会有下面的输出,从Bytecode age: 0到Constant pool (size = 2)之间,是这一段demo生成的字节码

大致解释下,见注释。简单的说明是最左侧是存放字节码的地址,右侧@后面的数字是相对于这一段字节码的偏移地址,: 后面的是内存里的字节码,然后最后是指令

也就是说下面的r0是new出来的array,然后索引是r3,r3通过常量池中的p赋值的到,在循环体中每次+smi[5],r1作为index,每次执行自加的操作,直到大于等于25跳出循环

对于后续更详细的流程,可以去看这个23年p4nda师傅在bh eu上的slide

查看下issue对应的diff,完整的diff内容如下,主要是在/src/maglev/maglev-graph-builder.cc下的修改

分析之前需要熟悉一下这个函数MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct(),从这个函数名不难看出,这个流程发生在Maglev的图建立阶段,用于处理派生类非默认构造函数的访问

这里需要配合一个说明的代码,看一下这个函数的触发流程

对于上面的代码,有两个类,分别是Base和Derived,Derived继承了Base。然后调用Reflect.construct创建了Derived对象

使用如下参数./d8 --allow-natives-syntax --maglev --print-bytecode ./poc.js执行这个脚本,会有这样的输出,重点看一下Derived函数

0x3f5c0019b0c1调用了FindNonDefaultConstructorOrConstruct 这个函数,后续将一些寄存器赋值之后,有一个JumpIfTrue 的判断,接着就是正常的返回操作

接着通过上面的代码去分析VisitFindNonDefaultConstructorOrConstruct函数的源码

首先就是获取参数,this_function对应了Derived,new_target对应了Base,这里的register_pair是存储返回结果

接着获取this_function的ref,调用TryGetConstant方法,如果调用成功的话,那么就获取this_function的map的ref,这里获取的map是Derived函数(class 本质上还是一个函数)的map,接着获取Derived的原型对象

下面是一个很大的循环,推动循环执行的是这个语句current = current_function.map(broker()).prototype(broker()),本质上是顺着Derived的map向上查找原型,也就是遍历原型链,接着可以继续看这个循环体的逻辑

首先这里会去判断当前前遍历到的 prototype 对象是不是 JSFunction,接着获取这个function的ref,然后如果当前构造函数有实例字段(或类似需要初始化的成员),就不能跳过这个requires_instance_members_initializer构造函数,必须执行它的初始化逻辑,最后判断当前 class是否 有 private 字段或方法,不存在则会强制执行初始化逻辑

然后通过SFI(SharedFunctionInfo)去获取当前函数的类型,这里可以补充一下一些函数类型,下面就先解释下会用到的,全部的位于这里src/objects/function-kind.h文件里的FunctionKind枚举类里

当获取到当前函数的类型之后,因为从Derived,所以这个判断kind == FunctionKind::kDefaultDerivedConstructor为true,先执行一个依赖保护,通常都是true。

但是这里我们修改了Derived的构造函数内容,所以这里会返回false,所以会进入else逻辑,但是因为也不是FunctionKind::kDefaultBaseConstructor类型,所以会接着会遍历到上层,会获取到Base的prototype,判断是否为JSFunction时会转化为基类的构造函数,类型对应的是kDefaultBaseConstructor。然后也是执行依赖保护,从function map开始到current_function,也就是Derived开始到base。使用TryGetConstant获取当前构造函数的ref。接着通过if的判断,判断new_target_func函数是否存在且类型是JSFunction,然后调用FastObject分配快速对象,接着调用BuildAllocateFastObject分配对象,类型是kYoung,可以被gc回收

如果说这里原型链没有遍历到基类,那么这里的类型就不会是kDefaultBaseConstructor,从而进入else的逻辑,这里会调用BuildCallBuiltin分配对象

当所有的遍历流程接触,会将结果load到寄存器,最后返回

接着看一下FastObject的实现,位于/src/maglev/maglev-graph-builder.cc

可以看到这里其实都没有check,根据constructor.initial_map来初始化对象的map,对应上面的就是Base的initial map,然后根据当前Base的构造函数预测该构造函数实例化对象的最终属性数量和大小,最后就是为这个对象分配内存

这里是初始化操作

BuildAllocateFastObject的实现,位于/src/maglev/maglev-graph-builder.cc

基本上也没有什么检查,除了一个DCHECK,可以留意的是这里直接就根据传进来的快速对象去分配了,这里通过object.map去使用快速对象的map,对应上面的就是Base的initial map

其实到这里细心的读者应该是发现问题了,这里快速对象的分配不遵循之前的三个条件了,这里只判断了是current是否存在,且为JSFunction

回顾一下,成功进行快速对象分配的条件是

那么其实这里意味着我们通过这个路径,去分配出target(Derived)和new_target(Base)的map不一致的情况,也就是使用new_target的map去分配Derived对象,这样就会造成类型混淆了

同时结合上面分配的flag是AllocationType::kYoung,意味着内存可以被gc回收,那么就可以主动触发gc,从而导致使用到未被初始化的内存

所以现在就需要看下这个执行路径,这里最需要注意的是TryGetConstant,其他的正常路径都是会达到的

TryGetConstant函数的实现,位于/src/maglev/maglev-graph-builder.cc

这里的存在两个方法,一个static方法,一个成员方法,然后成员方法会调用到static方法

存在有两个路径,第一个路径是用于直接常量,第二个路径是用于传播常量;

比如说这种

可以通过使用直接常量的方式绕过

结合上面的分析,我们可以发现一个正常的快速对象分配是存在很多的检测,但是当Maglev在图建立阶段,分配快速对象的时候产生了问题,就是不会检测new_target和target的map是否一致,同时分配快速对象的时候使用的是new_target的initial map,因此如果二者的map不一致,那么这个可能会导致类型混淆的问题,常见的类型混淆就是JSObject和JSArray进行类型混淆。

同时还需要思考的是如何让两个TryGetConstant成立,第一处对于this_funciton的判断,也就是对于target的判断,我们可以让这个变量一直不变,让Maglev判断这个值时恒定的,也就是属于上面分析的传播常量的情况;第二处对于new_target的判断,考虑到new_target的initial map还会作为快速对象的map,因此这里可以设置为Array,对应的也就是直接常量,这样就可以绕过第二处的TryGetConstant,同时实现类型混淆,也就是意图创建target的对象实例,但是使用了new_target的initial map分配。

因为JSObject和JSArray的结构并不相同,这里会导致JSObject的in-object[0]这个字段会变成JSArray的Length字段,详细的可以看下面的两个对象的结构图

这里是JSObject的对象结构,Elements后面是in-object[0]

示例代码

不难看出elements后面就是2和4,右移1位之后就是in_obj1和in_obj2。然后out1和out2存储在properties中

这里是JSArray对象的结构

示例代码

输出

因为使用Reflect.construct(Derived, [], Base);的时候无法创建in-object对象,所以这里混淆后的Length就只能为0,但是别忘了还有gc,我们可以主动触发gc,这样会使用到一些地址上残留的值,这样就会有一个oob了

因此下面是一个poc

因为chrome执行会默认带--maglev这个flag,所以这里d8执行的时候需要加上--maglev,下面是输出,可以看到length字段已经出来了

方便查阅

有了一个oob的原语,下面的思路比较固定,我构造了这样的一个结构,首先堆喷一个victim_array(用对象初始化),这样Elements的地址相较于oob_array会相对稳定(这里笔者的机器信息见下方,然后这里oob_array的Elements在笔者机器上的offset稳定为0x219)

接着利用oob_array的oob去修改victim_array的elements元素,布置伪造的map(根据版本动态修改)和对象,然后布置这个fake_obj_addr,便于后续伪造fake_object,也就是一步到位直接有fakeObject原语了

然后victim_array的Elements首部可以布置一个obj,这样方便写addressOf的原语,通过oob_array越界写

笔者机器使用的环境

遇到的一些问题

exp

笔者机器上,堆喷射后victim_array的elements地址只有这两个情况,0x2423cd和0x2423e9

d23K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2T1L8r3!0Y4i4K6u0r3M7$3g2U0N6i4u0A6N6s2W2Q4x3V1k6$3N6h3I4F1k6i4u0S2j5X3W2D9K9i4c8&6i4K6u0V1M7X3g2K6k6h3q4J5j5$3S2Q4x3V1k6Y4k6i4c8@1K9h3&6Y4i4K6u0V1M7X3y4W2i4K6u0V1K9h3&6Q4x3X3c8U0K9s2u0G2L8h3g2Q4x3X3c8%4K9i4c8Z5i4K6u0V1K9h3&6U0L8$3#2H3L8r3g2@1k6g2)9J5k6r3!0T1K9X3g2U0N6q4)9J5k6r3W2F1K9i4c8A6j5h3I4A6P5X3q4@1K9h3!0F1i4K6u0V1K9h3&6Q4x3X3c8@1K9r3g2Q4x3X3c8E0j5h3N6D9k6i4k6Q4x3X3c8U0L8$3#2H3K9h3I4W2M7W2)9J5c8R3`.`.
b3bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3V1k6K6k6h3y4#2M7X3W2@1P5h3I4S2j5W2)9J5c8X3u0D9L8$3u0Q4x3V1k6E0j5h3W2F1i4K6u0r3f1$3g2U0N6i4u0A6N6s2W2q4P5s2m8D9L8$3W2@1M7#2)9J5c8V1y4Z5M7X3!0E0k6g2)9J5c8Y4j5^5i4K6u0r3b7#2k6q4i4K6g2X3x3U0l9J5x3#2)9#2k6U0b7H3y4U0W2Q4x3V1k6H3L8$3y4Q4x3X3g2B7M7H3`.`.
fe1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3V1k6K6k6h3y4#2M7X3W2@1P5h3I4S2j5W2)9J5c8X3c8A6M7$3y4#2M7%4y4A6L8$3&6K6i4K6u0r3y4K6V1%4
aa5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2E0j5i4c8@1k6h3!0E0j5h3I4$3K9h3y4S2i4K6u0W2j5$3!0E0i4K6u0r3j5X3I4G2k6#2)9J5c8U0t1H3x3U0c8Q4x3V1j5H3y4W2)9J5c8U0l9#2i4K6u0r3K9h3&6@1M7X3!0Q4x3X3c8$3z5q4)9J5k6r3g2^5M7r3I4G2K9i4c8S2N6r3W2G2L8W2)9J5k6r3#2S2k6$3I4W2N6W2)9J5c8R3`.`.

git checkout 5315f073233429c5f5c2c794594499debda307bd
gclient sync -D
python3 tools\dev\gm.py x64.release
git checkout 5315f073233429c5f5c2c794594499debda307bd
gclient sync -D
python3 tools\dev\gm.py x64.release
Reflect.construct(target, argumentsList[, newTarget])
Reflect.construct(target, argumentsList[, newTarget])
function OneClass() {
  this.name = "one";
}
function OtherClass() {
  this.name = "other";
}
// 创建一个对象:
var obj1 = Reflect.construct(OneClass, args, OtherClass);
console.log(obj1.name); // 'one'
console.log(obj1 instanceof OneClass); // false
console.log(obj1 instanceof OtherClass); // true
function OneClass() {
  this.name = "one";
}
function OtherClass() {
  this.name = "other";
}
// 创建一个对象:
var obj1 = Reflect.construct(OneClass, args, OtherClass);
console.log(obj1.name); // 'one'
console.log(obj1 instanceof OneClass); // false
console.log(obj1 instanceof OtherClass); // true
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
  auto context = Parameter<Context>(Descriptor::kContext);
  auto target = Parameter<JSFunction>(Descriptor::kTarget);
  auto new_target = Parameter<JSReceiver>(Descriptor::kNewTarget);
 
  Label call_runtime(this);
 
  TNode<JSObject> result =
      FastNewObject(context, target, new_target, &call_runtime);
  Return(result);
 
  BIND(&call_runtime);
  TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
  auto context = Parameter<Context>(Descriptor::kContext);
  auto target = Parameter<JSFunction>(Descriptor::kTarget);
  auto new_target = Parameter<JSReceiver>(Descriptor::kNewTarget);
 
  Label call_runtime(this);
 
  TNode<JSObject> result =
      FastNewObject(context, target, new_target, &call_runtime);
  Return(result);
 
  BIND(&call_runtime);
  TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
    TNode<Context> context, TNode<JSFunction> target,
    TNode<JSReceiver> new_target, Label* call_runtime) {
  // Verify that the new target is a JSFunction.
  Label end(this);
  TNode<JSFunction> new_target_func =
      HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
  // Fast path.
 
  // Load the initial map and verify that it's in fact a map.
  TNode<Object> initial_map_or_proto =
      LoadJSFunctionPrototypeOrInitialMap(new_target_func);
  GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
  GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE),
         call_runtime);
  TNode<Map> initial_map = CAST(initial_map_or_proto);
 
  // Fall back to runtime if the target differs from the new target's
  // initial map constructor.
  TNode<Object> new_target_constructor = LoadObjectField(
      initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
  GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
 
  TVARIABLE(HeapObject, properties);
 
  Label instantiate_map(this), allocate_properties(this);
  GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
  {
    properties = EmptyFixedArrayConstant();
    Goto(&instantiate_map);
  }
  BIND(&allocate_properties);
  {
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      properties =
          AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
    } else {
      properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
    }
    Goto(&instantiate_map);
  }
 
  BIND(&instantiate_map);
  return AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
                                 AllocationFlag::kNone, kWithSlackTracking);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
    TNode<Context> context, TNode<JSFunction> target,
    TNode<JSReceiver> new_target, Label* call_runtime) {
  // Verify that the new target is a JSFunction.
  Label end(this);
  TNode<JSFunction> new_target_func =
      HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
  // Fast path.
 
  // Load the initial map and verify that it's in fact a map.
  TNode<Object> initial_map_or_proto =
      LoadJSFunctionPrototypeOrInitialMap(new_target_func);
  GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
  GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE),
         call_runtime);
  TNode<Map> initial_map = CAST(initial_map_or_proto);
 
  // Fall back to runtime if the target differs from the new target's
  // initial map constructor.
  TNode<Object> new_target_constructor = LoadObjectField(
      initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
  GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
 
  TVARIABLE(HeapObject, properties);
 
  Label instantiate_map(this), allocate_properties(this);
  GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
  {
    properties = EmptyFixedArrayConstant();
    Goto(&instantiate_map);
  }
  BIND(&allocate_properties);
  {
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      properties =
          AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
    } else {
      properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
    }
    Goto(&instantiate_map);
  }
 
  BIND(&instantiate_map);
  return AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
                                 AllocationFlag::kNone, kWithSlackTracking);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
    TNode<Context> context, TNode<JSFunction> target,
    TNode<JSReceiver> new_target, Label* call_runtime) {
  // Verify that the new target is a JSFunction.
  Label end(this);
  TNode<JSFunction> new_target_func =
      HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
    TNode<Context> context, TNode<JSFunction> target,
    TNode<JSReceiver> new_target, Label* call_runtime) {
  // Verify that the new target is a JSFunction.
  Label end(this);
  TNode<JSFunction> new_target_func =
      HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
// Load the initial map and verify that it's in fact a map.
TNode<Object> initial_map_or_proto =
    LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE),
       call_runtime);
TNode<Map> initial_map = CAST(initial_map_or_proto);
// Load the initial map and verify that it's in fact a map.
TNode<Object> initial_map_or_proto =
    LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE),
       call_runtime);
TNode<Map> initial_map = CAST(initial_map_or_proto);
// Fall back to runtime if the target differs from the new target's
// initial map constructor.
TNode<Object> new_target_constructor = LoadObjectField(
    initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
// Fall back to runtime if the target differs from the new target's
// initial map constructor.
TNode<Object> new_target_constructor = LoadObjectField(
    initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
  TVARIABLE(HeapObject, properties);
 
  Label instantiate_map(this), allocate_properties(this);
  GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
  {
    properties = EmptyFixedArrayConstant();
    Goto(&instantiate_map);
  }
  BIND(&allocate_properties);
  {
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      properties =
          AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
    } else {
      properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
    }
    Goto(&instantiate_map);
  }
 
  BIND(&instantiate_map);
  return AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
                                 AllocationFlag::kNone, kWithSlackTracking);
}
  TVARIABLE(HeapObject, properties);
 
  Label instantiate_map(this), allocate_properties(this);
  GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
  {
    properties = EmptyFixedArrayConstant();
    Goto(&instantiate_map);
  }
  BIND(&allocate_properties);
  {
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      properties =
          AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
    } else {
      properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
    }
    Goto(&instantiate_map);
  }
 
  BIND(&instantiate_map);
  return AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
                                 AllocationFlag::kNone, kWithSlackTracking);
}
RUNTIME_FUNCTION(Runtime_NewObject) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  Handle<JSFunction> target = args.at<JSFunction>(0);
  Handle<JSReceiver> new_target = args.at<JSReceiver>(1);
  RETURN_RESULT_OR_FAILURE(
      isolate,
      JSObject::New(target, new_target, Handle<AllocationSite>::null()));
}
RUNTIME_FUNCTION(Runtime_NewObject) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  Handle<JSFunction> target = args.at<JSFunction>(0);
  Handle<JSReceiver> new_target = args.at<JSReceiver>(1);
  RETURN_RESULT_OR_FAILURE(
      isolate,
      JSObject::New(target, new_target, Handle<AllocationSite>::null()));
}
// static
MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
                                    Handle<JSReceiver> new_target,
                                    Handle<AllocationSite> site) {
  // If called through new, new.target can be:
  // - a subclass of constructor,
  // - a proxy wrapper around constructor, or
  // - the constructor itself.
  // If called through Reflect.construct, it's guaranteed to be a constructor.
  Isolate* const isolate = constructor->GetIsolate();
  DCHECK(constructor->IsConstructor());
  DCHECK(new_target->IsConstructor());
  DCHECK(!constructor->has_initial_map() ||
         !InstanceTypeChecker::IsJSFunction(
             constructor->initial_map().instance_type()));
// static
MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
                                    Handle<JSReceiver> new_target,
                                    Handle<AllocationSite> site) {
  // If called through new, new.target can be:
  // - a subclass of constructor,
  // - a proxy wrapper around constructor, or
  // - the constructor itself.
  // If called through Reflect.construct, it's guaranteed to be a constructor.
  Isolate* const isolate = constructor->GetIsolate();
  DCHECK(constructor->IsConstructor());
  DCHECK(new_target->IsConstructor());
  DCHECK(!constructor->has_initial_map() ||
         !InstanceTypeChecker::IsJSFunction(
             constructor->initial_map().instance_type()));
  Handle<Map> initial_map;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, initial_map,
      JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
  constexpr int initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
                                       ? SwissNameDictionary::kInitialCapacity
                                       : NameDictionary::kInitialCapacity;
  Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
      initial_map, initial_capacity, AllocationType::kYoung, site);
  return result;
}
  Handle<Map> initial_map;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, initial_map,
      JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
  constexpr int initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
                                       ? SwissNameDictionary::kInitialCapacity
                                       : NameDictionary::kInitialCapacity;
  Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
      initial_map, initial_capacity, AllocationType::kYoung, site);
  return result;
}
// static
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
                                           Handle<JSFunction> constructor,
                                           Handle<JSReceiver> new_target) {
  EnsureHasInitialMap(constructor);
 
  Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
  if (*new_target == *constructor) return constructor_initial_map;
 
  Handle<Map> result_map;
  // Fast case, new.target is a subclass of constructor. The map is cacheable
  // (and may already have been cached). new.target.prototype is guaranteed to
  // be a JSReceiver.
  if (new_target->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
    if (FastInitializeDerivedMap(isolate, function, constructor,
                                 constructor_initial_map)) {
      return handle(function->initial_map(), isolate);
    }
  }
 
  // Slow path, new.target is either a proxy or can't cache the map.
  // new.target.prototype is not guaranteed to be a JSReceiver, and may need to
  // fall back to the intrinsicDefaultProto.
  Handle<Object> prototype;
  if (new_target->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
    if (function->has_prototype_slot()) {
      // Make sure the new.target.prototype is cached.
      EnsureHasInitialMap(function);
      prototype = handle(function->prototype(), isolate);
    } else {
      // No prototype property, use the intrinsict default proto further down.
      prototype = isolate->factory()->undefined_value();
    }
  } else {
    Handle<String> prototype_string = isolate->factory()->prototype_string();
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, prototype,
        JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
    // The above prototype lookup might change the constructor and its
    // prototype, hence we have to reload the initial map.
    EnsureHasInitialMap(constructor);
    constructor_initial_map = handle(constructor->initial_map(), isolate);
  }
 
  // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
  // correct realm. Rather than directly fetching the .prototype, we fetch the
  // constructor that points to the .prototype. This relies on
  // constructor.prototype being FROZEN for those constructors.
  if (!prototype->IsJSReceiver()) {
    Handle<Context> context;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
                               JSReceiver::GetFunctionRealm(new_target), Map);
    DCHECK(context->IsNativeContext());
    Handle<Object> maybe_index = JSReceiver::GetDataProperty(
        isolate, constructor,
        isolate->factory()->native_context_index_symbol());
    int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
                                     : Context::OBJECT_FUNCTION_INDEX;
    Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
                                         isolate);
    prototype = handle(realm_constructor->prototype(), isolate);
  }
 
  Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
  map->set_new_target_is_base(false);
  CHECK(prototype->IsJSReceiver());
  if (map->prototype() != *prototype)
    Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
  map->SetConstructor(*constructor);
  return map;
}
// static
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
                                           Handle<JSFunction> constructor,
                                           Handle<JSReceiver> new_target) {
  EnsureHasInitialMap(constructor);
 
  Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
  if (*new_target == *constructor) return constructor_initial_map;
 
  Handle<Map> result_map;
  // Fast case, new.target is a subclass of constructor. The map is cacheable
  // (and may already have been cached). new.target.prototype is guaranteed to
  // be a JSReceiver.
  if (new_target->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
    if (FastInitializeDerivedMap(isolate, function, constructor,
                                 constructor_initial_map)) {
      return handle(function->initial_map(), isolate);
    }
  }
 
  // Slow path, new.target is either a proxy or can't cache the map.
  // new.target.prototype is not guaranteed to be a JSReceiver, and may need to
  // fall back to the intrinsicDefaultProto.
  Handle<Object> prototype;
  if (new_target->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
    if (function->has_prototype_slot()) {
      // Make sure the new.target.prototype is cached.
      EnsureHasInitialMap(function);
      prototype = handle(function->prototype(), isolate);
    } else {
      // No prototype property, use the intrinsict default proto further down.
      prototype = isolate->factory()->undefined_value();
    }
  } else {
    Handle<String> prototype_string = isolate->factory()->prototype_string();
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, prototype,
        JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
    // The above prototype lookup might change the constructor and its
    // prototype, hence we have to reload the initial map.
    EnsureHasInitialMap(constructor);
    constructor_initial_map = handle(constructor->initial_map(), isolate);
  }
 
  // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
  // correct realm. Rather than directly fetching the .prototype, we fetch the
  // constructor that points to the .prototype. This relies on
  // constructor.prototype being FROZEN for those constructors.
  if (!prototype->IsJSReceiver()) {
    Handle<Context> context;
    ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
                               JSReceiver::GetFunctionRealm(new_target), Map);
    DCHECK(context->IsNativeContext());
    Handle<Object> maybe_index = JSReceiver::GetDataProperty(
        isolate, constructor,
        isolate->factory()->native_context_index_symbol());
    int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
                                     : Context::OBJECT_FUNCTION_INDEX;
    Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
                                         isolate);
    prototype = handle(realm_constructor->prototype(), isolate);
  }
 
  Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
  map->set_new_target_is_base(false);
  CHECK(prototype->IsJSReceiver());
  if (map->prototype() != *prototype)
    Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
  map->SetConstructor(*constructor);
  return map;
}
// static
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
                                           Handle<JSFunction> constructor,
                                           Handle<JSReceiver> new_target) {
  EnsureHasInitialMap(constructor);
 
  Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
  if (*new_target == *constructor) return constructor_initial_map;
// static
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
                                           Handle<JSFunction> constructor,
                                           Handle<JSReceiver> new_target) {
  EnsureHasInitialMap(constructor);
 
  Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
  if (*new_target == *constructor) return constructor_initial_map;
Handle<Map> result_map;
// Fast case, new.target is a subclass of constructor. The map is cacheable
// (and may already have been cached). new.target.prototype is guaranteed to
// be a JSReceiver.
if (new_target->IsJSFunction()) {
  Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
  if (FastInitializeDerivedMap(isolate, function, constructor,
                               constructor_initial_map)) {
    return handle(function->initial_map(), isolate);
  }
}
Handle<Map> result_map;
// Fast case, new.target is a subclass of constructor. The map is cacheable
// (and may already have been cached). new.target.prototype is guaranteed to
// be a JSReceiver.
if (new_target->IsJSFunction()) {
  Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
  if (FastInitializeDerivedMap(isolate, function, constructor,
                               constructor_initial_map)) {
    return handle(function->initial_map(), isolate);
  }
}
bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
                              Handle<JSFunction> constructor,
                              Handle<Map> constructor_initial_map) {
  // Use the default intrinsic prototype instead.
  if (!new_target->has_prototype_slot()) return false;
  // Check that |function|'s initial map still in sync with the |constructor|,
  // otherwise we must create a new initial map for |function|.
  if (new_target->has_initial_map() &&
      new_target->initial_map().GetConstructor() == *constructor) {
    DCHECK(new_target->instance_prototype().IsJSReceiver());
    return true;
  }
  InstanceType instance_type = constructor_initial_map->instance_type();
  DCHECK(CanSubclassHaveInobjectProperties(instance_type));
  // Create a new map with the size and number of in-object properties
  // suggested by |function|.
 
  // Link initial map and constructor function if the new.target is actually a
  // subclass constructor.
  if (!IsDerivedConstructor(new_target->shared().kind())) return false;
 
  int instance_size;
  int in_object_properties;
  int embedder_fields =
      JSObject::GetEmbedderFieldCount(*constructor_initial_map);
  // Constructor expects certain number of in-object properties to be in the
  // object. However, CalculateExpectedNofProperties() may return smaller value
  // if 1) the constructor is not in the prototype chain of new_target, or
  // 2) the prototype chain is modified during iteration, or 3) compilation
  // failure occur during prototype chain iteration.
  // So we take the maximum of two values.
  int expected_nof_properties = std::max(
      static_cast<int>(constructor->shared().expected_nof_properties()),
      JSFunction::CalculateExpectedNofProperties(isolate, new_target));
  JSFunction::CalculateInstanceSizeHelper(
      instance_type, constructor_initial_map->has_prototype_slot(),
      embedder_fields, expected_nof_properties, &instance_size,
      &in_object_properties);
 
  int pre_allocated = constructor_initial_map->GetInObjectProperties() -
                      constructor_initial_map->UnusedPropertyFields();
  CHECK_LE(constructor_initial_map->UsedInstanceSize(), instance_size);
  int unused_property_fields = in_object_properties - pre_allocated;
  Handle<Map> map =
      Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
                          in_object_properties, unused_property_fields);
  map->set_new_target_is_base(false);
  Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
  JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
  DCHECK(new_target->instance_prototype().IsJSReceiver());
  map->set_construction_counter(Map::kNoSlackTracking);
  map->StartInobjectSlackTracking();
  return true;
}
bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
                              Handle<JSFunction> constructor,
                              Handle<Map> constructor_initial_map) {
  // Use the default intrinsic prototype instead.
  if (!new_target->has_prototype_slot()) return false;
  // Check that |function|'s initial map still in sync with the |constructor|,
  // otherwise we must create a new initial map for |function|.
  if (new_target->has_initial_map() &&
      new_target->initial_map().GetConstructor() == *constructor) {
    DCHECK(new_target->instance_prototype().IsJSReceiver());
    return true;
  }
  InstanceType instance_type = constructor_initial_map->instance_type();
  DCHECK(CanSubclassHaveInobjectProperties(instance_type));
  // Create a new map with the size and number of in-object properties
  // suggested by |function|.
 
  // Link initial map and constructor function if the new.target is actually a
  // subclass constructor.
  if (!IsDerivedConstructor(new_target->shared().kind())) return false;
 
  int instance_size;
  int in_object_properties;
  int embedder_fields =
      JSObject::GetEmbedderFieldCount(*constructor_initial_map);
  // Constructor expects certain number of in-object properties to be in the
  // object. However, CalculateExpectedNofProperties() may return smaller value
  // if 1) the constructor is not in the prototype chain of new_target, or
  // 2) the prototype chain is modified during iteration, or 3) compilation
  // failure occur during prototype chain iteration.
  // So we take the maximum of two values.
  int expected_nof_properties = std::max(
      static_cast<int>(constructor->shared().expected_nof_properties()),
      JSFunction::CalculateExpectedNofProperties(isolate, new_target));
  JSFunction::CalculateInstanceSizeHelper(
      instance_type, constructor_initial_map->has_prototype_slot(),
      embedder_fields, expected_nof_properties, &instance_size,
      &in_object_properties);
 
  int pre_allocated = constructor_initial_map->GetInObjectProperties() -
                      constructor_initial_map->UnusedPropertyFields();
  CHECK_LE(constructor_initial_map->UsedInstanceSize(), instance_size);
  int unused_property_fields = in_object_properties - pre_allocated;
  Handle<Map> map =
      Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
                          in_object_properties, unused_property_fields);
  map->set_new_target_is_base(false);
  Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
  JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
  DCHECK(new_target->instance_prototype().IsJSReceiver());
  map->set_construction_counter(Map::kNoSlackTracking);
  map->StartInobjectSlackTracking();
  return true;
}
bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
                              Handle<JSFunction> constructor,
                              Handle<Map> constructor_initial_map) {
  // Use the default intrinsic prototype instead.
  if (!new_target->has_prototype_slot()) return false;
  // Check that |function|'s initial map still in sync with the |constructor|,
  // otherwise we must create a new initial map for |function|.
  if (new_target->has_initial_map() &&
      new_target->initial_map().GetConstructor() == *constructor) {
    DCHECK(new_target->instance_prototype().IsJSReceiver());
    return true;
  }
bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
                              Handle<JSFunction> constructor,
                              Handle<Map> constructor_initial_map) {
  // Use the default intrinsic prototype instead.
  if (!new_target->has_prototype_slot()) return false;
  // Check that |function|'s initial map still in sync with the |constructor|,
  // otherwise we must create a new initial map for |function|.
  if (new_target->has_initial_map() &&
      new_target->initial_map().GetConstructor() == *constructor) {
    DCHECK(new_target->instance_prototype().IsJSReceiver());
    return true;
  }
// Slow path, new.target is either a proxy or can't cache the map.
// new.target.prototype is not guaranteed to be a JSReceiver, and may need to
// fall back to the intrinsicDefaultProto.
Handle<Object> prototype;
if (new_target->IsJSFunction()) {
  Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
  if (function->has_prototype_slot()) {
    // Make sure the new.target.prototype is cached.
    EnsureHasInitialMap(function);
    prototype = handle(function->prototype(), isolate);
  } else {
    // No prototype property, use the intrinsict default proto further down.
    prototype = isolate->factory()->undefined_value();
  }
} else {
  Handle<String> prototype_string = isolate->factory()->prototype_string();
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, prototype,
      JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
  // The above prototype lookup might change the constructor and its
  // prototype, hence we have to reload the initial map.
  EnsureHasInitialMap(constructor);
  constructor_initial_map = handle(constructor->initial_map(), isolate);
}
// Slow path, new.target is either a proxy or can't cache the map.
// new.target.prototype is not guaranteed to be a JSReceiver, and may need to
// fall back to the intrinsicDefaultProto.
Handle<Object> prototype;
if (new_target->IsJSFunction()) {
  Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
  if (function->has_prototype_slot()) {
    // Make sure the new.target.prototype is cached.
    EnsureHasInitialMap(function);
    prototype = handle(function->prototype(), isolate);
  } else {
    // No prototype property, use the intrinsict default proto further down.
    prototype = isolate->factory()->undefined_value();
  }
} else {
  Handle<String> prototype_string = isolate->factory()->prototype_string();
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, prototype,
      JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
  // The above prototype lookup might change the constructor and its
  // prototype, hence we have to reload the initial map.
  EnsureHasInitialMap(constructor);
  constructor_initial_map = handle(constructor->initial_map(), isolate);
}
function f0(a1){
    const v5 = new Array(7);
    for (let v6 = 0; v6 < 25; v6++) {
      v5["p" + v6] = v6;
    }
}
 
f0(f0());
%OptimizeMaglevOnNextCall(f0);
f0(f0());
function f0(a1){
    const v5 = new Array(7);
    for (let v6 = 0; v6 < 25; v6++) {
      v5["p" + v6] = v6;
    }
}
 
f0(f0());
%OptimizeMaglevOnNextCall(f0);
f0(f0());
0x363c0019adf2 @    0 : 21 00 00          LdaGlobal [0], [0]  ;load global “Array”,从下面的常量池取出来
0x363c0019adf5 @    3 : c3                Star2               ;将值 store 到 r2
0x363c0019adf6 @    4 : 0d 07             LdaSmi [7]          ;将smi 7 load 到 累加器中
0x363c0019adf8 @    6 : c2                Star3               ;将累加器中的值 store 到 r3
0x363c0019adf9 @    7 : 0b f8             Ldar r2             ;将r2的值 load 到累加器中
0x363c0019adfb @    9 : 69 f8 f7 01 02    Construct r2, r3-r3, [2];r2是Array r3是7 => new Array(7)
0x363c0019ae00 @   14 : c5                Star0                ;值存到r0
0x363c0019ae01 @   15 : 0c                LdaZero              ;0存到累加器
0x363c0019ae02 @   16 : c4                Star1                ;累加器的值存到r1
0x363c0019ae03 @   17 : 0d 19             LdaSmi [25]          ;smi 25存到 累加器
0x363c0019ae05 @   19 : 6d f9 04          TestLessThan r1, [4] ;比较r1和smi 4,对应的是for循环的判断
0x363c0019ae08 @   22 : 9a 1a             JumpIfFalse [26] (0x363c0019ae22 @ 48);如果返回值为false 则跳转到偏移为48的地方,对应后方的LdaUndefined指令
0x363c0019ae0a @   24 : 13 01             LdaConstant [1]      ;常量池[1],也就是p,load到累加器
0x363c0019ae0c @   26 : c2                Star3                ;再存到r3
0x363c0019ae0d @   27 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae0f @   29 : 38 f7 05          Add r3, [5]          ;r3+smi[5],对应"p" + v6
0x363c0019ae12 @   32 : c2                Star3                ;值存到r3
0x363c0019ae13 @   33 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae15 @   35 : 34 fa f7 06       SetKeyedProperty r0, r3, [6];r0是上面创建的Array,然后r3是设置的val,索引赋值为smi[6]
0x363c0019ae19 @   39 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae1b @   41 : 50 08             Inc [8]              ;r1+8,也就是v6++
0x363c0019ae1d @   43 : c4                Star1                ;累加器的值存到r1
0x363c0019ae1e @   44 : 8a 1b 00 09       JumpLoop [27], [0], [9] (0x363c0019ae03 @ 17);跳转到偏移为17的地方,也就是这个指令的位置LdaSmi [25]
0x363c0019ae22 @   48 : 0e                LdaUndefined
0x363c0019ae23 @   49 : aa                Return
0x363c0019adf2 @    0 : 21 00 00          LdaGlobal [0], [0]  ;load global “Array”,从下面的常量池取出来
0x363c0019adf5 @    3 : c3                Star2               ;将值 store 到 r2
0x363c0019adf6 @    4 : 0d 07             LdaSmi [7]          ;将smi 7 load 到 累加器中
0x363c0019adf8 @    6 : c2                Star3               ;将累加器中的值 store 到 r3
0x363c0019adf9 @    7 : 0b f8             Ldar r2             ;将r2的值 load 到累加器中
0x363c0019adfb @    9 : 69 f8 f7 01 02    Construct r2, r3-r3, [2];r2是Array r3是7 => new Array(7)
0x363c0019ae00 @   14 : c5                Star0                ;值存到r0
0x363c0019ae01 @   15 : 0c                LdaZero              ;0存到累加器
0x363c0019ae02 @   16 : c4                Star1                ;累加器的值存到r1
0x363c0019ae03 @   17 : 0d 19             LdaSmi [25]          ;smi 25存到 累加器
0x363c0019ae05 @   19 : 6d f9 04          TestLessThan r1, [4] ;比较r1和smi 4,对应的是for循环的判断
0x363c0019ae08 @   22 : 9a 1a             JumpIfFalse [26] (0x363c0019ae22 @ 48);如果返回值为false 则跳转到偏移为48的地方,对应后方的LdaUndefined指令
0x363c0019ae0a @   24 : 13 01             LdaConstant [1]      ;常量池[1],也就是p,load到累加器
0x363c0019ae0c @   26 : c2                Star3                ;再存到r3
0x363c0019ae0d @   27 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae0f @   29 : 38 f7 05          Add r3, [5]          ;r3+smi[5],对应"p" + v6
0x363c0019ae12 @   32 : c2                Star3                ;值存到r3
0x363c0019ae13 @   33 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae15 @   35 : 34 fa f7 06       SetKeyedProperty r0, r3, [6];r0是上面创建的Array,然后r3是设置的val,索引赋值为smi[6]
0x363c0019ae19 @   39 : 0b f9             Ldar r1              ;r1存到累加器
0x363c0019ae1b @   41 : 50 08             Inc [8]              ;r1+8,也就是v6++
0x363c0019ae1d @   43 : c4                Star1                ;累加器的值存到r1
0x363c0019ae1e @   44 : 8a 1b 00 09       JumpLoop [27], [0], [9] (0x363c0019ae03 @ 17);跳转到偏移为17的地方,也就是这个指令的位置LdaSmi [25]
0x363c0019ae22 @   48 : 0e                LdaUndefined
0x363c0019ae23 @   49 : aa                Return
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index d5f6128..2c5227e 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -5347,6 +5347,14 @@
   StoreRegister(iterator_.GetRegisterOperand(0), map_proto);
 }
  
+bool MaglevGraphBuilder::HasValidInitialMap(
+    compiler::JSFunctionRef new_target, compiler::JSFunctionRef constructor) {
if (!new_target.map(broker()).has_prototype_slot()) return false;
if (!new_target.has_initial_map(broker())) return false;
+  compiler::MapRef initial_map = new_target.initial_map(broker());
return initial_map.GetConstructor(broker()).equals(constructor);
+}
+
 void MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
   ValueNode* this_function = LoadRegisterTagged(0);
   ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
               TryGetConstant(new_target);
           if (kind == FunctionKind::kDefaultBaseConstructor) {
             ValueNode* object;
-            if (new_target_function && new_target_function->IsJSFunction()) {
+            if (new_target_function && new_target_function->IsJSFunction() &&
+                HasValidInitialMap(new_target_function->AsJSFunction(),
+                                   current_function)) {
               object = BuildAllocateFastObject(
                   FastObject(new_target_function->AsJSFunction(), zone(),
                              broker()),
diff --git a/src/maglev/maglev-graph-builder.h b/src/maglev/maglev-graph-builder.h
index 0abb4a8..d92354c 100644
--- a/src/maglev/maglev-graph-builder.h
+++ b/src/maglev/maglev-graph-builder.h
@@ -1884,6 +1884,9 @@
   void MergeDeadLoopIntoFrameState(int target);
   void MergeIntoInlinedReturnFrameState(BasicBlock* block);
  
bool HasValidInitialMap(compiler::JSFunctionRef new_target,
+                          compiler::JSFunctionRef constructor);
+
   enum JumpType { kJumpIfTrue, kJumpIfFalse };
   enum class BranchSpecializationMode { kDefault, kAlwaysBoolean };
   JumpType NegateJumpType(JumpType jump_type);
diff --git a/test/mjsunit/maglev/regress/regress-crbug-1465326.js b/test/mjsunit/maglev/regress/regress-crbug-1465326.js
new file mode 100644
index 0000000..6e01c1e
--- /dev/null
+++ b/test/mjsunit/maglev/regress/regress-crbug-1465326.js
@@ -0,0 +1,25 @@
+// Copyright 2023 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --maglev --allow-natives-syntax
+
+class A {}
+
+var x = Function;
+
+class B extends A {
+  constructor() {
+    x = new.target;
+    super();
+  }
+}
+function construct() {
return Reflect.construct(B, [], Function);
+}
+%PrepareFunctionForOptimization(B);
+construct();
+construct();
+%OptimizeMaglevOnNextCall(B);
+var arr = construct();
+console.log(arr.prototype);
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index d5f6128..2c5227e 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -5347,6 +5347,14 @@
   StoreRegister(iterator_.GetRegisterOperand(0), map_proto);
 }
  
+bool MaglevGraphBuilder::HasValidInitialMap(
+    compiler::JSFunctionRef new_target, compiler::JSFunctionRef constructor) {
if (!new_target.map(broker()).has_prototype_slot()) return false;
if (!new_target.has_initial_map(broker())) return false;
+  compiler::MapRef initial_map = new_target.initial_map(broker());
return initial_map.GetConstructor(broker()).equals(constructor);
+}
+
 void MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
   ValueNode* this_function = LoadRegisterTagged(0);
   ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
               TryGetConstant(new_target);
           if (kind == FunctionKind::kDefaultBaseConstructor) {
             ValueNode* object;
-            if (new_target_function && new_target_function->IsJSFunction()) {
+            if (new_target_function && new_target_function->IsJSFunction() &&
+                HasValidInitialMap(new_target_function->AsJSFunction(),
+                                   current_function)) {
               object = BuildAllocateFastObject(
                   FastObject(new_target_function->AsJSFunction(), zone(),
                              broker()),
diff --git a/src/maglev/maglev-graph-builder.h b/src/maglev/maglev-graph-builder.h
index 0abb4a8..d92354c 100644
--- a/src/maglev/maglev-graph-builder.h
+++ b/src/maglev/maglev-graph-builder.h
@@ -1884,6 +1884,9 @@
   void MergeDeadLoopIntoFrameState(int target);
   void MergeIntoInlinedReturnFrameState(BasicBlock* block);
  
bool HasValidInitialMap(compiler::JSFunctionRef new_target,
+                          compiler::JSFunctionRef constructor);
+
   enum JumpType { kJumpIfTrue, kJumpIfFalse };
   enum class BranchSpecializationMode { kDefault, kAlwaysBoolean };
   JumpType NegateJumpType(JumpType jump_type);
diff --git a/test/mjsunit/maglev/regress/regress-crbug-1465326.js b/test/mjsunit/maglev/regress/regress-crbug-1465326.js
new file mode 100644
index 0000000..6e01c1e
--- /dev/null
+++ b/test/mjsunit/maglev/regress/regress-crbug-1465326.js
@@ -0,0 +1,25 @@
+// Copyright 2023 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --maglev --allow-natives-syntax
+
+class A {}
+
+var x = Function;
+
+class B extends A {
+  constructor() {
+    x = new.target;
+    super();
+  }
+}
+function construct() {
return Reflect.construct(B, [], Function);
+}
+%PrepareFunctionForOptimization(B);
+construct();
+construct();
+%OptimizeMaglevOnNextCall(B);
+var arr = construct();
+console.log(arr.prototype);
[generated bytecode for function: Derived (0x3f5c0019ac6d <SharedFunctionInfo Derived>)]
Bytecode length: 39
Parameter count 1
Register count 7
Frame size 56
Bytecode age: 0
         0x3f5c0019b0be @    0 : 19 fe f9          Mov <closure>, r1
         0x3f5c0019b0c1 @    3 : 5a f9 fa f5       FindNonDefaultConstructorOrConstruct r1, r0, r5-r6
         0x3f5c0019b0c5 @    7 : 0b f5             Ldar r5
         0x3f5c0019b0c7 @    9 : 19 f9 f8          Mov r1, r2
         0x3f5c0019b0ca @   12 : 19 fa f6          Mov r0, r4
         0x3f5c0019b0cd @   15 : 19 f4 f7          Mov r6, r3
         0x3f5c0019b0d0 @   18 : 99 0c             JumpIfTrue [12] (0x3f5c0019b0dc @ 30)
         0x3f5c0019b0d2 @   20 : ae f7             ThrowIfNotSuperConstructor r3
         0x3f5c0019b0d4 @   22 : 0b f6             Ldar r4
         0x3f5c0019b0d6 @   24 : 69 f7 fa 00 00    Construct r3, r0-r0, [0]
         0x3f5c0019b0db @   29 : c2                Star3
         0x3f5c0019b0dc @   30 : 0b 02             Ldar <this>
         0x3f5c0019b0de @   32 : ad                ThrowSuperAlreadyCalledIfNotHole
         0x3f5c0019b0df @   33 : 19 f7 02          Mov r3, <this>
         0x3f5c0019b0e2 @   36 : 0b 02             Ldar <this>
         0x3f5c0019b0e4 @   38 : aa                Return
Constant pool (size = 0)
Handler Table (size = 0)
Source Position Table (size = 0)
[generated bytecode for function: Derived (0x3f5c0019ac6d <SharedFunctionInfo Derived>)]
Bytecode length: 39

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
最新回复 (2)
雪    币: 2561
活跃值: (10620)
能力值: (RANK:438 )
在线值:
发帖
回帖
粉丝
2
感谢分享,高质量文章,写的很全面~
2025-7-7 17:23
0
雪    币: 9923
活跃值: (21475)
能力值: ( LV15,RANK:558 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-7-8 09:04
0
游客
登录 | 注册 方可回帖
返回