一.TypedArray类型

TypedArray是漏洞中常见到的结构,手册用法有四

1.new TypedArray(length);
//byteLength=length * sizeof(TypeName);

length

当传入length参数时,一个内部数组缓冲区被创建,该缓存区的大小是传入的length乘以数组中每个元素的字节数,每个元素的值都为0.(译者注:每个元素的字节数是由具体的构造函数决定的,比如Int16Array的每个元素的字节数为2,Int32Array的每个元素的字节数为4)

2.new TypedArray(typedArray);

typedArray

当传入一个包含任意类型元素的任意类型化数组对象(typedArray) (比如 Int32Array)作为参数时,typeArray被复制到一个新的类型数组。typeArray中的每个值会在复制到新的数组之前根据构造器进行转化.新的生成的类型化数组对象将会有跟传入的数组相同的length(译者注:比如原来的typeArray.length==2,那么新生成的数组的length也是2,只是数组中的每一项进行了转化)

3.new TypedArray(object);

object

当传入一个 object 作为参数时,如同通过 TypedArray.from() 方法一样创建一个新的类型数组。

4.new TypedArray(buffer [, byteOffset [, length]]);
//最常见用法,byteOffset、length是字节数

buffer[, byteOffset, length]

当传入arrayBuffer和可选参数byteOffset,可选参数length时,一个新的类型化数组视图将会被创建,该类型化数组视图用于呈现传入的ArrayBuffer实例。byteOffset和length指定类型化数组视图暴露的内存范围,如果两者都未传入,那么整个buffer都会被呈现,如果仅仅忽略length,那么buffer中偏移(byteOffset)后剩下的buffer将会被呈现.

//MDN规定的类型
Int8Array();
Uint8Array();
Uint8ClampedArray();
Int16Array();
Uint16Array();
Int32Array();
Uint32Array();
Float32Array();
Float64Array(); //但是Chakra在实现上定义如下更多的类型 OBJECT_TYPE(UninitializedObject ) //未初始化时就是这种 // Typed arrays that are optimized by the JIT
OBJECT_TYPE(Int8Array )
OBJECT_TYPE(Uint8Array )
OBJECT_TYPE(Uint8ClampedArray )
OBJECT_TYPE(Int16Array )
OBJECT_TYPE(Uint16Array )
OBJECT_TYPE(Int32Array )
OBJECT_TYPE(Uint32Array )
OBJECT_TYPE(Float32Array )
OBJECT_TYPE(Float64Array ) // Virtual Arrays
//Chakra中一种TypedArray对应两种OBJECT_TYPE
OBJECT_TYPE(Int8VirtualArray)
OBJECT_TYPE(Uint8VirtualArray)
OBJECT_TYPE(Uint8ClampedVirtualArray)
OBJECT_TYPE(Int16VirtualArray)
OBJECT_TYPE(Uint16VirtualArray)
OBJECT_TYPE(Int32VirtualArray)
OBJECT_TYPE(Uint32VirtualArray)
OBJECT_TYPE(Float32VirtualArray)
OBJECT_TYPE(Float64VirtualArray) //Mixed Arrays
OBJECT_TYPE(Int8MixedArray)
OBJECT_TYPE(Uint8MixedArray)
OBJECT_TYPE(Uint8ClampedMixedArray)
OBJECT_TYPE(Int16MixedArray)
OBJECT_TYPE(Uint16MixedArray)
OBJECT_TYPE(Int32MixedArray)
OBJECT_TYPE(Uint32MixedArray)
OBJECT_TYPE(Float32MixedArray)
OBJECT_TYPE(Float64MixedArray) // Typed arrays that are not optimized by the JIT
OBJECT_TYPE(Int64Array)
OBJECT_TYPE(Uint64Array)
OBJECT_TYPE(BoolArray)
OBJECT_TYPE(CharArray) // SIMD_JS
// SIMD并不是TypedArray,但是与TypedArray在一起处理
// Only Simd128 sub-types. Currently no need to track top Simd128 type
OBJECT_TYPE(Simd128Float32x4 )
OBJECT_TYPE(Simd128Int32x4 )
OBJECT_TYPE(Simd128Int16x8 )
OBJECT_TYPE(Simd128Int8x16 )
OBJECT_TYPE(Simd128Uint32x4 )
OBJECT_TYPE(Simd128Uint16x8 )
OBJECT_TYPE(Simd128Uint8x16 )
OBJECT_TYPE(Simd128Bool32x4 )
OBJECT_TYPE(Simd128Bool16x8 )
OBJECT_TYPE(Simd128Bool8x16 )
OBJECT_TYPE(Simd128Float64x2 ) // !! This is a marker for last SIMD type. Insert new SIMD types above.

