summaryrefslogtreecommitdiff
path: root/Userland/DevTools/HackStudio
diff options
context:
space:
mode:
authorFalseHonesty <thefalsehonesty@gmail.com>2021-04-12 21:59:15 -0400
committerLinus Groh <mail@linusgroh.de>2021-04-25 19:03:57 +0200
commit4f2c0e9968dea9a96b65d2f6de65e48d82c02d47 (patch)
treecd7effef5a827cf77794519145ef5dfdfa0192e1 /Userland/DevTools/HackStudio
parent60d329a18624ffc1ce84498ce1378f078cc8baaa (diff)
downloadserenity-4f2c0e9968dea9a96b65d2f6de65e48d82c02d47.zip
HackStudio: Implement custom JS -> C++ "proxy" objects
This patch adds a custom JS Object type that will convert written properties to their C++ equivalents, reflecting JS writes back to the debugging session. This is better than a simple proxy because printing this custom object works as expected because properties still exist on the object as existing handlers expect.
Diffstat (limited to 'Userland/DevTools/HackStudio')
-rw-r--r--Userland/DevTools/HackStudio/CMakeLists.txt3
-rw-r--r--Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.cpp40
-rw-r--r--Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.h1
-rw-r--r--Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.cpp68
-rw-r--r--Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.h35
5 files changed, 111 insertions, 36 deletions
diff --git a/Userland/DevTools/HackStudio/CMakeLists.txt b/Userland/DevTools/HackStudio/CMakeLists.txt
index 7342dc1de8..3bfdf9d24f 100644
--- a/Userland/DevTools/HackStudio/CMakeLists.txt
+++ b/Userland/DevTools/HackStudio/CMakeLists.txt
@@ -10,7 +10,8 @@ set(SOURCES
Debugger/BacktraceModel.cpp
Debugger/DebugInfoWidget.cpp
Debugger/Debugger.cpp
- Debugger/DebuggerGlobalJSObject.cpp
+ Debugger/DebuggerGlobalJSObject.cpp
+ Debugger/DebuggerVariableJSObject.cpp
Debugger/DisassemblyModel.cpp
Debugger/DisassemblyWidget.cpp
Debugger/EvaluateExpressionDialog.cpp
diff --git a/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.cpp b/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.cpp
index eda05da06f..5b1ba7e1b4 100644
--- a/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.cpp
+++ b/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.cpp
@@ -6,6 +6,7 @@
#include "DebuggerGlobalJSObject.h"
#include "Debugger.h"
+#include "DebuggerVariableJSObject.h"
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ProxyObject.h>
@@ -83,45 +84,16 @@ Optional<JS::Value> DebuggerGlobalJSObject::debugger_to_js(const Debug::DebugInf
return JS::Value(value.value() != 0);
}
- auto& global = const_cast<DebuggerGlobalJSObject&>(*this);
-
- auto* object = JS::Object::create_empty(global);
- auto* handler = JS::Object::create_empty(global);
- auto proxy = JS::ProxyObject::create(global, *object, *handler);
-
- auto set = [&](JS::VM& vm, JS::GlobalObject&) {
- auto property = vm.argument(1).value_or(JS::js_undefined());
- if (!property.is_string())
- return JS::Value(false);
- auto property_name = property.as_string().string();
-
- auto value = vm.argument(2).value_or(JS::js_undefined());
- dbgln("prop name {}", property_name);
-
- auto it = variable.members.find_if([&](auto& variable) {
- dbgln("candidate debugger var name: {}", variable->name);
- return variable->name == property_name;
- });
- if (it.is_end())
- return JS::Value(false);
- auto& member = **it;
- dbgln("Found var {}", member.name);
-
- auto new_value = js_to_debugger(value, member);
- Debugger::the().session()->poke((u32*)member.location_data.address, new_value.value());
-
- return JS::Value(true);
- };
-
- handler->define_native_function("set", move(set), 4);
-
+ auto* object = DebuggerVariableJSObject::create(const_cast<DebuggerGlobalJSObject&>(*this), variable);
for (auto& member : variable.members) {
auto member_value = debugger_to_js(member);
if (!member_value.has_value())
continue;
- object->put(member.name, member_value.value());
+ object->put(member.name, member_value.value(), {});
}
- return proxy;
+ object->finish_writing_properties();
+
+ return JS::Value(object);
}
Optional<u32> DebuggerGlobalJSObject::js_to_debugger(JS::Value value, const Debug::DebugInfo::VariableInfo& variable) const
diff --git a/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.h b/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.h
index f8412986e6..5c551d5091 100644
--- a/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.h
+++ b/Userland/DevTools/HackStudio/Debugger/DebuggerGlobalJSObject.h
@@ -23,7 +23,6 @@ public:
JS::Value get(const JS::PropertyName& name, JS::Value receiver, bool without_side_effects) const override;
bool put(const JS::PropertyName& name, JS::Value value, JS::Value receiver) override;
-private:
Optional<JS::Value> debugger_to_js(const Debug::DebugInfo::VariableInfo&) const;
Optional<u32> js_to_debugger(JS::Value value, const Debug::DebugInfo::VariableInfo&) const;
diff --git a/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.cpp b/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.cpp
new file mode 100644
index 0000000000..ecf95de914
--- /dev/null
+++ b/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, Matthew Olsson <matthewcolsson@gmail.com>
+ * Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "DebuggerVariableJSObject.h"
+#include "Debugger.h"
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/PropertyName.h>
+
+namespace HackStudio {
+
+DebuggerVariableJSObject* DebuggerVariableJSObject::create(DebuggerGlobalJSObject& global_object, const Debug::DebugInfo::VariableInfo& variable_info)
+{
+ return global_object.heap().allocate<DebuggerVariableJSObject>(global_object, variable_info, *global_object.object_prototype());
+}
+
+DebuggerVariableJSObject::DebuggerVariableJSObject(const Debug::DebugInfo::VariableInfo& variable_info, JS::Object& prototype)
+ : JS::Object(prototype)
+ , m_variable_info(variable_info)
+{
+}
+
+DebuggerVariableJSObject::~DebuggerVariableJSObject()
+{
+}
+
+bool DebuggerVariableJSObject::put(const JS::PropertyName& name, JS::Value value, JS::Value receiver)
+{
+ if (m_is_writing_properties)
+ return JS::Object::put(name, value, receiver);
+
+ if (!name.is_string()) {
+ vm().throw_exception<JS::TypeError>(global_object(), String::formatted("Invalid variable name {}", name.to_string()));
+ return false;
+ }
+
+ auto property_name = name.as_string();
+ auto it = m_variable_info.members.find_if([&](auto& variable) {
+ return variable->name == property_name;
+ });
+
+ if (it.is_end()) {
+ vm().throw_exception<JS::TypeError>(global_object(), String::formatted("Variable of type {} has no property {}", m_variable_info.type_name, property_name));
+ return false;
+ }
+
+ auto& member = **it;
+ auto new_value = debugger_object().js_to_debugger(value, member);
+ if (!new_value.has_value()) {
+ auto string_error = String::formatted("Cannot convert JS value {} to variable {} of type {}", value.to_string_without_side_effects(), name.as_string(), member.type_name);
+ vm().throw_exception<JS::TypeError>(global_object(), string_error);
+ return false;
+ }
+ Debugger::the().session()->poke((u32*)member.location_data.address, new_value.value());
+ return true;
+}
+
+DebuggerGlobalJSObject& DebuggerVariableJSObject::debugger_object() const
+{
+ return static_cast<DebuggerGlobalJSObject&>(global_object());
+}
+
+}
diff --git a/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.h b/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.h
new file mode 100644
index 0000000000..bdf06360e4
--- /dev/null
+++ b/Userland/DevTools/HackStudio/Debugger/DebuggerVariableJSObject.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, Matthew Olsson <matthewcolsson@gmail.com>
+ * Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "DebuggerGlobalJSObject.h"
+#include <LibDebug/DebugInfo.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace HackStudio {
+
+class DebuggerVariableJSObject final : public JS::Object {
+ JS_OBJECT(DebuggerVariableJSObject, JS::Object);
+
+public:
+ static DebuggerVariableJSObject* create(DebuggerGlobalJSObject&, const Debug::DebugInfo::VariableInfo& variable_info);
+
+ DebuggerVariableJSObject(const Debug::DebugInfo::VariableInfo& variable_info, JS::Object& prototype);
+ virtual ~DebuggerVariableJSObject() override;
+
+ virtual bool put(const JS::PropertyName& name, JS::Value value, JS::Value) override;
+ void finish_writing_properties() { m_is_writing_properties = false; }
+
+private:
+ DebuggerGlobalJSObject& debugger_object() const;
+
+ const Debug::DebugInfo::VariableInfo& m_variable_info;
+ bool m_is_writing_properties { true };
+};
+
+}