From 31e61752a62e8cd1ecfc518d60ed6545c109a91a Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sat, 27 Jul 2024 13:14:04 -0700 Subject: [PATCH] Migration from ESR 115 to 128 Includes updated build instructions and a migration guide. One of the examples needs to be updated, with the new column number API; and the WeakRefs example needs its job queue and realm creation options adjusted. Everything else builds as is. --- README.md | 2 +- docs/Building SpiderMonkey.md | 12 +-- docs/Migration Guide.md | 151 ++++++++++++++++++++++++++++++++++ examples/cookbook.cpp | 8 +- examples/weakref.cpp | 9 +- meson.build | 4 +- 6 files changed, 169 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f2f2e45..b236aad 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This repository contains documentation and examples for people who want to embed the SpiderMonkey JavaScript engine. -The information on this `esr102` branch applies to SpiderMonkey 102.x, an +The information on this `esr128` branch applies to SpiderMonkey 128.x, an Extended Support Release (ESR). For other versions of SpiderMonkey, check the other branches: `next` for information that will apply in the next as-yet-unreleased ESR, or diff --git a/docs/Building SpiderMonkey.md b/docs/Building SpiderMonkey.md index 0aff0b6..36adc19 100644 --- a/docs/Building SpiderMonkey.md +++ b/docs/Building SpiderMonkey.md @@ -5,7 +5,7 @@ Use these instructions to build your own copy of SpiderMonkey. ## Prerequisites ## You will need a **C++ compiler** that can handle the C++17 standard, -**Rust** version [1.66][minimum-rust-version] or later, **GNU Make**, +**Rust** version [1.76][minimum-rust-version] or later, **GNU Make**, **zlib**, and **libffi**. These can usually be installed with a package manager. @@ -16,16 +16,16 @@ These can usually be installed with a package manager. > `--with-system-icu` in the build instructions below, for a shorter > build time. -[minimum-rust-version]: https://searchfox.org/mozilla-esr115/rev/61b47de1faebf23626e519b2464b461589fbea3e/python/mozboot/mozboot/util.py#14 -[minimum-icu-version]: https://searchfox.org/mozilla-esr115/rev/61b47de1faebf23626e519b2464b461589fbea3e/js/moz.configure#1107 +[minimum-rust-version]: https://searchfox.org/mozilla-esr128/rev/2d28d1b9e757a35095de45c818a0432e031f339d/python/mozboot/mozboot/util.py#14 +[minimum-icu-version]: https://searchfox.org/mozilla-esr128/rev/2d28d1b9e757a35095de45c818a0432e031f339d/js/moz.configure#1308 ## Getting the source code ## Currently, the most reliable way to get the SpiderMonkey source code is to download the Firefox source. -At the time of writing, the latest source for Firefox ESR 115, which -contains the source for SpiderMonkey ESR 115, can be found here: -https://ftp.mozilla.org/pub/firefox/releases/115.1.0esr/source/ +At the time of writing, the latest source for Firefox ESR 128, which +contains the source for SpiderMonkey ESR 128, can be found here: +https://ftp.mozilla.org/pub/firefox/releases/128.1.0esr/source/ The ESR releases have a major release approximately once a year with security patches released throughout the year. diff --git a/docs/Migration Guide.md b/docs/Migration Guide.md index be6d710..2f26589 100644 --- a/docs/Migration Guide.md +++ b/docs/Migration Guide.md @@ -3,6 +3,157 @@ This document describes how to port your code from using one ESR version of SpiderMonkey to the next ESR version. +## ESR 115 to ESR 128 ## + +### New API for column numbers ### + +Previously, column numbers (in source locations and stack frames) were +stored as mostly as numbers starting from 0. +When column numbers are shown in a user-facing context, however, they +are usually displayed as starting at 1. +SpiderMonkey has changed the internal representation of column numbers +to start at 1 as well. +There is a new set of APIs in `` that allows +explicitly converting from 1-origin to 0-origin in order to avoid +confusion, and handles tagged column numbers from WASM frames. + +**Recommendation:** You will need to use the new column number API if +you obtain a column number from any stack frame API such as +`JS::GetSavedFrameColumn()`. +Example: + +```c++ +// old +uint32_t column; +if (JS::GetSavedFrameColumn(cx, nullptr, frame, &column) != JS::SavedFrameResult::Ok) + return false; +std::cout << "column number " << column + 1; + +// new +JS::TaggedColumnNumberOneOrigin tagged_column; +if (JS::GetSavedFrameColumn(cx, nullptr, frame, &tagged_column) != JS::SavedFrameResult::Ok) + return false; +std::cout << "column number " << tagged_column.oneOriginValue(); +``` + +The function `JS::CreateError()` takes a `JS::ColumnNumberOneOrigin` +which is different from `JS::TaggedColumnNumberOneOrigin`. +The "tagged" type exists to accommodate WASM frames, which have a +function index instead of a column number. + +If you don't have WASM frames, you can convert the tagged column to an +untagged column number using: +```c++ +JS::ColumnNumberOneOrigin{tagged_column.toLimitedColumnNumber()}; +``` +This will assert that the column number is not tagged as a WASM function +index. +If you have WASM frames, you'll need to handle those separately. + +See [bug 1847469](https://bugzilla.mozilla.org/show_bug.cgi?id=1847469). + +### New API for ArrayBuffer buffers ### + +ArrayBuffer APIs that take data buffers from the embedding, such as +`JS::NewExternalArrayBuffer()` and `JS::NewArrayBufferWithContents()`, +now take a `mozilla::UniquePtr&&` argument for the data buffer. + +**Recommendation:** This is a fairly straightforward migration, in which +you can just put your data buffer into a UniquePtr and then +`std::move()` it into the ArrayBuffer. +If you previously passed a free callback, use that callback as the +deleter argument to the UniquePtr. +Examples: + +```c++ +// old +JS::RootedObject array_buffer{cx, JS::NewExternalArrayBuffer( + cx, len, my_struct->buffer, my_buffer_free_func, my_struct)}; + +// new +mozilla::UniquePtr contents{ + my_struct->buffer, {my_buffer_free_func, my_struct}}; +JS::RootedObject array_buffer{cx, JS::NewExternalArrayBuffer( + cx, len, std::move(contents))}; +``` + +### `JS::GCPolicy::needsSweep()` ### + +`JS::GCPolicy` has a new method, `needsSweep()`. +It is like a const version of `JS::GCPolicy::traceWeak()`, but the sense +of the boolean return value is reversed. +(Returns true if the weak pointer is dead, and false if it is live.) +The argument is const, so the method will not be called if the pointer +needs to move. +Implementing the method is optional. + +**Recommendation:** If you have specialized `JS::GCPolicy` for any of +your classes, consider whether you need to implement this method. +If your class has a weak pointer, you probably do. + +Also, note that `JS::GCPolicy>` does not implement +`needsSweep()`. +If you were storing a `JS::Heap` inside `JS::WeakCache`, you will +need to use a different data structure. +An easy drop-in replacement for `JS::Heap` suitable for this purpose +would be something like this: + +```c++ +template +class WeakPtr : public JS::Heap { + public: + using JS::Heap::Heap; + using JS::Heap::operator=; +}; + +namespace JS { + +template +struct GCPolicy> { + static void trace(JSTracer* trc, WeakPtr* thingp, const char* name) { + return JS::TraceEdge(trc, thingp, name); + } + + static bool traceWeak(JSTracer* trc, WeakPtr* thingp) { + return js::gc::TraceWeakEdge(trc, thingp); + } + + static bool needsSweep(JSTracer* trc, const WeakPtr* thingp) { + WeakPtr thing{*thingp}; + return !js::gc::TraceWeakEdge(trc, &thing); + } +}; + +} // namespace JS +``` + +### Getting Function IDs ### + +`JS_GetFunctionId()` and `JS_GetFunctionDisplayId()` now require a +JSContext. +There are now alternatives that don't take a JSContext parameter, but +avoid computing the full function name with "`get `" or "`set `" prefix +for accessors: `JS_GetMaybePartialFunctionId()` and +`JS_GetMaybePartialFunctionDisplayId()`. + +**Recommendation:** The fact that these APIs didn't require a JSContext +meant they were convenient to use for log output or debugging. +If you were using them for this purpose, switch to the `MaybePartial` +alternatives. +Otherwise, just adapt to the new signature. + +### Various API changes ### + +This is a non-exhaustive list of minor API changes and renames. + +- The type of `JSErrorBase::filename()` is now `JS::ConstUTF8CharsZ` + instead of a C string, so when using `JSErrorReport` you will need to + use `.c_str()` if you still need the C string. +- Several runtime flags for new features in `JS::RealmCreationOptions` + are enabled unconditionally, so the flags are removed. +- Classes that extend `JS::JobQueue` must now implement + `JS::JobQueue::isDrainingStopped()`, which is an abstract method. + ## ESR 102 to ESR 115 ## ### More Versatile `JS_InitClass` ### diff --git a/examples/cookbook.cpp b/examples/cookbook.cpp index 652e508..b120c65 100644 --- a/examples/cookbook.cpp +++ b/examples/cookbook.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -352,9 +353,10 @@ static bool ThrowValue(JSContext* cx, JS::HandleValue exc) { * * return ThrowError(cx, global, message, __FILE__, __LINE__, column); */ -static bool ThrowError(JSContext* cx, JS::HandleObject global, - const char* message, const char* filename, - int32_t lineno, int32_t colno = 0) { +static bool ThrowError( + JSContext* cx, JS::HandleObject global, const char* message, + const char* filename, int32_t lineno, + JS::ColumnNumberOneOrigin colno = JS::ColumnNumberOneOrigin{1}) { JS::RootedString messageStr(cx, JS_NewStringCopyZ(cx, message)); if (!messageStr) return false; JS::RootedString filenameStr(cx, JS_NewStringCopyZ(cx, filename)); diff --git a/examples/weakref.cpp b/examples/weakref.cpp index d8a6ead..7cb5d20 100644 --- a/examples/weakref.cpp +++ b/examples/weakref.cpp @@ -118,6 +118,9 @@ class CustomJobQueue : public JS::JobQueue { // JS::JobQueue override bool empty() const override { return m_queue.empty(); } + // JS::JobQueue override + bool isDrainingStopped() const override { return !m_draining; } + void queueFinalizationRegistryCallback(JSFunction* callback) { mozilla::Unused << m_finalizationRegistryCallbacks.append(callback); } @@ -247,16 +250,12 @@ static bool WeakRefExample(JSContext* cx) { JS::SetHostCleanupFinalizationRegistryCallback( cx, CleanupFinalizationRegistry, &jobQueue); - JS::RealmOptions options; - options.creationOptions().setWeakRefsEnabled( - JS::WeakRefSpecifier::EnabledWithoutCleanupSome); - static JSClass GlobalClass = {"WeakRefsGlobal", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps}; JS::Rooted global{ cx, JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, - options)}; + JS::RealmOptions{})}; if (!global) return false; JSAutoRealm ar{cx, global}; diff --git a/meson.build b/meson.build index 35bc176..5938013 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('spidermonkey-embedding-examples', 'cpp', version: 'esr115', +project('spidermonkey-embedding-examples', 'cpp', version: 'esr128', meson_version: '>= 0.43.0', default_options: ['cpp_std=c++20', 'warning_level=3']) @@ -7,7 +7,7 @@ cxx = meson.get_compiler('cpp') args = [] zlib = dependency('zlib') # (is already a SpiderMonkey dependency) -spidermonkey = dependency('mozjs-127a1') +spidermonkey = dependency('mozjs-128') readline = cxx.find_library('readline') # Check if SpiderMonkey was compiled with --enable-debug. If this is the case,