二.正文

var tst = new Uint32Array(0x10000);

1.TypedArray<>::NewInstance

率先执行到

template <typename TypeName, bool clamped, bool virtualAllocated>
Var TypedArray<TypeName, clamped, virtualAllocated>::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)

TypedArray类是一个template <typename TypeName, bool clamped, bool virtualAllocated>模版类

如此设计是为了TypeName可以指定不同的种类如Uint32Int16

TypedArray<>::NewInstance是一个public static方法,提供外部调用创建TypedArray

public:
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);

TypedArray::NewInstance上来首先获取ScriptContextThreadContext

Var TypedArray<TypeName, clamped, virtualAllocated>::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
{
function->GetScriptContext()->GetThreadContext()->ProbeStack(Js::Constants::MinStackDefault, function->GetScriptContext());
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();

ThreadContext

ThreadContext在CreateRuntimeCore里,我们可以看到,在创建JsrtRuntime之前我们需要创建一个ThreadContext,而主要的初始化都是在ThreadContext上进行的。在ThreadContext里面,我们还可以看到比JsrtRuntime多得多的成员变量,并且有很多我们都非常感兴趣,比如:

  • 和内存管理相关的Recycler,各种Page Allocator * 和控制流相关的异常信息 * 各种统计信息 *

    我们马上会提到的JsrtContext的主要实现——ScriptContext的列表 * ……等等

可以看出,上面我们提到的Runtime提供的主要功能基本都在ThreadContext里面,可以说它是JsrtRuntime的主要实现。而通过代码我们可以看得到,JsrtRuntime和ThreadContext是一对一的,所以在读ChakraCore的代码时,我们基本可以把他们认为是一个东西。

ScriptContext

虽然在ScriptContext并没有直接被JsrtContext所持有,而是放在了JavascriptLibrary之中,但是我们还是先来看看这个类,因为这个类其实更加重要也更加的靠上层。

在JsrtContext的构造函数里面,我们可以看到第一步就是创建ScriptContext,而在销毁JsrtContext时,其主要做的事情也是由ScriptContext来完成的,可见ScriptContext其实就是JsrtContext的真实实现。(其实看名字我们也看的出来……)

还记得JsrtContext提供的功能么?在ScriptContext中,我们都可以在其成员变量中找到踪迹:

  • globalObject:这个就是浏览器里JavaScript中的window变量。 *

    url:当前ScriptContext的创建者的URL。 * sourceList:用于储存每个ScriptContext中加载的代码。

之后传递TypedArray<>::Create函数指针进入TypedArrayBase::CreateNewInstance

Var object = TypedArrayBase::CreateNewInstance(args, scriptContext, sizeof(TypeName), TypedArray<TypeName, clamped, virtualAllocated>::Create);

2.TypedArrayBase::CreateNewInstance

static Var CreateNewInstance(Arguments& args, ScriptContext* scriptContext, uint32 elementSize, PFNCreateTypedArray pfnCreateTypedArray );

第一次进入TypedArrayBase::CreateNewInstance时,arrayBuffer为空。因此会执行

scriptContext->GetLibrary()->CreateArrayBuffer(byteLength)

if (arrayBuffer != nullptr)
{ }
else
{
// Null arrayBuffer - could be new constructor or copy constructor.
byteLength = elementCount * elementSize;
arrayBuffer = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);
}

其中前两次的函数调用

scriptContext ScriptContext

GetLibrary() JavascriptLibrary

最后的CreateArrayBuffer函数,是从JavascriptLibrary中调用的

ArrayBuffer* JavascriptLibrary::CreateArrayBuffer(uint32 length)

这个函数是

JavascriptArrayBuffer::Create

的简单封装。

3.JavascriptArrayBuffer::Create

JavascriptArrayBuffer* JavascriptArrayBuffer::Create(uint32 length, DynamicType * type)
{
Recycler* recycler = type->GetScriptContext()->GetRecycler();
JavascriptArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptArrayBuffer, length, type);
Assert(result);
recycler->AddExternalMemoryUsage(length);
return result;
}

函数在通过ScriptContext获取到Memory:Recycler之后调用了RecyclerNewFinalized

