Skip to content

[WIP]: Feat: add JavaScript debugger support #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 74 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
6d095d3
feat: add debugger core impls.
andycall Nov 29, 2022
c54676e
feat: inject debugger hooks for quickjs runtime.
andycall Dec 1, 2022
809aada
feat: add connect api.
andycall Dec 1, 2022
f2dc857
feat: add test262 github ci.
andycall Dec 1, 2022
10b6626
Committing clang-format changes
Dec 1, 2022
237fc95
feat: add quickjs test262 test
andycall Dec 1, 2022
ea95f84
chore: fix compile.
andycall Dec 1, 2022
b7905d2
feat: add inspector test.
andycall Dec 5, 2022
4be4660
feat: add communicate methods between inspector server debugger.
andycall Dec 6, 2022
7258176
Committing clang-format changes
Dec 6, 2022
15c6f84
fix: fix message read.
andycall Dec 6, 2022
6be49cb
feat: add inspector server.
andycall Dec 12, 2022
c27329c
feat: support waiting for debugger attach.
andycall Dec 14, 2022
358e9fd
fix: fix waiting for debugger.
andycall Dec 15, 2022
a475005
feat: generate dap stringify and parser code from typings.
andycall Dec 15, 2022
f0e809d
feat: support evaluate scripts with new dap protocol.
andycall Dec 28, 2022
ad04861
feat: support customize debugger port.
andycall Jan 5, 2023
756a65b
feat: rewrite debugger core with dap protocol.
andycall Jan 10, 2023
4c564f8
fix: fix generated dap code.
andycall Jan 10, 2023
9640ffa
feat: reimpl setBreakspoints with dap.
andycall Jan 12, 2023
4353e82
fix: fix crash and add exception breakpoints.
andycall Jan 12, 2023
46fb56a
feat: support set breakpoints from vscode client.
andycall Jan 15, 2023
7b0676d
chore: remove hashmap.
andycall Jan 15, 2023
7b4f914
fix: fix empty struct crash.
andycall Jan 16, 2023
d54e313
fix: stop at breakpoint now works.
andycall Jan 16, 2023
2f1f9b7
feat: debugger stackframe works.
andycall Jan 16, 2023
1d9c202
feat: support getting scopes.
andycall Jan 17, 2023
f43c193
feat: debugger get variable mostly works.
andycall Jan 18, 2023
0b3f8e5
feat: optimize variable preview.
andycall Jan 18, 2023
4dfe572
fix: fix message and request leaks.
andycall Jan 19, 2023
49b5947
feat: optimize variable kind and presentation.
andycall Jan 19, 2023
eebdae4
fix: fix inspect variable performance issue.
andycall Jan 19, 2023
c40d63a
fix: variable now works.
andycall Jan 20, 2023
245f774
fix: variable now works.
andycall Jan 20, 2023
1c870db
fix: fix switching stack frames.
andycall Jan 20, 2023
0bc30e0
fix: fix NaN types.
andycall Jan 21, 2023
6561c60
test: fix inspector test.
andycall Jan 22, 2023
ca3a0a2
test: reopen inspector test.
andycall Jan 22, 2023
41f8a30
fix: fix evaluate.
andycall Jan 22, 2023
e075a54
fix: fix `this` is not defined in evaluate.
andycall Jan 22, 2023
69db13a
feat: support completions.
andycall Jan 22, 2023
8b3f224
feat: support debugger keywords.
andycall Jan 23, 2023
4cfd892
feat: support logging to debug console.
andycall Jan 27, 2023
db01180
Committing clang-format changes
Jan 27, 2023
b7b7211
feat: support inspect objects in console.
andycall Jan 28, 2023
245ccb5
feat: optimize completion to support suggestions for object property.
andycall Jan 29, 2023
aa627b1
fix: fix step over needs to operate more than once to next line.
andycall Jan 30, 2023
8828b80
feat: check opcode to skip unnecessary stop.
andycall Feb 2, 2023
48e90ba
feat: add more skiled opcode.
andycall Feb 2, 2023
831b9bd
feat: fix get object class name.
andycall Feb 2, 2023
1e8fa69
feat: add more skipped opcodes.
andycall Feb 3, 2023
03829fb
feat: support logging object and messages when debugger not paused.
andycall Feb 4, 2023
b1a218f
feat: support inspect logging values.
andycall Feb 6, 2023
b384407
fix: fix expand logging objects in console.
andycall Feb 7, 2023
1d8c24c
fix: fix get scope variables.
andycall Feb 7, 2023
517b399
test: add inspector tests.
andycall Feb 7, 2023
b63d0c4
feat: only attach debugger when client connected.
andycall Feb 7, 2023
be330bf
test: fix inspector test.
andycall Feb 7, 2023
d6b3787
feat: hidden the logging key.
andycall Feb 8, 2023
130731f
test: add more test specs.
andycall Feb 9, 2023
076756b
test: add full test specs.
andycall Feb 9, 2023
c7e0889
fix: fix debugger check called when debugger not attached.
andycall Feb 10, 2023
73bf4c0
feat: add source map polyfill.
andycall Feb 12, 2023
9a66c75
feat: add btoa and atob support.
andycall Feb 17, 2023
56f3c6a
Committing clang-format changes
Feb 17, 2023
a82772a
feat: support parse inline source map.
andycall Mar 4, 2023
85a5d50
Committing clang-format changes
Mar 4, 2023
19cc392
fix: compact with mimalloc.
andycall Mar 27, 2023
fee9586
fix: fix exception report.
andycall Mar 28, 2023
57530ec
fix: fix debugger variables inspection.
andycall Mar 28, 2023
d0ce526
chore: add test debugger tests.
andycall Mar 29, 2023
3ae4a7a
fix: fix logger stack trace parsing.
andycall Mar 29, 2023
2144d77
fix: fix mapped column num.
andycall Apr 20, 2023
93240bc
fix: sync codes.
andycall Jul 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/integration_test_flutter.yml
Original file line number Diff line number Diff line change
@@ -164,3 +164,9 @@ jobs:
- name: Check on failures
if: steps.test.outcome != 'success'
run: exit 1
quickjs_test262:
runs-on: ubuntu-latest
steps:
- run: sudo apt install -y libcppunit-dev cmake
- uses: actions/checkout@v2
- run: cd bridge/third_party/quickjs/scripts && bash test.sh
21 changes: 16 additions & 5 deletions bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -49,6 +49,10 @@ if(NOT MSVC)
endif()
endif()

