From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/gdb/tests/enum-printers.cpp | 25 ++++++++ js/src/gdb/tests/enum-printers.py | 47 ++++++++++++++ js/src/gdb/tests/test-ExecutableAllocator.cpp | 49 +++++++++++++++ js/src/gdb/tests/test-ExecutableAllocator.py | 22 +++++++ js/src/gdb/tests/test-GCCellPtr.cpp | 62 +++++++++++++++++++ js/src/gdb/tests/test-GCCellPtr.py | 17 ++++++ js/src/gdb/tests/test-Interpreter.cpp | 88 +++++++++++++++++++++++++++ js/src/gdb/tests/test-Interpreter.py | 23 +++++++ js/src/gdb/tests/test-JSObject-null.py | 8 +++ js/src/gdb/tests/test-JSObject.cpp | 54 ++++++++++++++++ js/src/gdb/tests/test-JSObject.py | 24 ++++++++ js/src/gdb/tests/test-JSString-null.py | 8 +++ js/src/gdb/tests/test-JSString-subclasses.py | 7 +++ js/src/gdb/tests/test-JSString.cpp | 65 ++++++++++++++++++++ js/src/gdb/tests/test-JSString.py | 24 ++++++++ js/src/gdb/tests/test-JSSymbol.cpp | 22 +++++++ js/src/gdb/tests/test-JSSymbol.py | 11 ++++ js/src/gdb/tests/test-Root-null.py | 21 +++++++ js/src/gdb/tests/test-Root.cpp | 67 ++++++++++++++++++++ js/src/gdb/tests/test-Root.py | 30 +++++++++ js/src/gdb/tests/test-asmjs.cpp | 44 ++++++++++++++ js/src/gdb/tests/test-asmjs.py | 16 +++++ js/src/gdb/tests/test-jsbytecode.cpp | 32 ++++++++++ js/src/gdb/tests/test-jsbytecode.py | 9 +++ js/src/gdb/tests/test-jsid.cpp | 45 ++++++++++++++ js/src/gdb/tests/test-jsid.py | 19 ++++++ js/src/gdb/tests/test-jsop.cpp | 13 ++++ js/src/gdb/tests/test-jsop.py | 9 +++ js/src/gdb/tests/test-jsval.cpp | 49 +++++++++++++++ js/src/gdb/tests/test-jsval.py | 23 +++++++ js/src/gdb/tests/test-prettyprinters.cpp | 38 ++++++++++++ js/src/gdb/tests/test-prettyprinters.py | 40 ++++++++++++ js/src/gdb/tests/test-unwind.cpp | 76 +++++++++++++++++++++++ js/src/gdb/tests/test-unwind.py | 61 +++++++++++++++++++ js/src/gdb/tests/typedef-printers.cpp | 11 ++++ js/src/gdb/tests/typedef-printers.py | 18 ++++++ 36 files changed, 1177 insertions(+) create mode 100644 js/src/gdb/tests/enum-printers.cpp create mode 100644 js/src/gdb/tests/enum-printers.py create mode 100644 js/src/gdb/tests/test-ExecutableAllocator.cpp create mode 100644 js/src/gdb/tests/test-ExecutableAllocator.py create mode 100644 js/src/gdb/tests/test-GCCellPtr.cpp create mode 100644 js/src/gdb/tests/test-GCCellPtr.py create mode 100644 js/src/gdb/tests/test-Interpreter.cpp create mode 100644 js/src/gdb/tests/test-Interpreter.py create mode 100644 js/src/gdb/tests/test-JSObject-null.py create mode 100644 js/src/gdb/tests/test-JSObject.cpp create mode 100644 js/src/gdb/tests/test-JSObject.py create mode 100644 js/src/gdb/tests/test-JSString-null.py create mode 100644 js/src/gdb/tests/test-JSString-subclasses.py create mode 100644 js/src/gdb/tests/test-JSString.cpp create mode 100644 js/src/gdb/tests/test-JSString.py create mode 100644 js/src/gdb/tests/test-JSSymbol.cpp create mode 100644 js/src/gdb/tests/test-JSSymbol.py create mode 100644 js/src/gdb/tests/test-Root-null.py create mode 100644 js/src/gdb/tests/test-Root.cpp create mode 100644 js/src/gdb/tests/test-Root.py create mode 100644 js/src/gdb/tests/test-asmjs.cpp create mode 100644 js/src/gdb/tests/test-asmjs.py create mode 100644 js/src/gdb/tests/test-jsbytecode.cpp create mode 100644 js/src/gdb/tests/test-jsbytecode.py create mode 100644 js/src/gdb/tests/test-jsid.cpp create mode 100644 js/src/gdb/tests/test-jsid.py create mode 100644 js/src/gdb/tests/test-jsop.cpp create mode 100644 js/src/gdb/tests/test-jsop.py create mode 100644 js/src/gdb/tests/test-jsval.cpp create mode 100644 js/src/gdb/tests/test-jsval.py create mode 100644 js/src/gdb/tests/test-prettyprinters.cpp create mode 100644 js/src/gdb/tests/test-prettyprinters.py create mode 100644 js/src/gdb/tests/test-unwind.cpp create mode 100644 js/src/gdb/tests/test-unwind.py create mode 100644 js/src/gdb/tests/typedef-printers.cpp create mode 100644 js/src/gdb/tests/typedef-printers.py (limited to 'js/src/gdb/tests') diff --git a/js/src/gdb/tests/enum-printers.cpp b/js/src/gdb/tests/enum-printers.cpp new file mode 100644 index 0000000000..dfb1cd7b46 --- /dev/null +++ b/js/src/gdb/tests/enum-printers.cpp @@ -0,0 +1,25 @@ +#include "gdb-tests.h" + +#include + +enum unscoped_no_storage { EnumValue1 }; + +enum unscoped_with_storage : uint8_t { EnumValue2 }; + +enum class scoped_no_storage { EnumValue3 }; + +enum class scoped_with_storage : uint8_t { EnumValue4 }; + +FRAGMENT(enum_printers, one) { + unscoped_no_storage i1 = EnumValue1; + unscoped_with_storage i2 = EnumValue2; + scoped_no_storage i3 = scoped_no_storage::EnumValue3; + scoped_with_storage i4 = scoped_with_storage::EnumValue4; + + breakpoint(); + + use(i1); + use(i2); + use(i3); + use(i4); +} diff --git a/js/src/gdb/tests/enum-printers.py b/js/src/gdb/tests/enum-printers.py new file mode 100644 index 0000000000..ff2c088dc8 --- /dev/null +++ b/js/src/gdb/tests/enum-printers.py @@ -0,0 +1,47 @@ +# Test that we can find pretty-printers for enums. +# flake8: noqa: F821 + +import mozilla.prettyprinters + + +@mozilla.prettyprinters.pretty_printer("unscoped_no_storage") +class my_typedef(object): + def __init__(self, value, cache): + pass + + def to_string(self): + return "unscoped_no_storage::success" + + +@mozilla.prettyprinters.pretty_printer("unscoped_with_storage") +class my_typedef(object): + def __init__(self, value, cache): + pass + + def to_string(self): + return "unscoped_with_storage::success" + + +@mozilla.prettyprinters.pretty_printer("scoped_no_storage") +class my_typedef(object): + def __init__(self, value, cache): + pass + + def to_string(self): + return "scoped_no_storage::success" + + +@mozilla.prettyprinters.pretty_printer("scoped_with_storage") +class my_typedef(object): + def __init__(self, value, cache): + pass + + def to_string(self): + return "scoped_with_storage::success" + + +run_fragment("enum_printers.one") +assert_pretty("i1", "unscoped_no_storage::success") +assert_pretty("i2", "unscoped_with_storage::success") +assert_pretty("i3", "scoped_no_storage::success") +assert_pretty("i4", "scoped_with_storage::success") diff --git a/js/src/gdb/tests/test-ExecutableAllocator.cpp b/js/src/gdb/tests/test-ExecutableAllocator.cpp new file mode 100644 index 0000000000..2a2ae1cf63 --- /dev/null +++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp @@ -0,0 +1,49 @@ +#include "gdb-tests.h" + +#include "jit/ExecutableAllocator.h" +#include "vm/JSContext.h" + +FRAGMENT(ExecutableAllocator, empty) { + using namespace js::jit; + ExecutableAllocator execAlloc; + + breakpoint(); + + use(execAlloc); +} + +FRAGMENT(ExecutableAllocator, onepool) { + using namespace js::jit; + ExecutablePool* pool = nullptr; + ExecutableAllocator execAlloc; + execAlloc.alloc(cx, 16 * 1024, &pool, CodeKind::Baseline); + + breakpoint(); + + use(pool); + use(execAlloc); +} + +FRAGMENT(ExecutableAllocator, twopools) { + using namespace js::jit; + const size_t INIT_ALLOC_SIZE = 16 * 1024; + const size_t ALLOC_SIZE = 32 * 1024; + ExecutablePool* init = nullptr; + ExecutablePool* pool = nullptr; + ExecutableAllocator execAlloc; + size_t allocated = 0; + + execAlloc.alloc(cx, INIT_ALLOC_SIZE, &init, CodeKind::Baseline); + + do { // Keep allocating until we get a second pool. + execAlloc.alloc(cx, ALLOC_SIZE, &pool, CodeKind::Ion); + allocated += ALLOC_SIZE; + } while (pool == init); + + breakpoint(); + + use(execAlloc); + init->release(INIT_ALLOC_SIZE, CodeKind::Baseline); + init->release(allocated - ALLOC_SIZE, CodeKind::Ion); + pool->release(ALLOC_SIZE, CodeKind::Ion); +} diff --git a/js/src/gdb/tests/test-ExecutableAllocator.py b/js/src/gdb/tests/test-ExecutableAllocator.py new file mode 100644 index 0000000000..bec2dda623 --- /dev/null +++ b/js/src/gdb/tests/test-ExecutableAllocator.py @@ -0,0 +1,22 @@ +# Tests for ExecutableAllocator pretty-printing +# Ignore flake8 errors "undefined name 'assert_regexp_pretty'" +# As it caused by the way we instanciate this file +# flake8: noqa: F821 + +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 0000000000..8dcf135be8 --- /dev/null +++ b/js/src/gdb/tests/test-GCCellPtr.cpp @@ -0,0 +1,62 @@ +#include "gdb-tests.h" + +#include "js/CompileOptions.h" +#include "js/CompilationAndEvaluation.h" +#include "js/GlobalObject.h" +#include "js/HeapAPI.h" +#include "js/RegExpFlags.h" +#include "js/SourceText.h" +#include "js/Symbol.h" +#include "js/TypeDecls.h" +#include "vm/BigIntType.h" +#include "vm/JSObject.h" +#include "vm/RegExpObject.h" +#include "vm/Shape.h" + +#include "vm/JSObject-inl.h" + +FRAGMENT(GCCellPtr, simple) { + JS::Rooted glob(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted empty(cx, JS_NewStringCopyN(cx, nullptr, 0)); + JS::Rooted unique(cx, JS::NewSymbol(cx, nullptr)); + JS::Rooted zeroBigInt(cx, JS::BigInt::zero(cx)); + JS::Rooted regExp( + cx, js::RegExpObject::create(cx, u"", 0, JS::RegExpFlags{}, + js::GenericObject)); + JS::Rooted rootedRegExpShared( + cx, js::RegExpObject::getShared(cx, regExp)); + + JS::CompileOptions options(cx); + options.setFileAndLine(__FILE__, __LINE__); + JS::SourceText srcBuf; + (void)srcBuf.init(cx, nullptr, 0, JS::SourceOwnership::Borrowed); + JS::RootedScript emptyScript(cx, JS::Compile(cx, options, srcBuf)); + + // Inline TraceKinds. + JS::GCCellPtr nulll(nullptr); + JS::GCCellPtr object(glob.get()); + JS::GCCellPtr string(empty.get()); + JS::GCCellPtr symbol(unique.get()); + JS::GCCellPtr bigint(zeroBigInt.get()); + JS::GCCellPtr shape(glob->shape()); + + // Out-of-line TraceKinds. + JS::GCCellPtr baseShape(glob->shape()->base()); + // JitCode can't easily be tested here, so skip it. + JS::GCCellPtr script(emptyScript.get()); + JS::GCCellPtr scope(emptyScript->bodyScope()); + JS::GCCellPtr regExpShared(rootedRegExpShared.get()); + + breakpoint(); + + use(nulll); + use(object); + use(string); + use(symbol); + use(bigint); + use(shape); + use(baseShape); + use(script); + use(scope); + use(regExpShared); +} diff --git a/js/src/gdb/tests/test-GCCellPtr.py b/js/src/gdb/tests/test-GCCellPtr.py new file mode 100644 index 0000000000..355ca69806 --- /dev/null +++ b/js/src/gdb/tests/test-GCCellPtr.py @@ -0,0 +1,17 @@ +# Tests for GCCellPtr pretty-printing +# flake8: noqa: F821 + +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*) )") +assert_pretty("bigint", "JS::GCCellPtr((JS::BigInt*) )") +assert_pretty("shape", "JS::GCCellPtr((js::Shape*) )") +assert_pretty("baseShape", "JS::GCCellPtr((js::BaseShape*) )") +assert_pretty("script", "JS::GCCellPtr((js::BaseScript*) )") +assert_pretty("scope", "JS::GCCellPtr((js::Scope*) )") +assert_pretty("regExpShared", "JS::GCCellPtr((js::RegExpShared*) )") diff --git a/js/src/gdb/tests/test-Interpreter.cpp b/js/src/gdb/tests/test-Interpreter.cpp new file mode 100644 index 0000000000..cc844d59a0 --- /dev/null +++ b/js/src/gdb/tests/test-Interpreter.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "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, + 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; +} + +void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, + wasm::DebugFrame* ptr) { + MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0); + frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_WasmDebugFrame; +} + +} // namespace js + +FRAGMENT(Interpreter, Regs) { + struct FakeFrame { + js::InterpreterFrame frame; + JS::Value slot0; + JS::Value slot1; + JS::Value slot2; + } fakeFrame; + uint8_t fakeOpcode = uint8_t(JSOp::True); + + js::InterpreterRegs regs; + js::GDBTestInitInterpreterRegs(regs, &fakeFrame.frame, &fakeFrame.slot2, + &fakeOpcode); + + breakpoint(); + + use(regs); +} + +FRAGMENT(Interpreter, AbstractFramePtr) { + 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)); + + js::AbstractFramePtr sfptr; + GDBTestInitAbstractFramePtr(sfptr, + (js::wasm::DebugFrame*)uintptr_t(0xcb98ad00)); + + breakpoint(); + + use(ifptr); + use(bfptr); + use(rfptr); + use(sfptr); +} diff --git a/js/src/gdb/tests/test-Interpreter.py b/js/src/gdb/tests/test-Interpreter.py new file mode 100644 index 0000000000..6e36e61787 --- /dev/null +++ b/js/src/gdb/tests/test-Interpreter.py @@ -0,0 +1,23 @@ +# Test printing interpreter internal data structures. +# Ignore flake8 errors "undefined name 'assert_pretty'" +# As it caused by the way we instanciate this file +# flake8: noqa: F821 + +assert_subprinter_registered("SpiderMonkey", "js::InterpreterRegs") + +run_fragment("Interpreter.Regs") + +assert_pretty("regs", "{ fp_ = , sp = fp_.slots() + 2, pc = (JSOp::True) }") + +run_fragment("Interpreter.AbstractFramePtr") + +assert_pretty( + "ifptr", "AbstractFramePtr ((js::InterpreterFrame *) ) = {ptr_ = 146464512}" +) +assert_pretty( + "bfptr", "AbstractFramePtr ((js::jit::BaselineFrame *) ) = {ptr_ = 3135025121}" +) +assert_pretty( + "rfptr", + "AbstractFramePtr ((js::jit::RematerializedFrame *) ) = {ptr_ = 3669732610}", +) 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 0000000000..fcda681aea --- /dev/null +++ b/js/src/gdb/tests/test-JSObject-null.py @@ -0,0 +1,8 @@ +# flake8: noqa: F821 + +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 0000000000..b1780a7332 --- /dev/null +++ b/js/src/gdb/tests/test-JSObject.cpp @@ -0,0 +1,54 @@ +#include "gdb-tests.h" +#include "jsapi.h" +#include "js/GlobalObject.h" +#include "js/Object.h" // JS::GetClass + +FRAGMENT(JSObject, simple) { + JS::Rooted glob(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted plain(cx, JS_NewPlainObject(cx)); + JS::Rooted objectProto(cx, JS::GetRealmObjectPrototype(cx)); + JS::Rooted global(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted func( + cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "dys")); + JS::Rooted anon( + cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, nullptr)); + JS::Rooted funcPtr( + cx, JS_NewFunction(cx, (JSNative)1, 0, 0, "formFollows")); + + // JS_NewObject will now assert if you feed it a bad class name, so mangle + // the name after construction. + char namebuf[20] = "goodname"; + static JSClass cls{namebuf}; + JS::RootedObject badClassName(cx, JS_NewObject(cx, &cls)); + MOZ_RELEASE_ASSERT(badClassName); + strcpy(namebuf, "\xc7X"); + + JSObject& plainRef = *plain; + JSFunction& funcRef = *funcPtr; + JSObject* plainRaw = plain; + JSObject* funcRaw = func; + + breakpoint(); + + use(glob); + use(plain); + use(objectProto); + use(func); + use(anon); + use(funcPtr); + use(&plainRef); + use(&funcRef); + use(JS::GetClass((JSObject*)&funcRef)); + use(plainRaw); + use(funcRaw); +} + +FRAGMENT(JSObject, null) { + JS::Rooted null(cx, nullptr); + JSObject* nullRaw = null; + + breakpoint(); + + use(null); + use(nullRaw); +} diff --git a/js/src/gdb/tests/test-JSObject.py b/js/src/gdb/tests/test-JSObject.py new file mode 100644 index 0000000000..6f52317d28 --- /dev/null +++ b/js/src/gdb/tests/test-JSObject.py @@ -0,0 +1,24 @@ +# Printing JSObjects. +# flake8: noqa: F821 + +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]") +assert_pretty("plain", "(JSObject *) [object Object]") +assert_pretty("objectProto", "(JSObject *) [object Object] used_as_prototype") +assert_pretty("func", '(JSObject *) [object Function "dys"]') +assert_pretty("anon", "(JSObject *) [object Function ]") +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 0000000000..26bbfcf428 --- /dev/null +++ b/js/src/gdb/tests/test-JSString-null.py @@ -0,0 +1,8 @@ +# flake8: noqa: F821 + +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 0000000000..b685f46aff --- /dev/null +++ b/js/src/gdb/tests/test-JSString-subclasses.py @@ -0,0 +1,7 @@ +# flake8: noqa: F821 + +# We can print pointers to subclasses of JSString. + +run_fragment("JSString.subclasses") + +assert_pretty("linear", '"Hi!"') diff --git a/js/src/gdb/tests/test-JSString.cpp b/js/src/gdb/tests/test-JSString.cpp new file mode 100644 index 0000000000..9e930814da --- /dev/null +++ b/js/src/gdb/tests/test-JSString.cpp @@ -0,0 +1,65 @@ +#include "gdb-tests.h" + +#include "vm/JSContext.h" +// When JSGC_ANALYSIS is #defined, Rooted needs the definition +// of JSLinearString in order to figure out its ThingRootKind +#include "vm/StringType.h" + +FRAGMENT(JSString, simple) { + JS::Rooted empty(cx, JS_NewStringCopyN(cx, nullptr, 0)); + JS::Rooted x(cx, JS_NewStringCopyN(cx, "x", 1)); + JS::Rooted z(cx, JS_NewStringCopyZ(cx, "z")); + + // I expect this will be a non-inlined string. + JS::Rooted stars(cx, + JS_NewStringCopyZ(cx, + "*************************" + "*************************" + "*************************" + "*************************")); + + // This may well be an inlined string. + JS::Rooted xz(cx, JS_ConcatStrings(cx, x, z)); + + // This will probably be a rope. + JS::Rooted doubleStars(cx, JS_ConcatStrings(cx, stars, stars)); + + // Ensure we're not confused by typedefs for pointer types. + JSString* xRaw = x; + + breakpoint(); + + use(empty); + use(x); + use(z); + use(stars); + use(xz); + use(doubleStars); + use(xRaw); +} + +FRAGMENT(JSString, null) { + JS::Rooted null(cx, nullptr); + JSString* nullRaw = null; + + breakpoint(); + + use(null); + use(nullRaw); +} + +FRAGMENT(JSString, subclasses) { + JS::Rooted linear( + cx, JS_EnsureLinearString(cx, JS_NewStringCopyZ(cx, "Hi!"))); + + breakpoint(); + + use(linear); +} + +FRAGMENT(JSString, atom) { + JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10); + breakpoint(); + + use(molybdenum); +} diff --git a/js/src/gdb/tests/test-JSString.py b/js/src/gdb/tests/test-JSString.py new file mode 100644 index 0000000000..99cebc4ab5 --- /dev/null +++ b/js/src/gdb/tests/test-JSString.py @@ -0,0 +1,24 @@ +# Printing JSStrings. +# flake8: noqa: F821 + +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), "'*' ") + +doubleStars = gdb.parse_and_eval("doubleStars") +assert_eq(str(doubleStars), "'*' ") + +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 0000000000..815f2d9e29 --- /dev/null +++ b/js/src/gdb/tests/test-JSSymbol.cpp @@ -0,0 +1,22 @@ +#include "gdb-tests.h" + +#include "js/String.h" +#include "js/Symbol.h" + +FRAGMENT(JSSymbol, simple) { + using namespace JS; + + RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!")); + + Rooted unique(cx, NewSymbol(cx, nullptr)); + Rooted unique_with_desc(cx, NewSymbol(cx, hello)); + Rooted registry(cx, GetSymbolFor(cx, hello)); + Rooted well_known(cx, GetWellKnownSymbol(cx, SymbolCode::iterator)); + + breakpoint(); + + use(unique); + use(unique_with_desc); + use(registry); + use(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 0000000000..a0dba5c036 --- /dev/null +++ b/js/src/gdb/tests/test-JSSymbol.py @@ -0,0 +1,11 @@ +# Printing JS::Symbols. +# flake8: noqa: F821 + +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 0000000000..839fa069c5 --- /dev/null +++ b/js/src/gdb/tests/test-Root-null.py @@ -0,0 +1,21 @@ +# 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. +# flake8: noqa: F821 + +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 0000000000..f93beba88a --- /dev/null +++ b/js/src/gdb/tests/test-Root.cpp @@ -0,0 +1,67 @@ +#include "gdb-tests.h" + +#include "jsapi.h" + +#include "gc/Barrier.h" +#include "js/Array.h" // JS::NewArrayObject +#include "js/GlobalObject.h" +#include "js/ValueArray.h" +#include "vm/JSFunction.h" + +using namespace js; + +FRAGMENT(Root, null) { + JS::Rooted null(cx, nullptr); + + breakpoint(); + + use(null); +} + +void callee(JS::Handle obj, + JS::MutableHandle 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 global(cx, JS::CurrentGlobalOrNull(cx)); + callee(global, &global); + use(global); +} + +FRAGMENT(Root, HeapSlot) { + JS::Rooted plinth( + cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth"))); + JS::Rooted array( + cx, JS::NewArrayObject(cx, JS::HandleValueArray(plinth))); + + breakpoint(); + + use(plinth); + use(array); +} + +FRAGMENT(Root, barriers) { + JSObject* obj = JS_NewPlainObject(cx); + PreBarriered prebarriered(obj); + GCPtr heapptr(obj); + HeapPtr relocatable(obj); + + JS::Value val = JS::ObjectValue(*obj); + PreBarriered prebarrieredValue(JS::ObjectValue(*obj)); + GCPtr heapValue(JS::ObjectValue(*obj)); + HeapPtr relocatableValue(JS::ObjectValue(*obj)); + + breakpoint(); + + use(prebarriered); + use(heapptr); + use(relocatable); + use(val); + use(prebarrieredValue); + use(heapValue); + use(relocatableValue); +} diff --git a/js/src/gdb/tests/test-Root.py b/js/src/gdb/tests/test-Root.py new file mode 100644 index 0000000000..e593a972ab --- /dev/null +++ b/js/src/gdb/tests/test-Root.py @@ -0,0 +1,30 @@ +# Test printing Handles. +# Ignore flake8 errors "undefined name 'assert_pretty'" +# As it caused by the way we instanciate this file +# flake8: noqa: F821 + +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]") +assert_pretty("mutableObj", "(JSObject *) [object global]") + +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.ptr)->elements_[0]", '$JS::Value("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", "$JS::Value((JSObject *) [object Object])") +assert_pretty("heapValue", "$JS::Value((JSObject *) [object Object])") +assert_pretty("prebarrieredValue", "$JS::Value((JSObject *) [object Object])") +assert_pretty("relocatableValue", "$JS::Value((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 0000000000..2f7927bfa4 --- /dev/null +++ b/js/src/gdb/tests/test-asmjs.cpp @@ -0,0 +1,44 @@ +#include "gdb-tests.h" +#include "js/CompilationAndEvaluation.h" +#include "js/CompileOptions.h" +#include "js/RootingAPI.h" +#include "js/SourceText.h" +#include "js/Value.h" +#include "mozilla/Utf8.h" +#include "util/Text.h" + +#include + +FRAGMENT(asmjs, segfault) { + constexpr unsigned line0 = __LINE__; + static const char chars[] = + "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"; + + JS::CompileOptions opts(cx); + opts.setFileAndLine(__FILE__, line0 + 1); + opts.setAsmJSOption(JS::AsmJSOption::Enabled); + + JS::SourceText srcBuf; + JS::Rooted rval(cx); + + bool ok = + srcBuf.init(cx, chars, js_strlen(chars), JS::SourceOwnership::Borrowed) && + JS::Evaluate(cx, opts, srcBuf, &rval); + + breakpoint(); + + use(ok); + use(rval); +} diff --git a/js/src/gdb/tests/test-asmjs.py b/js/src/gdb/tests/test-asmjs.py new file mode 100644 index 0000000000..9e3ed7fc89 --- /dev/null +++ b/js/src/gdb/tests/test-asmjs.py @@ -0,0 +1,16 @@ +# 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. +# flake8: noqa: F821 + +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", '$JS::Value("ok")') diff --git a/js/src/gdb/tests/test-jsbytecode.cpp b/js/src/gdb/tests/test-jsbytecode.cpp new file mode 100644 index 0000000000..c28c6da28a --- /dev/null +++ b/js/src/gdb/tests/test-jsbytecode.cpp @@ -0,0 +1,32 @@ +#include "gdb-tests.h" +#include "js/CompilationAndEvaluation.h" +#include "js/CompileOptions.h" +#include "js/SourceText.h" +#include "util/Text.h" +#include "vm/JSFunction.h" +#include "vm/JSScript.h" +#include "mozilla/Utf8.h" + +FRAGMENT(jsbytecode, simple) { + constexpr unsigned line0 = __LINE__; + static const char chars[] = R"( + debugger; + )"; + + JS::CompileOptions opts(cx); + opts.setFileAndLine(__FILE__, line0 + 1); + + JS::SourceText srcBuf; + JS::Rooted rval(cx); + + bool ok = + srcBuf.init(cx, chars, js_strlen(chars), JS::SourceOwnership::Borrowed); + + JSScript* script = JS::Compile(cx, opts, srcBuf); + jsbytecode* code = script->code(); + + breakpoint(); + + use(ok); + use(code); +} diff --git a/js/src/gdb/tests/test-jsbytecode.py b/js/src/gdb/tests/test-jsbytecode.py new file mode 100644 index 0000000000..e69732d4c7 --- /dev/null +++ b/js/src/gdb/tests/test-jsbytecode.py @@ -0,0 +1,9 @@ +# Basic unit tests for jsbytecode* pretty-printer. +# flake8: noqa: F821 + +assert_subprinter_registered("SpiderMonkey", "ptr-to-jsbytecode") + +run_fragment("jsbytecode.simple") + +assert_pretty("ok", "true") +assert_pretty("code", " (JSOp::Debugger)") diff --git a/js/src/gdb/tests/test-jsid.cpp b/js/src/gdb/tests/test-jsid.cpp new file mode 100644 index 0000000000..e679b956a3 --- /dev/null +++ b/js/src/gdb/tests/test-jsid.cpp @@ -0,0 +1,45 @@ +#include "gdb-tests.h" + +#include "js/Id.h" // jsid, JS::PropertyKey +#include "js/String.h" +#include "js/Symbol.h" + +FRAGMENT(jsid, simple) { + const char* chars = "moon"; + JS::Rooted string(cx, JS_NewStringCopyZ(cx, chars)); + JS::Rooted interned(cx, JS_AtomizeAndPinString(cx, chars)); + JS::Rooted string_id(cx, JS::PropertyKey::fromPinnedString(interned)); + JS::Rooted int_id(cx, JS::PropertyKey::Int(1729)); + JS::Rooted unique_symbol_id( + cx, JS::PropertyKey::Symbol(JS::NewSymbol(cx, interned))); + JS::Rooted registry_symbol_id( + cx, JS::PropertyKey::Symbol(JS::GetSymbolFor(cx, interned))); + JS::Rooted well_known_symbol_id( + cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::iterator)); + jsid void_id = JS::PropertyKey::Void(); + + breakpoint(); + + use(string_id); + use(int_id); + use(unique_symbol_id); + use(registry_symbol_id); + use(well_known_symbol_id); + use(void_id); +} + +void jsid_handles(JS::Handle jsid_handle, + JS::MutableHandle 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) { + const char* chars = "shovel"; + JS::Rooted string(cx, JS_NewStringCopyZ(cx, chars)); + JS::Rooted interned(cx, JS_AtomizeAndPinString(cx, chars)); + JS::Rooted string_id(cx, JS::PropertyKey::fromPinnedString(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 0000000000..ea43439f2f --- /dev/null +++ b/js/src/gdb/tests/test-jsid.py @@ -0,0 +1,19 @@ +# Tests for jsid pretty-printing +# flake8: noqa: F821 + +assert_subprinter_registered("SpiderMonkey", "JS::PropertyKey") + +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", "JS::VoidPropertyKey") + +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-jsop.cpp b/js/src/gdb/tests/test-jsop.cpp new file mode 100644 index 0000000000..d92c8f0d2e --- /dev/null +++ b/js/src/gdb/tests/test-jsop.cpp @@ -0,0 +1,13 @@ +#include "gdb-tests.h" + +#include "vm/BytecodeUtil.h" + +FRAGMENT(jsop, simple) { + JSOp undefined = JSOp::Undefined; + JSOp debugger = JSOp::Debugger; + + breakpoint(); + + use(undefined); + use(debugger); +} diff --git a/js/src/gdb/tests/test-jsop.py b/js/src/gdb/tests/test-jsop.py new file mode 100644 index 0000000000..fea0da6059 --- /dev/null +++ b/js/src/gdb/tests/test-jsop.py @@ -0,0 +1,9 @@ +# Basic unit tests for JSOp pretty-printer. +# flake8: noqa: F821 + +assert_subprinter_registered("SpiderMonkey", "JSOp") + +run_fragment("jsop.simple") + +assert_pretty("undefined", "JSOp::Undefined") +assert_pretty("debugger", "JSOp::Debugger") diff --git a/js/src/gdb/tests/test-jsval.cpp b/js/src/gdb/tests/test-jsval.cpp new file mode 100644 index 0000000000..3a89a76a83 --- /dev/null +++ b/js/src/gdb/tests/test-jsval.cpp @@ -0,0 +1,49 @@ +#include "gdb-tests.h" + +#include "js/GlobalObject.h" +#include "js/String.h" +#include "js/Symbol.h" +#include "vm/BigIntType.h" + +FRAGMENT(jsval, simple) { + using namespace JS; + + RootedValue fortytwo(cx, Int32Value(42)); + RootedValue fortytwoD(cx, DoubleValue(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 bi(cx, BigIntValue(BigInt::zero(cx))); + + 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(); + + use(fortytwo); + use(fortytwoD); + use(negone); + use(undefined); + use(js_true); + use(js_false); + use(null); + use(elements_hole); + use(empty_string); + use(friendly_string); + use(symbol); + use(bi); + use(global); +} diff --git a/js/src/gdb/tests/test-jsval.py b/js/src/gdb/tests/test-jsval.py new file mode 100644 index 0000000000..e5a42e72f4 --- /dev/null +++ b/js/src/gdb/tests/test-jsval.py @@ -0,0 +1,23 @@ +# Basic unit tests for jsval pretty-printer. +# flake8: noqa: F821 + +assert_subprinter_registered("SpiderMonkey", "JS::Value") + +run_fragment("jsval.simple") + +assert_pretty("fortytwo", "$JS::Int32Value(42)") +assert_pretty("fortytwoD", "$JS::DoubleValue(42.0)") +assert_pretty("negone", "$JS::Int32Value(-1)") +assert_pretty("undefined", "$JS::UndefinedValue()") +assert_pretty("null", "$JS::NullValue()") +assert_pretty("js_true", "$JS::BooleanValue(true)") +assert_pretty("js_false", "$JS::BooleanValue(false)") +assert_pretty("elements_hole", "$JS::MagicValue(JS_ELEMENTS_HOLE)") +assert_pretty("empty_string", '$JS::Value("")') +assert_pretty("friendly_string", '$JS::Value("Hello!")') +assert_pretty("symbol", '$JS::Value(Symbol.for("Hello!"))') +assert_pretty("bi", "$JS::BigIntValue()") +assert_pretty("global", "$JS::Value((JSObject *) [object global])") +assert_pretty( + "onehundredthirtysevenonehundredtwentyeighths", "$JS::DoubleValue(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 0000000000..af71c52fa3 --- /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 = 0; + A a = 0; + B b = 0; + C c; + C_ c_; + E e; + E_ e_; + F f; + H h; + + breakpoint(); + + use(i); + use(a); + use(b); + use(c); + use(c_); + use(e); + use(e_); + use(f); + use(h); +} diff --git a/js/src/gdb/tests/test-prettyprinters.py b/js/src/gdb/tests/test-prettyprinters.py new file mode 100644 index 0000000000..f3329c0288 --- /dev/null +++ b/js/src/gdb/tests/test-prettyprinters.py @@ -0,0 +1,40 @@ +# Ignore flake8 errors "undefined name 'assert_pretty'" +# As it caused by the way we instanciate this file +# flake8: noqa: F821 + +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"]) + +# Some compilers strip trivial typedefs in the debuginfo from classes' base +# classes. Sometimes this can be fixed with -fno-eliminate-unused-debug-types, +# but not always. Allow this test to pass if the typedefs are stripped. +# +# It would probably be better to figure out how to make the compiler emit them, +# since I think this test is here for a reason. +if gdb.lookup_type("F").fields()[0].name == "C_": + # We have the typedef info. + assert_eq(implemented_type_names("f"), ["F", "C_", "D_", "C", "D"]) + assert_eq(implemented_type_names("h"), ["H", "F", "G", "C_", "D_", "C", "D"]) +else: + 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 0000000000..a3dc6fe89c --- /dev/null +++ b/js/src/gdb/tests/test-unwind.cpp @@ -0,0 +1,76 @@ +#include "gdb-tests.h" +#include "jsfriendapi.h" // JSFunctionSpecWithHelp + +#include "jit/JitOptions.h" // js::jit::JitOptions +#include "js/CallArgs.h" // JS::CallArgs, JS::CallArgsFromVp +#include "js/CompilationAndEvaluation.h" // JS::Evaluate +#include "js/CompileOptions.h" // JS::CompileOptions +#include "js/GlobalObject.h" // JS::CurrentGlobalOrNull +#include "js/PropertyAndElement.h" // JS_DefineProperty +#include "js/PropertyDescriptor.h" // JSPROP_ENUMERATE +#include "js/RootingAPI.h" // JS::Rooted +#include "js/SourceText.h" // JS::Source{Ownership,Text} +#include "js/Value.h" // JS::Value + +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include // uint32_t +#include // strlen + +static bool Something(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + args.rval().setInt32(23); + breakpoint(); + return true; +} + +// clang-format off +static const JSFunctionSpecWithHelp unwind_functions[] = { + JS_FN_HELP("something", Something, 0, 0, +"something()", +" Test function for test-unwind."), + JS_FS_HELP_END +}; +// clang-format on + +FRAGMENT(unwind, simple) { + using namespace JS; + + JS::Rooted global(cx, JS::CurrentGlobalOrNull(cx)); + if (!JS_DefineFunctionsWithHelp(cx, global, unwind_functions)) { + return; + } + + // Define an itercount property and use it to ensure Baseline compilation. + uint32_t threshold = js::jit::JitOptions.baselineJitWarmUpThreshold; + RootedValue val(cx, Int32Value(threshold + 10)); + if (!JS_DefineProperty(cx, global, "itercount", val, 0)) { + return; + } + + int line0 = __LINE__; + const char* bytes = + "\n" + "function unwindFunctionInner() {\n" + " for (var i = 0; i < itercount; i++) {}\n" + " return something();\n" + "}\n" + "\n" + "function unwindFunctionOuter() {\n" + " for (var i = 0; i < itercount; i++) {}\n" + " return unwindFunctionInner();\n" + "}\n" + "\n" + "unwindFunctionOuter();\n"; + + JS::CompileOptions opts(cx); + opts.setFileAndLine(__FILE__, line0 + 1); + + JS::SourceText srcBuf; + if (!srcBuf.init(cx, bytes, strlen(bytes), JS::SourceOwnership::Borrowed)) { + return; + } + + JS::Rooted rval(cx); + JS::Evaluate(cx, opts, srcBuf, &rval); +} diff --git a/js/src/gdb/tests/test-unwind.py b/js/src/gdb/tests/test-unwind.py new file mode 100644 index 0000000000..d12c4b0f6e --- /dev/null +++ b/js/src/gdb/tests/test-unwind.py @@ -0,0 +1,61 @@ +# Test the unwinder and the frame filter. +# flake8: NOQA: F821 +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() == "<>": + found_exit = True + elif frame.function() == "<>": + 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 # NOQA: F401 + + do_unwinder_test() + except Exception: + pass diff --git a/js/src/gdb/tests/typedef-printers.cpp b/js/src/gdb/tests/typedef-printers.cpp new file mode 100644 index 0000000000..efb01f82cf --- /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(); + + use(i); +} diff --git a/js/src/gdb/tests/typedef-printers.py b/js/src/gdb/tests/typedef-printers.py new file mode 100644 index 0000000000..888379ee63 --- /dev/null +++ b/js/src/gdb/tests/typedef-printers.py @@ -0,0 +1,18 @@ +# Test that we can find pretty-printers for typedef names, not just for +# struct types and templates. +# flake8: noqa: F821 + +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") -- cgit v1.2.3