RecyclerNewFinalized函数内部则调用了经过重载的new运算符,如下

template <typename TAllocator>
_Ret_notnull_
NO_EXPORT(void *) __cdecl
operator new(DECLSPEC_GUARD_OVERFLOW size_t byteSize, TAllocator * alloc, char * (TAllocator::*AllocFunc)(size_t))
{
AssertCanHandleOutOfMemory();
Assert(byteSize != 0);
void * buffer = (alloc->*AllocFunc)(byteSize);
Assume(buffer != nullptr);
return buffer;
}

这里分配了72个字节,使用的是custom heap的内存管理,分配出来的是JavascriptArrayBuffer对象。new在分配了内存之后开始调用JavascriptArrayBuffer的构造函数。

经过一系列构造函数的继承关系后,最后会调用到ArrayBuffer::ArrayBuffer()

这个函数传递了lengthallocator两参数,最后调用buffer = (BYTE*)allocator(length)

template <class Allocator>
ArrayBuffer::ArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) :
ArrayBufferBase(type)
{
buffer = nullptr;
bufferLength = 0;
if (length > MaxArrayBufferLength)
{
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
}
else if (length > 0)
{
Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
if (recycler->ReportExternalMemoryAllocation(length))
{
buffer = (BYTE*)allocator(length);
if (buffer == nullptr)
{
recycler->ReportExternalMemoryFree(length);
}
} if (buffer == nullptr)
{
recycler->CollectNow<CollectOnTypedArrayAllocation>(); if (recycler->ReportExternalMemoryAllocation(length))
{
buffer = (BYTE*)allocator(length);
if (buffer == nullptr)
{
recycler->ReportExternalMemoryFailure(length);
}
}
} if (buffer != nullptr)
{
bufferLength = length;
ZeroMemory(buffer, bufferLength);
}
else
{
JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
}
}
}

注意这里的构造函数是这样进行传参的

JavascriptArrayBuffer::JavascriptArrayBuffer(uint32 length, DynamicType * type) :
ArrayBuffer(length, type, IsValidVirtualBufferLength(length) ? AsmJsVirtualAllocator : malloc)
{
}

AsmJsVirtualAllocator是一个宏

#define AsmJsVirtualAllocator ((AllocWrapperType)Js::ArrayBuffer::AllocWrapper<MAX_ASMJS_ARRAYBUFFER_LENGTH>)

跟进buffer = (BYTE*)allocator(length)之后会进入AllocWrapper这个函数

template<size_t MaxVirtualSize = MAX_ASMJS_ARRAYBUFFER_LENGTH>
static void* __cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length)
{
LPVOID address = VirtualAlloc(nullptr, MaxVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
//throw out of memory
if (!address)
{
return nullptr;
} if (length == 0)
{
return address;
} LPVOID arrayAddress = VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
if (!arrayAddress)
{
VirtualFree(address, 0, MEM_RELEASE);
return nullptr;
}
return arrayAddress;
} #define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 // 4GB

注意这个函数两次调用了VirtualAlloc,第一次是RESERVE,第二次是COMMIT。分配的思路就是无论申请多大内存,只要满足VirtualArray的范围那么就RESERVE 4GB的地址空间,之后再有需要多少直接COMMIT就可以了。

之后再跳回到ArrayBuffer::ArrayBuffer中,执行ZeroMemory清空分配出来的内存。

这里实现的是VirtualBuffer的分配

if (buffer != nullptr)
{
bufferLength = length;
ZeroMemory(buffer, bufferLength);
}

4.TypedArray<>::Create

在经过上面的一系列分配之后,执行流程返回到TypedArrayBase::CreateNewInstance函数中去。

之后在TypedArrayBase::CreateNewInstance函数中执行了如下流程

byteLength = elementCount * elementSize;    

if (mappedLength == -1)
{
mappedLength = (byteLength - offset)/elementSize;
} // Create and set the array based on the source.
TypedArrayBase* newArray = static_cast<TypedArrayBase*>(pfnCreateTypedArray(arrayBuffer, offset, mappedLength, scriptContext->GetLibrary()));

mappedLength也就是等于byteLength,之后在调用pfnCreateTypedArray函数时传递了之前创建的arrayBuffer

arrayBuffer = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);

分配出来的arrayBuffer是 ArrayBufferBase*,在后面可以看到ArrayBufferBase对象是建立TypedArray的基础