if (${ENABLE_DEBUGGER})
add_definitions(-DENABLE_DEBUGGER=1)
endif()

list(APPEND WEBF_PUBLIC_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/webf_bridge.h
)
@@ -101,7 +105,7 @@ list(APPEND BRIDGE_SOURCE
foundation/native_value.cc
foundation/native_type.cc
foundation/ui_command_buffer.cc
polyfill/dist/polyfill.cc
polyfill/dist/polyfill.c
${CMAKE_CURRENT_LIST_DIR}/third_party/dart/include/dart_api_dl.c
)

@@ -130,6 +134,9 @@ list(APPEND BRIDGE_INCLUDE
${ADDITIONAL_INCLUDE_DIRS}
)

add_library(modb STATIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/modp_b64/modp_b64.cc)
list(APPEND BRIDGE_LINK_LIBS modb)

if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
add_compile_options(-DWEBF_QUICK_JS_ENGINE=1)

@@ -138,10 +145,6 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
OUTPUT_VARIABLE QUICKJS_VERSION
)

add_library(modb STATIC
third_party/modp_b64/modp_b64.cc
)

if (NOT MSVC)
list(APPEND QUICK_JS_SOURCE third_party/quickjs/src/libbf.c)
endif()
@@ -160,6 +163,10 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
third_party/quickjs/src/core/gc.c
third_party/quickjs/src/core/malloc.c
third_party/quickjs/src/core/shape.c
third_party/quickjs/out/dap_converter.c
third_party/quickjs/out/dap_protocol.cc
third_party/quickjs/out/sourcemap.c
third_party/quickjs/src/core/debugger.c
third_party/quickjs/src/core/parser.c
third_party/quickjs/src/core/convertion.c
third_party/quickjs/src/core/runtime.c
@@ -211,7 +218,11 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
target_link_libraries(quickjs mimalloc-static)
endif()

target_link_libraries(quickjs modb)

target_include_directories(quickjs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quickjs/include)
target_include_directories(quickjs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quickjs/out)
target_include_directories(quickjs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/modp_b64/include)

if (MSVC)
target_include_directories(quickjs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quickjs/compat/win32/pthreads)
2 changes: 1 addition & 1 deletion bridge/core/executing_context.cc
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context,
// nativePerformance.mark(PERF_JS_POLYFILL_INIT_START);
//#endif

initWebFPolyFill(this);
initWebFPolyFill(ctx);

