JS!!!

This post is a collection of notes on how to get started hacking on SpiderMonkey, I mean holy shit, did I just change JS!?!!

Gotta get the code

Mozilla Central is officially hosted in a public mercurial repo, but it’s easier to get the git mirror on github

git clone https://github.com/mozilla/gecko-dev.git

Directories

The key directory is js/src, but you’ll quickly make an out directory and build JS into it.

js/src/vm has some crazy cool files in it like interpreter.cpp (i wonder what that does)?!?!

Getting started

cd js/src
mkdir out
cd out

../configure # configure directory
make -j8 -s  # build your very own JS

Write some code

I started working on adding watch points to the debugger. Here is some code for adding the debugger api:

diff --git a/js/src/jit-test/tests/debug/watchProperty-01.js b/js/src/jit-test/tests/debug/watchProperty-01.js
new file mode 100644
index 0000000..76df4c0
--- /dev/null
+++ b/js/src/jit-test/tests/debug/watchProperty-01.js
@@ -0,0 +1,23 @@
+// In a debuggee with live objects, findObjects finds those objects.
+
+var g = newGlobal();
+
+let defObject = v => g.eval(`this.${v} = { toString: () => "[object ${v}]" }`);
+defObject("a");
+// defObject("b");
+// defObject("c");
+
+var dbg = new Debugger();
+
+var gw = dbg.addDebuggee(g);
+
+var aw = gw.makeDebuggeeValue(g.a);
+aw.watchProperty(1);
+
+// var bw = gw.makeDebuggeeValue(g.b);
+// var cw = gw.makeDebuggeeValue(g.c);
+
+// assertEq(dbg.findObjects().indexOf(aw) != -1, true);
+// assertEq(dbg.findObjects().indexOf(bw) != -1, true);
+// assertEq(dbg.findObjects().indexOf(cw) != -1, true);
+assertEq(1,1)
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index 67d42d8..c0f14e8 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6433,6 +6433,7 @@ static bool
 DebuggerScript_setBreakpoint(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script);
+    printf(">>> hello\n");
     if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2))
         return false;
     Debugger* dbg = Debugger::fromChildJSObject(obj);
@@ -9251,6 +9252,21 @@ DebuggerObject::makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp)
     return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
 }

+
+/* static */ bool
+DebuggerObject::watchPropertyMethod(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGOBJECT(cx, argc, vp, "watchProperty", args, object);
+    // if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue", 1))
+    //     return false;
+
+    printf("HI!\n");
+    return true;
+    //
+    // return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
+}
+
+
 /* static */ bool
 DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -9338,6 +9354,7 @@ const JSFunctionSpec DebuggerObject::methods_[] = {
     JS_FN("executeInGlobal", DebuggerObject::executeInGlobalMethod, 1, 0),
     JS_FN("executeInGlobalWithBindings", DebuggerObject::executeInGlobalWithBindingsMethod, 2, 0),
     JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
+    JS_FN("watchProperty", DebuggerObject::watchPropertyMethod, 1, 0),
     JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
     JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
     JS_FS_END
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
index 0fd5997..d334c62 100644
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1230,6 +1230,7 @@ class DebuggerObject : public NativeObject
                                   HandleNativeObject debugger);

     // Properties
+    // (interesting)
     static MOZ_MUST_USE bool getClassName(JSContext* cx, HandleDebuggerObject object,
                                           MutableHandleString result);
     static MOZ_MUST_USE bool getGlobal(JSContext* cx, HandleDebuggerObject object,
@@ -1409,6 +1410,7 @@ class DebuggerObject : public NativeObject
     static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool watchPropertyMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool getErrorReport(JSContext* cx, HandleObject maybeError,

Run our very own JS

You can run your JS, from the JS shell

dist/bin/js

The shell has lots of cool functions that are worth exploring, like looking at your bytecode

Every time you make a change, you’ll make a new build

make -j8 -s

also, don’t forget your semicolons! You might be making JS, but you’re writing CPP, oh god.

Running the tests

Here’s how you run the tests from the out directory

python ../jit-test dist/bin/js debug/breakpoint-05.js

output

[1|0|0|0] 100% ==========================================================>|   0.0s
PASSED ALL

GDB the tests

gdb --args /Users/jlaster/src/mozilla/gecko/js/src/obj/dist/bin/js -f /Users/jlaster/src/mozilla/gecko/js/src/jit-test/lib/prologue.js --js-cache /Users/jlaster/src/mozilla/gecko/js/sr*   8322 c/jit-test/.js-cache -e "const platform='darwin'; const libdir='/Users/jlaster/src/mozilla/gecko/js/src/jit-test/lib/'; const scriptdir='/Users/jlaster/src/mozilla/gecko/js/src/jit-test/tests*   8322 /debug/'" -f /Users/jlaster/src/mozilla/gecko/js/src/jit-test/tests/debug/watchProperty-01.js

Good resources

Here are some good resource I found on spider monkey. I haven’t done much with them

18 Dec 2016