summaryrefslogtreecommitdiffstats
path: root/js/src/gdb/tests
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gdb/tests')
-rw-r--r--js/src/gdb/tests/test-ExecutableAllocator.cpp44
-rw-r--r--js/src/gdb/tests/test-ExecutableAllocator.py17
-rw-r--r--js/src/gdb/tests/test-GCCellPtr.cpp23
-rw-r--r--js/src/gdb/tests/test-GCCellPtr.py10
-rw-r--r--js/src/gdb/tests/test-Interpreter.cpp92
-rw-r--r--js/src/gdb/tests/test-Interpreter.py14
-rw-r--r--js/src/gdb/tests/test-JSObject-null.py6
-rw-r--r--js/src/gdb/tests/test-JSObject.cpp43
-rw-r--r--js/src/gdb/tests/test-JSObject.py22
-rw-r--r--js/src/gdb/tests/test-JSString-null.py6
-rw-r--r--js/src/gdb/tests/test-JSString-subclasses.py5
-rw-r--r--js/src/gdb/tests/test-JSString.cpp64
-rw-r--r--js/src/gdb/tests/test-JSString.py23
-rw-r--r--js/src/gdb/tests/test-JSSymbol.cpp20
-rw-r--r--js/src/gdb/tests/test-JSSymbol.py10
-rw-r--r--js/src/gdb/tests/test-Root-null.py20
-rw-r--r--js/src/gdb/tests/test-Root.cpp61
-rw-r--r--js/src/gdb/tests/test-Root.py27
-rw-r--r--js/src/gdb/tests/test-asmjs.cpp38
-rw-r--r--js/src/gdb/tests/test-asmjs.py15
-rw-r--r--js/src/gdb/tests/test-jsid.cpp44
-rw-r--r--js/src/gdb/tests/test-jsid.py19
-rw-r--r--js/src/gdb/tests/test-jsval.cpp40
-rw-r--r--js/src/gdb/tests/test-jsval.py18
-rw-r--r--js/src/gdb/tests/test-prettyprinters.cpp38
-rw-r--r--js/src/gdb/tests/test-prettyprinters.py22
-rw-r--r--js/src/gdb/tests/test-unwind.cpp52
-rw-r--r--js/src/gdb/tests/test-unwind.py58
-rw-r--r--js/src/gdb/tests/typedef-printers.cpp11
-rw-r--r--js/src/gdb/tests/typedef-printers.py14
30 files changed, 876 insertions, 0 deletions
diff --git a/js/src/gdb/tests/test-ExecutableAllocator.cpp b/js/src/gdb/tests/test-ExecutableAllocator.cpp
new file mode 100644
index 000000000..7cb7ad364
--- /dev/null
+++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp
@@ -0,0 +1,44 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include "jscntxt.h"
+
+#include "jit/ExecutableAllocator.h"
+
+FRAGMENT(ExecutableAllocator, empty) {
+ using namespace js::jit;
+ ExecutableAllocator execAlloc(cx->runtime());
+
+ breakpoint();
+
+ (void) execAlloc;
+}
+
+FRAGMENT(ExecutableAllocator, onepool) {
+ using namespace js::jit;
+ ExecutablePool* pool = nullptr;
+ ExecutableAllocator execAlloc(cx->runtime());
+ execAlloc.alloc(16 * 1024, &pool, BASELINE_CODE);
+
+ breakpoint();
+
+ (void) pool;
+ (void) execAlloc;
+}
+
+FRAGMENT(ExecutableAllocator, twopools) {
+ using namespace js::jit;
+ ExecutablePool* init = nullptr;
+ ExecutablePool* pool = nullptr;
+ ExecutableAllocator execAlloc(cx->runtime());
+
+ execAlloc.alloc(16 * 1024, &init, BASELINE_CODE);
+
+ do { // Keep allocating until we get a second pool.
+ execAlloc.alloc(32 * 1024, &pool, ION_CODE);
+ } while (pool == init);
+
+ breakpoint();
+
+ (void) execAlloc;
+}
diff --git a/js/src/gdb/tests/test-ExecutableAllocator.py b/js/src/gdb/tests/test-ExecutableAllocator.py
new file mode 100644
index 000000000..b790e2d34
--- /dev/null
+++ b/js/src/gdb/tests/test-ExecutableAllocator.py
@@ -0,0 +1,17 @@
+# Tests for ExecutableAllocator pretty-printing
+
+assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr')
+
+run_fragment('ExecutableAllocator.empty')
+
+assert_pretty('execAlloc', 'ExecutableAllocator([])')
+
+run_fragment('ExecutableAllocator.onepool')
+
+reExecPool = 'ExecutablePool [a-f0-9]{8,}-[a-f0-9]{8,}'
+assert_regexp_pretty('pool', reExecPool)
+assert_regexp_pretty('execAlloc', 'ExecutableAllocator\(\[' +reExecPool+ '\]\)')
+
+run_fragment('ExecutableAllocator.twopools')
+
+assert_regexp_pretty('execAlloc', 'ExecutableAllocator\(\[' + reExecPool + ', ' + reExecPool + '\]\)')
diff --git a/js/src/gdb/tests/test-GCCellPtr.cpp b/js/src/gdb/tests/test-GCCellPtr.cpp
new file mode 100644
index 000000000..cc5802dbe
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.cpp
@@ -0,0 +1,23 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include "js/HeapAPI.h"
+
+FRAGMENT(GCCellPtr, simple) {
+ JS::GCCellPtr nulll(nullptr);
+
+ JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+ JS::Rooted<JS::Symbol*> unique(cx, JS::NewSymbol(cx, nullptr));
+
+ JS::GCCellPtr object(glob.get());
+ JS::GCCellPtr string(empty.get());
+ JS::GCCellPtr symbol(unique.get());
+
+ breakpoint();
+
+ (void) nulll;
+ (void) object;
+ (void) string;
+ (void) symbol;
+}
diff --git a/js/src/gdb/tests/test-GCCellPtr.py b/js/src/gdb/tests/test-GCCellPtr.py
new file mode 100644
index 000000000..d5bb3d1e1
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.py
@@ -0,0 +1,10 @@
+# Tests for GCCellPtr pretty-printing
+
+assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr')
+
+run_fragment('GCCellPtr.simple')
+
+assert_pretty('nulll', 'JS::GCCellPtr(nullptr)')
+assert_pretty('object', 'JS::GCCellPtr((JSObject*) )')
+assert_pretty('string', 'JS::GCCellPtr((JSString*) )')
+assert_pretty('symbol', 'JS::GCCellPtr((JS::Symbol*) )')
diff --git a/js/src/gdb/tests/test-Interpreter.cpp b/js/src/gdb/tests/test-Interpreter.cpp
new file mode 100644
index 000000000..9e206ff6a
--- /dev/null
+++ b/js/src/gdb/tests/test-Interpreter.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include "vm/Stack.h"
+
+namespace js {
+
+void
+GDBTestInitInterpreterRegs(InterpreterRegs& regs,
+ js::InterpreterFrame* fp_,
+ JS::Value* sp,
+ uint8_t* pc)
+{
+ regs.fp_ = fp_;
+ regs.sp = sp;
+ regs.pc = pc;
+}
+
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, void* ptr)
+{
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_ScriptFrameIterData;
+}
+
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, InterpreterFrame* ptr)
+{
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_InterpreterFrame;
+}
+
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::BaselineFrame* ptr)
+{
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_BaselineFrame;
+}
+
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::RematerializedFrame* ptr)
+{
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame;
+}
+
+} // namespace js
+
+FRAGMENT(Interpreter, Regs) {
+ struct FakeFrame {
+ js::InterpreterFrame frame;
+ JS::Value slot0;
+ JS::Value slot1;
+ JS::Value slot2;
+ } fakeFrame;
+ uint8_t fakeOpcode = JSOP_IFEQ;
+
+ js::InterpreterRegs regs;
+ js::GDBTestInitInterpreterRegs(regs, &fakeFrame.frame, &fakeFrame.slot2, &fakeOpcode);
+
+ breakpoint();
+
+ (void) regs;
+}
+
+FRAGMENT(Interpreter, AbstractFramePtr) {
+
+ js::AbstractFramePtr sfidptr;
+ GDBTestInitAbstractFramePtr(sfidptr, (js::ScriptFrameIter::Data*) uintptr_t(0xdeeb0));
+
+ js::AbstractFramePtr ifptr;
+ GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) uintptr_t(0x8badf00));
+
+ js::AbstractFramePtr bfptr;
+ GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) uintptr_t(0xbadcafe0));
+
+ js::AbstractFramePtr rfptr;
+ GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) uintptr_t(0xdabbad00));
+
+ breakpoint();
+
+ (void) sfidptr;
+ (void) ifptr;
+ (void) bfptr;
+ (void) rfptr;
+}
diff --git a/js/src/gdb/tests/test-Interpreter.py b/js/src/gdb/tests/test-Interpreter.py
new file mode 100644
index 000000000..c1cf87c66
--- /dev/null
+++ b/js/src/gdb/tests/test-Interpreter.py
@@ -0,0 +1,14 @@
+# Test printing interpreter internal data structures.
+
+assert_subprinter_registered('SpiderMonkey', 'js::InterpreterRegs')
+
+run_fragment('Interpreter.Regs')
+
+assert_pretty('regs', '{ fp_ = , sp = fp_.slots() + 2, pc = (JSOP_IFEQ) }')
+
+run_fragment('Interpreter.AbstractFramePtr')
+
+assert_pretty('sfidptr', 'AbstractFramePtr ((js::ScriptFrameIter::Data *) ) = {ptr_ = 913072}')
+assert_pretty('ifptr', 'AbstractFramePtr ((js::InterpreterFrame *) ) = {ptr_ = 146464513}')
+assert_pretty('bfptr', 'AbstractFramePtr ((js::jit::BaselineFrame *) ) = {ptr_ = 3135025122}')
+assert_pretty('rfptr', 'AbstractFramePtr ((js::jit::RematerializedFrame *) ) = {ptr_ = 3669732611}')
diff --git a/js/src/gdb/tests/test-JSObject-null.py b/js/src/gdb/tests/test-JSObject-null.py
new file mode 100644
index 000000000..40721b2e2
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject-null.py
@@ -0,0 +1,6 @@
+gdb.execute('set print address on')
+
+run_fragment('JSObject.null')
+
+assert_pretty('null', '0x0')
+assert_pretty('nullRaw', '0x0')
diff --git a/js/src/gdb/tests/test-JSObject.cpp b/js/src/gdb/tests/test-JSObject.cpp
new file mode 100644
index 000000000..2be10e89c
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject.cpp
@@ -0,0 +1,43 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+FRAGMENT(JSObject, simple) {
+ JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSObject*> plain(cx, JS_NewPlainObject(cx));
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSObject*> func(cx, (JSObject*) JS_NewFunction(cx, (JSNative) 1, 0, 0,
+ "dys"));
+ JS::Rooted<JSObject*> anon(cx, (JSObject*) JS_NewFunction(cx, (JSNative) 1, 0, 0, nullptr));
+ JS::Rooted<JSFunction*> funcPtr(cx, JS_NewFunction(cx, (JSNative) 1, 0, 0,
+ "formFollows"));
+
+ JSObject& plainRef = *plain;
+ JSFunction& funcRef = *funcPtr;
+ JSObject* plainRaw = plain;
+ JSObject* funcRaw = func;
+
+ static const JSClass cls = { "\xc7X" };
+ JS::RootedObject badClassName(cx, JS_NewObject(cx, &cls));
+
+ breakpoint();
+
+ (void) glob;
+ (void) plain;
+ (void) func;
+ (void) anon;
+ (void) funcPtr;
+ (void) &plainRef;
+ (void) &funcRef;
+ (void) plainRaw;
+ (void) funcRaw;
+}
+
+FRAGMENT(JSObject, null) {
+ JS::Rooted<JSObject*> null(cx, nullptr);
+ JSObject* nullRaw = null;
+
+ breakpoint();
+
+ (void) null;
+ (void) nullRaw;
+}
diff --git a/js/src/gdb/tests/test-JSObject.py b/js/src/gdb/tests/test-JSObject.py
new file mode 100644
index 000000000..3d4c7c37b
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject.py
@@ -0,0 +1,22 @@
+# Printing JSObjects.
+
+assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSObject')
+assert_subprinter_registered('SpiderMonkey', 'ref-to-JSObject')
+
+run_fragment('JSObject.simple')
+
+# These patterns look a little strange because of prologue.py's 'set print
+# address off', which avoids putting varying addresses in the output. After
+# the '(JSObject *) ', there is a 'void *' value printing as the empty
+# string.
+
+assert_pretty('glob', '(JSObject *) [object global] delegate')
+assert_pretty('plain', '(JSObject *) [object Object]')
+assert_pretty('func', '(JSObject *) [object Function "dys"]')
+assert_pretty('anon', '(JSObject *) [object Function <unnamed>]')
+assert_pretty('funcPtr', '(JSFunction *) [object Function "formFollows"]')
+
+assert_pretty('badClassName', '(JSObject *) [object \\307X]')
+
+assert_pretty('plainRef', '(JSObject &) @ [object Object]')
+assert_pretty('funcRef', '(JSFunction &) @ [object Function "formFollows"]')
diff --git a/js/src/gdb/tests/test-JSString-null.py b/js/src/gdb/tests/test-JSString-null.py
new file mode 100644
index 000000000..eec3e03ba
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString-null.py
@@ -0,0 +1,6 @@
+gdb.execute('set print address on')
+
+run_fragment('JSString.null')
+
+assert_pretty('null', '0x0')
+assert_pretty('nullRaw', '0x0')
diff --git a/js/src/gdb/tests/test-JSString-subclasses.py b/js/src/gdb/tests/test-JSString-subclasses.py
new file mode 100644
index 000000000..4539cd145
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString-subclasses.py
@@ -0,0 +1,5 @@
+# We can print pointers to subclasses of JSString.
+
+run_fragment('JSString.subclasses')
+
+assert_pretty('flat', '"Hi!"')
diff --git a/js/src/gdb/tests/test-JSString.cpp b/js/src/gdb/tests/test-JSString.cpp
new file mode 100644
index 000000000..679640bc7
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString.cpp
@@ -0,0 +1,64 @@
+#include "gdb-tests.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+
+// When JSGC_ANALYSIS is #defined, Rooted<JSFlatString*> needs the definition
+// of JSFlatString in order to figure out its ThingRootKind
+#include "vm/String.h"
+
+FRAGMENT(JSString, simple) {
+ JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+ JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1));
+ JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z"));
+
+ // I expect this will be a non-inlined string.
+ JS::Rooted<JSString*> stars(cx, JS_NewStringCopyZ(cx,
+ "*************************"
+ "*************************"
+ "*************************"
+ "*************************"));
+
+ // This may well be an inlined string.
+ JS::Rooted<JSString*> xz(cx, JS_ConcatStrings(cx, x, z));
+
+ // This will probably be a rope.
+ JS::Rooted<JSString*> doubleStars(cx, JS_ConcatStrings(cx, stars, stars));
+
+ // Ensure we're not confused by typedefs for pointer types.
+ JSString* xRaw = x;
+
+ breakpoint();
+
+ (void) empty;
+ (void) x;
+ (void) z;
+ (void) stars;
+ (void) xz;
+ (void) doubleStars;
+ (void) xRaw;
+}
+
+FRAGMENT(JSString, null) {
+ JS::Rooted<JSString*> null(cx, nullptr);
+ JSString* nullRaw = null;
+
+ breakpoint();
+
+ (void) null;
+ (void) nullRaw;
+}
+
+FRAGMENT(JSString, subclasses) {
+ JS::Rooted<JSFlatString*> flat(cx, JS_FlattenString(cx, JS_NewStringCopyZ(cx, "Hi!")));
+
+ breakpoint();
+
+ (void) flat;
+}
+
+FRAGMENT(JSString, atom) {
+ JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10);
+ breakpoint();
+
+ (void) molybdenum;
+}
diff --git a/js/src/gdb/tests/test-JSString.py b/js/src/gdb/tests/test-JSString.py
new file mode 100644
index 000000000..3189f34a4
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString.py
@@ -0,0 +1,23 @@
+# Printing JSStrings.
+
+assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSString')
+run_fragment('JSString.simple')
+
+assert_pretty('empty', '""')
+assert_pretty('x', '"x"')
+assert_pretty('z', '"z"')
+assert_pretty('xz', '"xz"')
+
+stars = gdb.parse_and_eval('stars')
+assert_eq(str(stars), "'*' <repeats 100 times>")
+
+doubleStars = gdb.parse_and_eval('doubleStars')
+assert_eq(str(doubleStars), "'*' <repeats 200 times>")
+
+assert_pretty('xRaw', '"x"')
+
+# JSAtom *
+
+run_fragment('JSString.atom')
+
+assert_pretty('molybdenum', '"molybdenum"')
diff --git a/js/src/gdb/tests/test-JSSymbol.cpp b/js/src/gdb/tests/test-JSSymbol.cpp
new file mode 100644
index 000000000..933f3075c
--- /dev/null
+++ b/js/src/gdb/tests/test-JSSymbol.cpp
@@ -0,0 +1,20 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+FRAGMENT(JSSymbol, simple) {
+ using namespace JS;
+
+ RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
+
+ Rooted<Symbol*> unique(cx, NewSymbol(cx, nullptr));
+ Rooted<Symbol*> unique_with_desc(cx, NewSymbol(cx, hello));
+ Rooted<Symbol*> registry(cx, GetSymbolFor(cx, hello));
+ Rooted<Symbol*> well_known(cx, GetWellKnownSymbol(cx, SymbolCode::iterator));
+
+ breakpoint();
+
+ (void) unique;
+ (void) unique_with_desc;
+ (void) registry;
+ (void) well_known;
+}
diff --git a/js/src/gdb/tests/test-JSSymbol.py b/js/src/gdb/tests/test-JSSymbol.py
new file mode 100644
index 000000000..a9ffb5f41
--- /dev/null
+++ b/js/src/gdb/tests/test-JSSymbol.py
@@ -0,0 +1,10 @@
+# Printing JS::Symbols.
+
+assert_subprinter_registered('SpiderMonkey', 'ptr-to-JS::Symbol')
+
+run_fragment('JSSymbol.simple')
+
+assert_pretty('unique', 'Symbol()')
+assert_pretty('unique_with_desc', 'Symbol("Hello!")')
+assert_pretty('registry', 'Symbol.for("Hello!")')
+assert_pretty('well_known', 'Symbol.iterator')
diff --git a/js/src/gdb/tests/test-Root-null.py b/js/src/gdb/tests/test-Root-null.py
new file mode 100644
index 000000000..9abf2e801
--- /dev/null
+++ b/js/src/gdb/tests/test-Root-null.py
@@ -0,0 +1,20 @@
+# Test printing roots that refer to NULL pointers.
+
+# Since mozilla.prettyprinters.Pointer declines to create pretty-printers
+# for null pointers, GDB built-in printing code ends up handling them. But
+# as of 2012-11, GDB suppresses printing pointers in replacement values:
+# see: http://sourceware.org/ml/gdb/2012-11/msg00055.html
+#
+# Thus, if the pretty-printer for JS::Rooted simply returns the referent as
+# a replacement value (which seems reasonable enough, if you want the
+# pretty-printer to be completely transparent), and the referent is a null
+# pointer, it prints as nothing at all.
+#
+# This test ensures that the JS::Rooted pretty-printer doesn't make that
+# mistake.
+
+gdb.execute('set print address on')
+
+run_fragment('Root.null')
+
+assert_pretty('null', '0x0')
diff --git a/js/src/gdb/tests/test-Root.cpp b/js/src/gdb/tests/test-Root.cpp
new file mode 100644
index 000000000..6a8d8928b
--- /dev/null
+++ b/js/src/gdb/tests/test-Root.cpp
@@ -0,0 +1,61 @@
+#include "gdb-tests.h"
+
+#include "jsapi.h"
+#include "jsfun.h"
+
+#include "gc/Barrier.h"
+
+FRAGMENT(Root, null) {
+ JS::Rooted<JSObject*> null(cx, nullptr);
+
+ breakpoint();
+
+ (void) null;
+}
+
+void callee(JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> mutableObj)
+{
+ // Prevent the linker from unifying this function with others that are
+ // equivalent in machine code but not type.
+ fprintf(stderr, "Called " __FILE__ ":callee\n");
+ breakpoint();
+}
+
+FRAGMENT(Root, handle) {
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ callee(global, &global);
+ (void) global;
+}
+
+FRAGMENT(Root, HeapSlot) {
+ JS::Rooted<JS::Value> plinth(cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth")));
+ JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, JS::HandleValueArray(plinth)));
+
+ breakpoint();
+
+ (void) plinth;
+ (void) array;
+}
+
+FRAGMENT(Root, barriers) {
+ JSObject* obj = JS_NewPlainObject(cx);
+ js::PreBarriered<JSObject*> prebarriered(obj);
+ js::GCPtrObject heapptr(obj);
+ js::HeapPtr<JSObject*> relocatable(obj);
+
+ JS::Value val = JS::ObjectValue(*obj);
+ js::PreBarrieredValue prebarrieredValue(JS::ObjectValue(*obj));
+ js::GCPtrValue heapValue(JS::ObjectValue(*obj));
+ js::HeapPtr<JS::Value> relocatableValue(JS::ObjectValue(*obj));
+
+ breakpoint();
+
+ (void) prebarriered;
+ (void) heapptr;
+ (void) relocatable;
+ (void) val;
+ (void) prebarrieredValue;
+ (void) heapValue;
+ (void) relocatableValue;
+}
+
diff --git a/js/src/gdb/tests/test-Root.py b/js/src/gdb/tests/test-Root.py
new file mode 100644
index 000000000..e2b215082
--- /dev/null
+++ b/js/src/gdb/tests/test-Root.py
@@ -0,0 +1,27 @@
+# Test printing Handles.
+
+assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::Rooted')
+assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::Handle')
+assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::MutableHandle')
+assert_subprinter_registered('SpiderMonkey', 'instantiations-of-js::BarrieredBase')
+
+run_fragment('Root.handle')
+
+assert_pretty('obj', '(JSObject * const) [object global] delegate')
+assert_pretty('mutableObj', '(JSObject *) [object global] delegate')
+
+run_fragment('Root.HeapSlot')
+
+# This depends on implementation details of arrays, but since HeapSlot is
+# not a public type, I'm not sure how to avoid doing *something* ugly.
+assert_pretty('((js::NativeObject *) array.get())->elements_[0]', '$jsval("plinth")')
+
+run_fragment('Root.barriers');
+
+assert_pretty('prebarriered', '(JSObject *) [object Object]');
+assert_pretty('heapptr', '(JSObject *) [object Object]');
+assert_pretty('relocatable', '(JSObject *) [object Object]');
+assert_pretty('val', '$jsval((JSObject *) [object Object])');
+assert_pretty('heapValue', '$jsval((JSObject *) [object Object])');
+assert_pretty('prebarrieredValue', '$jsval((JSObject *) [object Object])');
+assert_pretty('relocatableValue', '$jsval((JSObject *) [object Object])');
diff --git a/js/src/gdb/tests/test-asmjs.cpp b/js/src/gdb/tests/test-asmjs.cpp
new file mode 100644
index 000000000..a9f16f1a1
--- /dev/null
+++ b/js/src/gdb/tests/test-asmjs.cpp
@@ -0,0 +1,38 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include <string.h>
+
+FRAGMENT(asmjs, segfault) {
+ using namespace JS;
+
+ int line0 = __LINE__;
+ const char* bytes = "\n"
+ "function f(glob, ffi, heap) {\n"
+ " \"use asm\";\n"
+ " var f32 = new glob.Float32Array(heap);\n"
+ " function g(n) {\n"
+ " n = n | 0;\n"
+ " return +f32[n>>2];\n"
+ " }\n"
+ " return g;\n"
+ "}\n"
+ "\n"
+ "var func = f(this, null, new ArrayBuffer(0x10000));\n"
+ "func(0x10000 << 2);\n"
+ "'ok'\n";
+
+ CompileOptions opts(cx);
+ opts.setFileAndLine(__FILE__, line0 + 1);
+ opts.asmJSOption = JS::AsmJSOption::Enabled;
+ RootedValue rval(cx);
+ bool ok;
+ ok = false;
+
+ ok = Evaluate(cx, opts, bytes, strlen(bytes), &rval);
+
+ breakpoint();
+
+ (void) ok;
+ (void) rval;
+}
diff --git a/js/src/gdb/tests/test-asmjs.py b/js/src/gdb/tests/test-asmjs.py
new file mode 100644
index 000000000..219cb1a4d
--- /dev/null
+++ b/js/src/gdb/tests/test-asmjs.py
@@ -0,0 +1,15 @@
+# Test for special asmjs SIGSEGV-handling.
+#
+# Expected behavior is for the asm.js code in the following fragment to trigger
+# SIGSEGV. The code in js/src/gdb/mozilla/asmjs.py should prevent GDB from
+# handling that signal.
+
+run_fragment('asmjs.segfault')
+
+# If SIGSEGV handling is broken, GDB would have stopped at the SIGSEGV signal.
+# The breakpoint would not have hit, and run_fragment would have thrown.
+#
+# So if we get here, and the asm.js code actually ran, we win.
+
+assert_pretty('ok', 'true')
+assert_pretty('rval', '$jsval("ok")')
diff --git a/js/src/gdb/tests/test-jsid.cpp b/js/src/gdb/tests/test-jsid.cpp
new file mode 100644
index 000000000..9dd1ff90a
--- /dev/null
+++ b/js/src/gdb/tests/test-jsid.cpp
@@ -0,0 +1,44 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+FRAGMENT(jsid, simple) {
+ JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "moon"));
+ JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
+ JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
+ jsid int_id = INT_TO_JSID(1729);
+ JS::Rooted<jsid> unique_symbol_id(
+ cx, SYMBOL_TO_JSID(JS::NewSymbol(cx, interned)));
+ JS::Rooted<jsid> registry_symbol_id(
+ cx, SYMBOL_TO_JSID(JS::GetSymbolFor(cx, interned)));
+ JS::Rooted<jsid> well_known_symbol_id(
+ cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator)));
+ jsid void_id = JSID_VOID;
+ jsid empty_id = JSID_EMPTY;
+
+ breakpoint();
+
+ (void) string_id;
+ (void) int_id;
+ (void) unique_symbol_id;
+ (void) registry_symbol_id;
+ (void) well_known_symbol_id;
+ (void) void_id;
+ (void) empty_id;
+}
+
+void
+jsid_handles(JS::Handle<jsid> jsid_handle,
+ JS::MutableHandle<jsid> mutable_jsid_handle)
+{
+ // Prevent the linker from unifying this function with others that are
+ // equivalent in machine code but not type.
+ fprintf(stderr, "Called " __FILE__ ":jsid_handles\n");
+ breakpoint();
+}
+
+FRAGMENT(jsid, handles) {
+ JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "shovel"));
+ JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
+ JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
+ jsid_handles(string_id, &string_id);
+}
diff --git a/js/src/gdb/tests/test-jsid.py b/js/src/gdb/tests/test-jsid.py
new file mode 100644
index 000000000..265e8ac6e
--- /dev/null
+++ b/js/src/gdb/tests/test-jsid.py
@@ -0,0 +1,19 @@
+# Tests for jsid pretty-printing
+
+assert_subprinter_registered('SpiderMonkey', 'jsid')
+
+run_fragment('jsid.simple')
+
+assert_pretty('string_id', '$jsid("moon")')
+assert_pretty('int_id', '$jsid(1729)')
+unique_symbol_pretty = str(gdb.parse_and_eval('unique_symbol_id')).split('@')[0]
+assert_eq(unique_symbol_pretty, '$jsid(Symbol("moon"))')
+assert_pretty('registry_symbol_id', '$jsid(Symbol.for("moon"))')
+assert_pretty('well_known_symbol_id', '$jsid(Symbol.iterator)')
+assert_pretty('void_id', 'JSID_VOID')
+assert_pretty('empty_id', 'JSID_EMPTY')
+
+run_fragment('jsid.handles')
+
+assert_pretty('jsid_handle', '$jsid("shovel")')
+assert_pretty('mutable_jsid_handle', '$jsid("shovel")')
diff --git a/js/src/gdb/tests/test-jsval.cpp b/js/src/gdb/tests/test-jsval.cpp
new file mode 100644
index 000000000..f3c3247e2
--- /dev/null
+++ b/js/src/gdb/tests/test-jsval.cpp
@@ -0,0 +1,40 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+FRAGMENT(jsval, simple) {
+ using namespace JS;
+
+ RootedValue fortytwo(cx, Int32Value(42));
+ RootedValue negone(cx, Int32Value(-1));
+ RootedValue undefined(cx, UndefinedValue());
+ RootedValue null(cx, NullValue());
+ RootedValue js_true(cx, BooleanValue(true));
+ RootedValue js_false(cx, BooleanValue(false));
+ RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE));
+
+ RootedValue empty_string(cx);
+ empty_string.setString(JS_NewStringCopyZ(cx, ""));
+ RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
+ RootedValue friendly_string(cx, StringValue(hello));
+ RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello)));
+
+ RootedValue global(cx);
+ global.setObject(*CurrentGlobalOrNull(cx));
+
+ // Some interesting value that floating-point won't munge.
+ RootedValue onehundredthirtysevenonehundredtwentyeighths(cx, DoubleValue(137.0 / 128.0));
+
+ breakpoint();
+
+ (void) fortytwo;
+ (void) negone;
+ (void) undefined;
+ (void) js_true;
+ (void) js_false;
+ (void) null;
+ (void) elements_hole;
+ (void) empty_string;
+ (void) friendly_string;
+ (void) symbol;
+ (void) global;
+}
diff --git a/js/src/gdb/tests/test-jsval.py b/js/src/gdb/tests/test-jsval.py
new file mode 100644
index 000000000..f39a6591f
--- /dev/null
+++ b/js/src/gdb/tests/test-jsval.py
@@ -0,0 +1,18 @@
+# Basic unit tests for jsval pretty-printer.
+
+assert_subprinter_registered('SpiderMonkey', 'JS::Value')
+
+run_fragment('jsval.simple')
+
+assert_pretty('fortytwo', '$jsval(42)')
+assert_pretty('negone', '$jsval(-1)')
+assert_pretty('undefined', 'JSVAL_VOID')
+assert_pretty('null', 'JSVAL_NULL')
+assert_pretty('js_true', 'JSVAL_TRUE')
+assert_pretty('js_false', 'JSVAL_FALSE')
+assert_pretty('elements_hole', '$jsmagic(JS_ELEMENTS_HOLE)')
+assert_pretty('empty_string', '$jsval("")')
+assert_pretty('friendly_string', '$jsval("Hello!")')
+assert_pretty('symbol', '$jsval(Symbol.for("Hello!"))')
+assert_pretty('global', '$jsval((JSObject *) [object global] delegate)')
+assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$jsval(1.0703125)')
diff --git a/js/src/gdb/tests/test-prettyprinters.cpp b/js/src/gdb/tests/test-prettyprinters.cpp
new file mode 100644
index 000000000..794e4fd56
--- /dev/null
+++ b/js/src/gdb/tests/test-prettyprinters.cpp
@@ -0,0 +1,38 @@
+#include "gdb-tests.h"
+
+typedef int A;
+typedef A B;
+
+class C { };
+class D { };
+typedef C C_;
+typedef D D_;
+class E: C, D { };
+typedef E E_;
+class F: C_, D_ { };
+class G { };
+class H: F, G { };
+
+FRAGMENT(prettyprinters, implemented_types) {
+ int i;
+ A a;
+ B b;
+ C c;
+ C_ c_;
+ E e;
+ E_ e_;
+ F f;
+ H h;
+
+ breakpoint();
+
+ (void) i;
+ (void) a;
+ (void) b;
+ (void) c;
+ (void) c_;
+ (void) e;
+ (void) e_;
+ (void) f;
+ (void) h;
+}
diff --git a/js/src/gdb/tests/test-prettyprinters.py b/js/src/gdb/tests/test-prettyprinters.py
new file mode 100644
index 000000000..9c380fdda
--- /dev/null
+++ b/js/src/gdb/tests/test-prettyprinters.py
@@ -0,0 +1,22 @@
+import mozilla.prettyprinters
+
+run_fragment('prettyprinters.implemented_types')
+
+def implemented_type_names(expr):
+ v = gdb.parse_and_eval(expr)
+ it = mozilla.prettyprinters.implemented_types(v.type)
+ return [str(_) for _ in it]
+
+assert_eq(implemented_type_names('i'), ['int'])
+assert_eq(implemented_type_names('a'), ['A', 'int'])
+assert_eq(implemented_type_names('b'), ['B', 'A', 'int'])
+assert_eq(implemented_type_names('c'), ['C'])
+assert_eq(implemented_type_names('c_'), ['C_', 'C'])
+assert_eq(implemented_type_names('e'), ['E', 'C', 'D'])
+assert_eq(implemented_type_names('e_'), ['E_', 'E', 'C', 'D'])
+assert_eq(implemented_type_names('f'), ['F', 'C', 'D'])
+assert_eq(implemented_type_names('h'), ['H', 'F', 'G', 'C', 'D'])
+
+# Check that our pretty-printers aren't interfering with printing other types.
+assert_pretty('10', '10')
+assert_pretty('(void*) 0', '') # Because of 'set print address off'
diff --git a/js/src/gdb/tests/test-unwind.cpp b/js/src/gdb/tests/test-unwind.cpp
new file mode 100644
index 000000000..6c8b7b86a
--- /dev/null
+++ b/js/src/gdb/tests/test-unwind.cpp
@@ -0,0 +1,52 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+#include "jit/JitOptions.h"
+
+#include <string.h>
+
+static bool
+Something(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setInt32(23);
+ breakpoint();
+ return true;
+}
+
+static const JSFunctionSpecWithHelp unwind_functions[] = {
+ JS_FN_HELP("something", Something, 0, 0,
+"something()",
+" Test function for test-unwind."),
+ JS_FS_HELP_END
+};
+
+FRAGMENT(unwind, simple) {
+ using namespace JS;
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ if (!JS_DefineFunctionsWithHelp(cx, global, unwind_functions))
+ return;
+
+ // baseline-eager.
+ uint32_t saveThreshold = js::jit::JitOptions.baselineWarmUpThreshold;
+ js::jit::JitOptions.baselineWarmUpThreshold = 0;
+
+ int line0 = __LINE__;
+ const char* bytes = "\n"
+ "function unwindFunctionInner() {\n"
+ " return something();\n"
+ "}\n"
+ "\n"
+ "function unwindFunctionOuter() {\n"
+ " return unwindFunctionInner();\n"
+ "}\n"
+ "\n"
+ "unwindFunctionOuter();\n";
+
+ CompileOptions opts(cx);
+ opts.setFileAndLine(__FILE__, line0 + 1);
+ RootedValue rval(cx);
+ Evaluate(cx, opts, bytes, strlen(bytes), &rval);
+
+ js::jit::JitOptions.baselineWarmUpThreshold = saveThreshold;
+}
diff --git a/js/src/gdb/tests/test-unwind.py b/js/src/gdb/tests/test-unwind.py
new file mode 100644
index 000000000..6ddf0cd22
--- /dev/null
+++ b/js/src/gdb/tests/test-unwind.py
@@ -0,0 +1,58 @@
+# Test the unwinder and the frame filter.
+
+import platform
+
+def do_unwinder_test():
+ # The unwinder is disabled by default for the moment. Turn it on to check
+ # that the unwinder works as expected.
+ import gdb
+ gdb.execute("enable unwinder .* SpiderMonkey")
+
+ run_fragment('unwind.simple', 'Something')
+
+ first = True
+ # The unwinder is a bit flaky still but should at least be able to
+ # recognize one set of entry and exit frames. This also tests to
+ # make sure we didn't end up solely in the interpreter.
+ found_entry = False
+ found_exit = False
+ found_main = False
+ found_inner = False
+ found_outer = False
+ frames = list(gdb.frames.execute_frame_filters(gdb.newest_frame(), 0, -1))
+ for frame in frames:
+ print("examining " + frame.function())
+ if first:
+ assert_eq(frame.function().startswith("Something"), True)
+ first = False
+ elif frame.function() == "<<JitFrame_Exit>>":
+ found_exit = True
+ elif frame.function() == "<<JitFrame_Entry>>":
+ found_entry = True
+ elif frame.function() == "main":
+ found_main = True
+ elif "unwindFunctionInner" in frame.function():
+ found_inner = True
+ elif "unwindFunctionOuter" in frame.function():
+ found_outer = True
+
+ # Had to have found a frame.
+ assert_eq(first, False)
+ # Had to have found main.
+ assert_eq(found_main, True)
+ # Had to have found the entry and exit frames.
+ assert_eq(found_exit, True)
+ assert_eq(found_entry, True)
+ # Had to have found the names of the two JS functions.
+ assert_eq(found_inner, True)
+ assert_eq(found_outer, True)
+
+# Only on the right platforms.
+if platform.machine() == 'x86_64' and platform.system() == 'Linux':
+ # Only test when gdb has the unwinder feature.
+ try:
+ import gdb.unwinder
+ import gdb.frames
+ do_unwinder_test()
+ except:
+ pass
diff --git a/js/src/gdb/tests/typedef-printers.cpp b/js/src/gdb/tests/typedef-printers.cpp
new file mode 100644
index 000000000..7573b5de5
--- /dev/null
+++ b/js/src/gdb/tests/typedef-printers.cpp
@@ -0,0 +1,11 @@
+#include "gdb-tests.h"
+
+typedef int my_typedef;
+
+FRAGMENT(typedef_printers, one) {
+ my_typedef i = 0;
+
+ breakpoint();
+
+ (void) i;
+}
diff --git a/js/src/gdb/tests/typedef-printers.py b/js/src/gdb/tests/typedef-printers.py
new file mode 100644
index 000000000..ed37e438e
--- /dev/null
+++ b/js/src/gdb/tests/typedef-printers.py
@@ -0,0 +1,14 @@
+# Test that we can find pretty-printers for typedef names, not just for
+# struct types and templates.
+
+import mozilla.prettyprinters
+
+@mozilla.prettyprinters.pretty_printer('my_typedef')
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+ def to_string(self):
+ return 'huzzah'
+
+run_fragment('typedef_printers.one')
+assert_pretty('i', 'huzzah')