pfnCreateTypedArray其实是

template <typename TypeName, bool clamped, bool virtualAllocated>
Var TypedArray<TypeName, clamped, virtualAllocated>::Create(ArrayBufferBase* arrayBuffer, uint32 byteOffSet, uint32 mappedLength, JavascriptLibrary* javascriptLibrary)

首先计算mappedByteLength=元素个数*单个元素尺寸,然后计算totalLength=byteOffSet+mappedByteLength

if (UInt32Math::Mul(mappedLength, sizeof(TypeName), &mappedByteLength) ||
UInt32Math::Add(byteOffSet, mappedByteLength, &totalLength) ||
(totalLength > arrayBuffer->GetByteLength()))

之后依然是调用RecyclerNew来分配内存,这个函数依然会调用重载后的new运算符分配64个字节,分配出的内存作为TypedArray view对象

 DynamicType *type = javascriptLibrary->GetTypedArrayType<TypeName, clamped>(0);
return RecyclerNew(javascriptLibrary->GetRecycler(), TypedArray, arrayBuffer, byteOffSet, mappedLength, type)

5.TypedArray<>::TypedArray

在new分配了TypedArray对象的内存后,就调用它的构造函数

template <typename TypeName, bool clamped, bool virtualAllocated>
TypedArray<TypeName, clamped, virtualAllocated>::TypedArray(ArrayBufferBase* arrayBuffer, uint32 byteOffset, uint32 mappedLength, DynamicType* type) :TypedArrayBase(arrayBuffer, byteOffset, mappedLength, sizeof(TypeName), type)

依据不同的Typed类型来设置属性

switch (type->GetTypeId())
{
case TypeIds_Int8Array:
VirtualTableInfo<Int8VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint8Array:
VirtualTableInfo<Uint8VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint8ClampedArray:
VirtualTableInfo<Uint8ClampedVirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Int16Array:
VirtualTableInfo<Int16VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint16Array:
VirtualTableInfo<Uint16VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Int32Array:
VirtualTableInfo<Int32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint32Array:
VirtualTableInfo<Uint32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Float32Array:
VirtualTableInfo<Float32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Float64Array:
VirtualTableInfo<Float64VirtualArray>::SetVirtualTable(this);
break;
default:
break;
}

最后返回的是TypedArray*的指针,至此TypedArray创建成功

对象继承关系

JavascriptArrayBuffer:
ArrayBuffer:
ArrayBufferBase:
DynamicObject:
RecyclableObject:
FinalizableObject

调用总览

1.创建JavascriptArrayBuffer对象

2.创建Virtual Buffer

3.创建TypedArray对象

Virtual buffer创建流程

kernel32.dll!VirtualAlloc
ChakraCore.dll!Js::ArrayBufferBase::AllocWrapper<4294967296>()
ChakraCore.dll!Js::ArrayBuffer::ArrayBuffer<void * __ptr64 (__cdecl*)(unsigned __int64)>()
ChakraCore.dll!Js::JavascriptArrayBuffer::JavascriptArrayBuffer()
ChakraCore.dll!Js::JavascriptArrayBuffer::Create()
ChakraCore.dll!Js::JavascriptLibrary::CreateArrayBuffer()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()

TypedArray创建流程

ChakraCore.dll!Memory::Recycler::AllocWithAttributesInlined<0,0>()
ChakraCore.dll!Memory::Recycler::AllocInlined(unsigned __int64 size)
ChakraCore.dll!operator new<Memory::Recycler>()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::Create()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()

JavascriptArrayBuffer创建流程

ChakraCore.dll!Js::JavascriptArrayBuffer::Create()
ChakraCore.dll!Js::JavascriptLibrary::CreateArrayBuffer()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()

