How to Enable and Use JavaScriptCore Debugger on iOS: A Step‑by‑Step Guide
Learn how to unlock JavaScriptCore's hidden debugger on iOS by locating the framework version, building the source with private headers, creating a custom Debugger subclass, and integrating it into an Xcode project, complete with code examples and configuration tips.
JSC's debugger is a little‑known feature, especially on iOS where the Objective‑C interface does not expose any debugger information.
Because JavaScriptCore is open source, you can locate the JSC::Debugger abstract class, subclass it, implement the required virtual methods, instantiate it, and attach the instance to the global object to gain debugging capabilities.
On iOS the JavaScriptCore library is delivered as a framework, so only public headers and a static library are available. To use the debugger you must:
Compile with the private headers that match the framework version and use identical compile options.
Link against the framework's internal methods.
Solution steps:
Check the JSC version inside the framework.
Find the matching source code for that version.
Build JavaScriptCore to obtain the private headers.
Create a new Xcode project and add the private headers.
Adjust macros and compile options.
Write the debugger subclass and integration code.
Check JSC Version in the Framework
The framework resides in /System/Library/Frameworks/JavaScriptCore.framework/. Open the version.plist file to read the current version (e.g., 604.4.7.1.3 in Xcode 9.2).
Download Source Code
Visit https://svn.webkit.org/repository/webkit/tags and download the source tree for the identified version. You only need the bmalloc, WTF, and JavaScriptCore directories.
Build JSC
Create an Xcode workspace, drag the three projects into it, and build them in order: bmalloc, WTF, then JavaScriptCore. Build for the macOS target (the default) to obtain the headers; you do not need an iOS build.
Create New Project
Start a new iOS project (any name) and add the private headers you copied from the built JSC project ( PrivateHeaders and WTF/include/wtf) to your project’s include path.
Other C++ flags: -std=c++14 Enable C++ runtime types: No System header search path: $(PRODUCT_NAME)/ Set the required preprocessor macros (e.g., ENABLE_3D_TRANSFORMS, ENABLE_ACCELERATED_OVERFLOW_SCROLLING, …) to match the framework build.
Write the Debugger Subclass
#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(vm) {}
virtual ~MyDebugger() { JSC::Debugger::~Debugger(); }
virtual void sourceParsed(JSC::ExecState* state, JSC::SourceProvider* sourceProvider, int errorLineNumber, const WTF::String& errorMessage) {
// Custom handling of parsed source
NSLog(@"sourceParsed");
}
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");
}
};Use the debugger in your code:
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;"];This sequence creates a JavaScript context, retrieves the underlying JSC objects, installs the custom debugger, enables breakpoints, and finally runs a script that triggers the debugger.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Taobao Frontend Technology
The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