for (auto& p : plugin_byte_code) {
EvaluateByteCode(p.second.bytes, p.second.length);
2 changes: 2 additions & 0 deletions bridge/core/executing_context_data.cc
Original file line number Diff line number Diff line change
@@ -69,6 +69,8 @@ JSValue ExecutionContextData::constructorForIdSlowCase(const WrapperTypeInfo* ty

// Configure to be called as a constructor.
JS_SetConstructorBit(ctx, classObject, true);
JS_DefinePropertyValue(ctx, classObject, JS_ATOM_length, JS_NewInt32(ctx, 0), JS_PROP_CONFIGURABLE);
JS_DefinePropertyValue(ctx, classObject, JS_ATOM_name, JS_NewString(ctx, type->className), JS_PROP_CONFIGURABLE);

// Store WrapperTypeInfo as private data.
JS_SetOpaque(classObject, (void*)type);
13 changes: 13 additions & 0 deletions bridge/core/executing_context_test.cc
Original file line number Diff line number Diff line change
@@ -355,3 +355,16 @@ TEST(jsValueToNativeString, utf8String) {
}
JS_FreeValue(env->page()->GetExecutingContext()->ctx(), str);
}

TEST(ContextData, constructorName) {
static bool errorHandlerExecuted = false;
static bool logCalled = false;
auto errorHandler = [](int32_t contextId, const char* errmsg) {
errorHandlerExecuted = true;
WEBF_LOG(VERBOSE) << errmsg;
};
auto bridge = TEST_init(errorHandler);
const char* code = "console.assert(Object.getPrototypeOf(window).constructor.name === 'Window')";
bridge->page()->evaluateScript(code, strlen(code), "file://", 0);
EXPECT_EQ(errorHandlerExecuted, false);
}
40 changes: 40 additions & 0 deletions bridge/core/frame/console.cc
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
* Copyright (C) 2022-present The WebF authors. All rights reserved.
*/
#include "console.h"
#include <quickjs/quickjs.h>
#include <sstream>
#include "built_in_string.h"
#include "foundation/logging.h"
@@ -27,4 +28,43 @@ void Console::__webf_print__(ExecutingContext* context, const AtomicString& log,
printLog(context, stream, "info", nullptr);
}

void Console::__webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
ExceptionState& exception_state) {
__webf_debug_inspect_vars__(context, value, built_in_string::kempty_string, built_in_string::kempty_string, 0, 0,
exception_state);
}
void Console::__webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& filepath,
ExceptionState& exception_state) {
__webf_debug_inspect_vars__(context, value, filepath, built_in_string::kempty_string, 0, 0, exception_state);
}
void Console::__webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& filepath,
const AtomicString& file_name,
ExceptionState& exception_state) {
__webf_debug_inspect_vars__(context, value, filepath, file_name, 0, 0, exception_state);
}
void Console::__webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& filepath,
const AtomicString& file_name,
int64_t lineno,
ExceptionState& exception_state) {
__webf_debug_inspect_vars__(context, value, filepath, file_name, lineno, 0, exception_state);
}

void Console::__webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& filepath,
const AtomicString& file_name,
int64_t lineno,
int64_t column,
ExceptionState& exception_state) {
JS_DebuggerInspectValue(context->ctx(), value.QJSValue(), filepath.ToStdString(context->ctx()).c_str(),
file_name.ToStdString(context->ctx()).c_str(), lineno, column);
}

} // namespace webf
1 change: 1 addition & 0 deletions bridge/core/frame/console.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
declare const __webf_print__: (log: string, level?: string) => void;
declare const __webf_debug_inspect_vars__: (value: any, filepath?: string, filename?: string, lineno?: int64, column?: int64) => void;
26 changes: 26 additions & 0 deletions bridge/core/frame/console.h
Original file line number Diff line number Diff line change
@@ -18,6 +18,32 @@ class Console final {
const AtomicString& level,
ExceptionState& exception);
static void __webf_print__(ExecutingContext* context, const AtomicString& log, ExceptionState& exception_state);

static void __webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
ExceptionState& exception_state);
static void __webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& file_path,
ExceptionState& exception_state);
static void __webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& file_path,
const AtomicString& filename,
ExceptionState& exception_state);
static void __webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& file_path,
const AtomicString& filename,
int64_t lineno,
ExceptionState& exception_state);
static void __webf_debug_inspect_vars__(ExecutingContext* context,
const ScriptValue& value,
const AtomicString& file_path,
const AtomicString& filename,
int64_t lineno,
int64_t column,
ExceptionState& exception_state);
};

} // namespace webf
12 changes: 12 additions & 0 deletions bridge/core/frame/console_test.cc
Original file line number Diff line number Diff line change
@@ -21,6 +21,18 @@ TEST(Console, rawPrintShouldWork) {
EXPECT_EQ(logExecuted, true);
}

TEST(Console, debugInspect) {
static bool logExecuted = false;
webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
logExecuted = true;
EXPECT_STREQ(message.c_str(), "123");
};
auto bridge = TEST_init();
const char* code = "function f() { console.log(123); } f();";
bridge->page()->evaluateScript(code, strlen(code), "/tmp/index.js", 0);
EXPECT_EQ(logExecuted, true);
}