Chakra调试笔记 TypedArray的更多相关文章

  1. Tomcat调试笔记

    调试笔记 在使用Tomcat过程中经常碰到问题,导致tomcat启动失败.如下↓ 由于报错太过笼统,我根本无法找出错误.后来我切换到Console视图下,看到了如下错误信息. 根据报错信息,错误原因是 ...

  2. JavaScript特效(调试笔记)

    JavaScript特效 一.在网页上显示当前的时间日期,例如:“2016年3月26日 星期六”. js源代码: function getTime() { var today = new Date() ...

  3. USB wifi调试笔记

    本文以realtek 8192CU WiFi模块为例,介绍USB wifi在Jelly Bean 4.1的调试笔记. 1.WIFI打不开现象概述 WiFi打不开是指您在UI的settings下选中Wi ...

  4. ida动态调试笔记

    ida动态调试笔记 目标文件:阿里安全挑战赛的第二题 点击打开链接 使用环境:ida6.8点击打开链接,adt bundle点击打开链接 首先打开avd安卓模拟器,界面如下: 在dos下运行adb命令 ...

  5. modbus-poll和modbus-slave工具的学习使用——modbus协议功能码3的解析(及欧姆龙温控器调试笔记)

    最近的项目中使用到了欧姆龙的温控器,里面有很多的通信方式,我们使用的常见的modbus——RTU方式,其他方式我们不使用,其中通信手册上面有很多通信的实例,欧姆龙modbus还区分4字节模式和2字节模 ...

  6. GDB调试笔记

    参考资料:GDB调试精粹及使用实例 # 调试实例 #include <iostream> #include <cstring> using namespace std; ][] ...

  7. 《C#并行编程高级教程》第7章 VS2010任务调试 笔记

    没有什么好说的,主要是将调试模式下的Parallel Tasks窗体和Parallel Stacks窗体.折腾一下应该比看书效果好.(表示自己没有折腾过) 另外值得注意的是,主线程不是一个任务.所以主 ...

  8. SIM900A模块HTTP相关调试笔记

    SIM900A模块使用笔记 更新2018-12-8 正常工作状态: 接线方法: 首先将 AT 写入字符串输入框,然后点击 发送.因为模块波特率默认是 9600,所以两条指令的显示都是没有问题的:如果将 ...

  9. 博世传感器调试笔记(一)----加速度传感器BMA253

    公司是bosch的代理商,最近一段时间一直在公司开发的传感器demo板上调试bosch sensor器件.涉及到的器件有7,8款,类型包括重力加速度.地磁.陀螺仪.温度.湿度.大气压力传感器等.在调试 ...

随机推荐

  1. Ubuntu14.10安装TensorFlow1.0.1

    本文记录了在Ubuntu上安装TensorFlow的步骤.系统环境:Ubuntu14.10 64bitPython版本:Python 2.7.8TensorFlow版:TensorFlow 1.0.1 ...

  2. 04-MySQL的存储引擎和列的常用类型

    1. MySQL中的数据库分类        2. MySQL中的存储引擎 MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并 ...

  3. Keil stm32 printf到Debug窗口

    使用JlinkV8+Keil41.在main.c输入以下代码 #include <stdio.h> #define ITM_Port8(n) (*((volatile unsigned c ...

  4. asp.net EF框架执行原生SQL语句

    1.执行无参数sql: string sql = "select * from IntegralInfo where convert(nvarchar,getdate(),23)='{0}' ...

  5. The connection to adb is down, and a severe error has occured(Android模拟器端口被占用)

    相信不少同学和我一样遇到这个问题,有时候搞的还要重启电脑,那究竟是什么原因导致的呢,很明显,你的端口被占用了,那下面给出终极解决方案 一.首先描述症状,如下图 二.出现问题了,首先确定你的sdk目录是 ...

  6. WEB 服务器 加速缓存比较

    Nginx 相对 Apache httpd 的优点: - 轻量级,同样起web 服务,比apache 占用更少的内存及资源 - 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的 ...

  7. 【Eclipse】Eclipse中打开cmd窗口和terminal窗口

    在IDEA的时候可以直接使用terminal打开类似于cmd窗口的功能,于是想着在eclipse也使用类似的功能. 1.Eclipse打开类似于cmd窗口的功能.(DOS) 1.window——> ...

  8. TrimLeft TrimRight

    strming.TrimLeft();  //将字符串最前面的空格修整掉.当在没有参数的情况下调用时,TrimLeft删除换行符,空格和tab字符. strming.TrimRight()://消除从 ...

  9. Linux文件目录类指令

    ⒈pwd 显示当前工作目录的绝对路径 ⒉ls [Options] [目录或文件] 常用选项: -a:显示当前目录下所有的文件和目录,包括隐藏的. -l:以列表的方式显示信息. ⒊cd [目录的绝对路径 ...

  10. Word打开默认显示缩略图,而不是文档结构图

    So easy! 1.打开Word文档,点击缩略图右侧的"X",关闭缩略图: 2.打开菜单[视图],勾选"文档结构图": 3.关闭当前Word文档: 4.再次打 ...