JSC 的 debugger 是个神奇的东西,在网上资料甚少,几乎完全搜索不到,尤其是 iOS 上,OC 接口没有暴露任何 debugger 信息。
不过好在 JSC 是开源的,通过分析源代码可以找到 JSC::Debugger 这个抽象类,我们继承这个抽象类,然后实现掉虚函数,创建实例并且把它挂载到 global object 即可开启 debug 能力了。
思路是简单的,在 iOS 设备上,我们要面临的另一个问题是它的 JavaScriptCore 本身是以 Framework 的形式而非源代码形式提供的,所以我们只有公开的 OC 头文件和静态库文件。
所以要想使用 debugger,我们需要:
1 编译时使用私有的头文件确保头文件的版本跟 framework 一致
确保编译选项跟 framework 一致
2 链接时链接 framwork 中的方法
如何解决呢?步骤如下:
1 查看 framework 中的 JSC 版本3 根据 JSC 版本,找到对应的源代码
4 构建 JSC 获取私有头文件
5 建立新项目,引入私有 JSC 头文件
6 调整宏和编译选项
7 编写代码
查看 framework 中的 JSC 版本
framework 是一个文件夹(在 XCode 中右键即可打开),可以从tbd 文件中找到系统中库的路径(一般是 /System/Library/Frameworks/JavaScriptCore.framework/ ),然后从 version.plist 中找到当前版本。
在我的 XCode9.2中,找到的版本是 604.4.7.1.3。
下载源代码
在https://svn.webkit.org/repository/webkit/tags 可以找到对应的源代码。
注意一般 opensource.apple.com 中找不到对应版本。
我们并不需要整个 webkit 代码,所以只要下载 source 目录下的 bmalloc, WTF 和 JavaScriptCore 三个项目就够了。
构建 JSC
首先我们需要建立一个 workspace,然后把三个项目文件拖进 workspace。
依次构建 bmalloc、WTF 和 JavaScriptCore 三个项目即可。
如果配置正确,构建 JSC 应该不会遇到什么困难。
我们只需要构建好的头文件,所以不需要选择 iOS 设备,使用默认的 mac 作为目标就好了。
建立新项目
接下来我们要建立一个 Debugger 项目,随便叫什么名字,选择 iOS 项目。
我们需要调整编译选项:
- other C++ flags: -std=c++14
- enable C++ runtime Types: No
- system header search path: $(PRODUCT_NAME)/
- Processor Macros:(太多了,建议直接到项目文件源代码里修改)
ENABLE3DTRANSFORMS, ENABLEACCELERATEDOVERFLOWSCROLLING, ENABLEAPPLEPAY,
ENABLEAPPLEPAYSESSIONV3, ENABLEATTACHMENTELEMENT, ENABLEAVFCAPTIONS,
ENABLECACHEPARTITIONING, ENABLECANVASPATH, ENABLECANVASPROXY,
ENABLECHANNELMESSAGING, ENABLECONTENTFILTERING, ENABLECSSANIMATIONSLEVEL2,
ENABLECSSBOXDECORATIONBREAK, ENABLECSSCOMPOSITING, ENABLECSSDEVICEADAPTATION,
ENABLECSSIMAGEORIENTATION, ENABLECSSIMAGERESOLUTION, ENABLECSSREGIONS,
ENABLECSSSCROLLSNAP, ENABLECSSSELECTORSLEVEL4, ENABLECSSTRAILINGWORD,
ENABLECSS3TEXT, ENABLECURSORVISIBILITY, ENABLECUSTOMSCHEMEHANDLER,
ENABLEDASHBOARDSUPPORT, ENABLEDATAINTERACTION, ENABLEDATATRANSFERITEMS,
ENABLEDATACUEVALUE, ENABLEDATALISTELEMENT, ENABLEDEVICEORIENTATION,
ENABLEDRAGSUPPORT, ENABLEENCRYPTEDMEDIA, ENABLEFETCHAPI, ENABLEFILTERSLEVEL2,
ENABLEFTLJIT, ENABLEFULLSCREENAPI, ENABLEGAMEPADDEPRECATED, ENABLEGAMEPAD,
ENABLEGEOLOCATION, ENABLEICONDATABASE, ENABLEINDEXEDDATABASEINWORKERS,
ENABLEINDEXEDDATABASE, ENABLEINPUTTYPECOLORPOPOVER, ENABLEINPUTTYPECOLOR,
ENABLEINPUTTYPEDATE, ENABLEINPUTTYPEDATETIMEINCOMPLETE, ENABLEINPUTTYPEDATETIMELOCAL,
ENABLEINPUTTYPEMONTH, ENABLEINPUTTYPETIME, ENABLEINPUTTYPEWEEK, ENABLEINTERSECTIONOBSERVER,
ENABLEINTL, ENABLEIOSGESTUREEVENTS, ENABLEIOSTOUCHEVENTS, ENABLEJIT,
ENABLEKEYBOARDKEYATTRIBUTE, ENABLEKEYBOARDCODEATTRIBUTE, ENABLELEGACYCSSVENDORPREFIXES,
ENABLELEGACYENCRYPTEDMEDIA, ENABLELEGACYVENDORPREFIXES, ENABLELETTERPRESS,
ENABLELINKPREFETCH, ENABLEMACGESTUREEVENTS, ENABLEMATHML, ENABLEMEDIACAPTURE,
ENABLEMEDIACONTROLSSCRIPT, ENABLEMEDIASESSION, ENABLEMEDIASOURCE, ENABLEMEDIASTATISTICS,
ENABLEMEDIASTREAM, ENABLEMETERELEMENT, ENABLEMHTML, ENABLEMOUSECURSORSCALE,
ENABLENAVIGATORCONTENTUTILS, ENABLENAVIGATORSTANDALONE, ENABLENOTIFICATIONS,
ENABLEPDFKITPLUGIN, ENABLEPOINTERLOCK, ENABLEPROXIMITYEVENTS, ENABLEPUBLICSUFFIXLIST,
ENABLEQUOTA, ENABLEREMOTEINSPECTOR, ENABLEREQUESTAUTOCOMPLETE, ENABLERESOLUTIONMEDIAQUERY,
ENABLERESOURCEUSAGE, ENABLERUBBERBANDING, ENABLESERVICECONTROLS, ENABLESPEECHSYNTHESIS,
ENABLESTREAMSAPI, ENABLESUBTLECRYPTO, ENABLESVGFONTS, ENABLETELEPHONENUMBERDETECTION,
ENABLETEXTAUTOSIZING, ENABLETOUCHEVENTS, ENABLETOUCHICONLOADING, ENABLEUSERSELECTALL,
ENABLEVARIATIONFONTS, ENABLEVIDEOPRESENTATIONMODE, ENABLEMACVIDEOTOOLBOX, ENABLEVIDEOTRACK,
ENABLEVIDEO, ENABLEVIEWMODECSSMEDIA, ENABLEWEBANIMATIONS, ENABLEWEBAUDIO, ENABLEWEBRTC,
ENABLEWEBSOCKETS, ENABLEWEBTIMING, ENABLEWEBGL, ENABLEWEBGL2, ENABLEWEBGPU,
ENABLEWIRELESSPLAYBACKTARGET, ENABLEXSLTFASTJITPERMISSIONS
然后我们打开构建好的 JSC 项目目标, 复制其中 PrivateHeaders 目录,到项目目录的 JavaScriptCore 目录。
再打开 WTF 项目目标, 复制目录下 /usr/local/include/wtf
接下来,我们需要对源代码做一下小修改,因为系统的 JSC 是在非 debug 模式下编译的,所以我们强行把头文件中跟 debug 相关的代码改成非 debug 模式:
JavaScriptCore/HandleStack.
所有
#ifdef
NDEBUG
WTF/hashtable.h
#ifdef
NDEBUG
#define
CHECK_HASHTABLE_ITERATORS
0
#define
CHECK_HASHTABLE_USE_AFTER_DESTRUCTION
0
#else
#define
CHECK_HASHTABLE_ITERATORS
0
#define
CHECK_HASHTABLE_USE_AFTER_DESTRUCTION
0
#endif
编写代码
代码必须使用 .mm 文件。
我们需要在项目的 build phases 中加入 JavaScriptCore.framework。
#import <JavaScriptCore/JavaScriptCore.h>
#import "JavaScriptCore/HeapInlines.h"
#import "JavaScriptCore/HeapCellInlines.h"
#import "JavaScriptCore/APICast.h"
#import "JavaScriptCore/Debugger.h"
#import "JavaScriptCore/SourceProvider.h"
#import "JavaScriptCore/JSRunLoopTimer.h"
#import "JavaScriptCore/JSVirtualMachineInternal.h"
class
MyDebugger
:
public
JSC
::
Debugger
{
public
:
MyDebugger
(
JSC
::
VM
&
vm
)
:
JSC
::
Debugger
::
Debugger
(
vm
){
}
virtual
~
MyDebugger
(){
JSC
::
Debugger
::~
Debugger
();
}
/*virtual void recompileAllJSFunctions() {
//JSC::Debugger::recompileAllJSFunctions();
}*/
virtual
void
sourceParsed
(
JSC
::
ExecState
*
state
,
JSC
::
SourceProvider
*
sourceProvider
,
int
errorLineNumber
,
const
WTF
::
String
&
errorMessage
)
{
//StringView sourceString = sourceProvider->source();
//NSLog(@"sourceParsed:%@", (NSString*)sourceString.toString());
NSLog
(@
"sourceParsed"
);
return
;
};
virtual
void
handleBreakpointHit
(
JSC
::
JSGlobalObject
*,
const
JSC
::
Breakpoint
&)
{
NSLog
(@
"handleBreakpointHit"
);
}
virtual
void
handleExceptionInBreakpointCondition
(
JSC
::
ExecState
*,
JSC
::
Exception
*)
const
{
NSLog
(@
"handleExceptionInBreakpointCondition"
);
}
virtual
void
handlePause
(
JSC
::
JSGlobalObject
*
globalObject
,
ReasonForPause
reason
)
{
NSLog
(@
"handlePause"
);
}
virtual
void
notifyDoneProcessingDebuggerEvents
()
{
NSLog
(@
"notifyDoneProcessingDebuggerEvents"
);
}
};
使用示例:
JSContext
*
jsContext
=
[[
JSContext
alloc
]
init
];
JSGlobalContextRef
globalContext
=
[
jsContext
JSGlobalContextRef
];
JSC
::
ExecState
*
es
=
toJS
(
globalContext
);
JSC
::
JSGlobalObject
*
globalObject
=
es
->
vmEntryGlobalObject
();
MyDebugger
*
debugger
=
new
MyDebugger
(
globalObject
->
vm
());
globalObject
->
setDebugger
(
static_cast
<
JSC
::
Debugger
*>(
debugger
));
debugger
->
setPauseOnNextStatement
(
TRUE
);
globalObject
->
vm
().
heap
.
acquireAccess
();
debugger
->
activateBreakpoints
();
globalObject
->
vm
().
heap
.
releaseAccess
();
[
jsContext evaluateScript
:@
"debugger;"
];
原文发布时间为:2018-02-06
本文作者:寒泉