TEST(Console, log) {
static bool logExecuted = false;
webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
27 changes: 10 additions & 17 deletions bridge/foundation/logging.cc
Original file line number Diff line number Diff line change
@@ -17,13 +17,6 @@
#include <iostream>
#endif

#if ENABLE_DEBUGGER
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/ConsoleTypes.h>
#include <JavaScriptCore/JSGlobalObject.h>
#include "inspector/impl/jsc_console_client_impl.h"
#endif

namespace webf {
namespace {

@@ -84,16 +77,16 @@ LogMessage::~LogMessage() {
#endif
}

#ifdef ENABLE_DEBUGGER
void pipeMessageToInspector(JSGlobalContextRef ctx, const std::string message, const JSC::MessageLevel logLevel) {
JSObjectRef globalObjectRef = JSContextGetGlobalObject(ctx);
auto client = JSObjectGetPrivate(globalObjectRef);
if (client && client != ((void*)0x1)) {
auto client_impl = reinterpret_cast<webf::debugger::JSCConsoleClientImpl*>(client);
client_impl->sendMessageToConsole(logLevel, message);
}
};
#endif
//#ifdef ENABLE_DEBUGGER
// void pipeMessageToInspector(JSGlobalContextRef ctx, const std::string message, const JSC::MessageLevel logLevel) {
// JSObjectRef globalObjectRef = JSContextGetGlobalObject(ctx);
// auto client = JSObjectGetPrivate(globalObjectRef);
// if (client && client != ((void*)0x1)) {
// auto client_impl = reinterpret_cast<webf::debugger::JSCConsoleClientImpl*>(client);
// client_impl->sendMessageToConsole(logLevel, message);
// }
//};
//#endif

void printLog(ExecutingContext* context, std::stringstream& stream, std::string level, void* ctx) {
MessageLevel _log_level = MessageLevel::Info;
6 changes: 6 additions & 0 deletions bridge/include/webf_bridge.h
Original file line number Diff line number Diff line change
@@ -77,4 +77,10 @@ void init_dart_dynamic_linking(void* data);
WEBF_EXPORT_C
void register_dart_context_finalizer(Dart_Handle dart_handle, void* dart_isolate_context);

// Debugger API
WEBF_EXPORT_C
void* attachDebugger(void* page, void* methods);
WEBF_EXPORT_C
void flushDebuggerCommands(void* page);

#endif // WEBF_BRIDGE_EXPORT_H
13 changes: 8 additions & 5 deletions bridge/polyfill/package.json
Original file line number Diff line number Diff line change
@@ -3,27 +3,30 @@
"description": "JavaScript polyfill for webf",
"main": "dist/index.js",
"scripts": {
"build": "cross-env NODE_ENV=development rollup --config rollup.config.js && npm run mainToC && npm run testToC",
"build:release": "cross-env NODE_ENV=production rollup --config rollup.config.js && npm run mainToC && npm run testToC",
"mainToC": "node scripts/js_to_c.js -s ../dist/main.js -o ../dist",
"testToC": "node scripts/js_to_c.js -s ../dist/test.js -o ../dist -n TestFramework"
"build": "cross-env NODE_ENV=development rollup --config rollup.config.js && npm run mainToC && npm run testToC && npm run sourceMapToC",
"build:release": "cross-env NODE_ENV=production rollup --config rollup.config.js && npm run mainToC && npm run testToC && npm run sourceMapToC",
"mainToC": "node scripts/js_to_c.js -s ./dist/main.js -o ./dist",
"testToC": "node scripts/js_to_c.js -s ./dist/test.js -o ./dist -n TestFramework",
"sourceMapToC": "node scripts/js_to_c.js -s ./dist/sourcemap.js -o ../third_party/quickjs/out -n SourceMap"
},
"dependencies": {
"@types/raf": "^3.4.0",
"es6-promise": "^4.2.8",
"event-emitter": "^0.3.5",
"expect": "^25.1.0",
"qjsc": "^0.2.11",
"source-map": "^0.6.1",
"ts-jest": "^24.3.0",
"tslib": "^1.11.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-commonjs": "^18.1.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-replace": "^2.3.2",
"@rollup/plugin-typescript": "^4.1.1",
"cross-env": "^7.0.3",
"@types/babel__traverse": "7.18.3",
"@types/source-map": "^0.5.7",
"jest": "^24.9.0",
"rollup": "^2.30.0",
"rollup-plugin-bundle-size": "^1.0.3",
10 changes: 10 additions & 0 deletions bridge/polyfill/rollup.config.js
Original file line number Diff line number Diff line change
@@ -57,5 +57,15 @@ module.exports = [
if (warning.code === 'EVAL') return
warn(warning)
},
},
{
input: 'src/sourcemap/index.js',
output: Object.assign({ file: 'dist/sourcemap.js'}, output),
plugins: [
...plugins,
commonjs(),
NODE_ENV === 'development' ? null : terser(uglifyOptions)
],
context: 'undefined'
}
];
Loading