This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch fix-js-sealing
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit b97b3ff056f7067e6115fb4aa67f99bd619b9077
Author: Jan Lehnardt <[email protected]>
AuthorDate: Fri Jan 24 12:29:07 2025 +0100

    fix(js): re-instate `deepFreeze()` for all object freezing duties
    
    Prior to landing SpiderMonkey 102+ we relied on a global
    `seal()` function that we defined in our custom `couchjs`
    C/C++ code.
    
    SM 102+ no longer exposes the API needed to implement
    this, but the JS standard also includes a userspace
    `Object.freeze()` method we can use.
    
    Our initial implementation of SM 102+ support introduced[1]
    a global `deepFreeze()` function that relied on `Reflect
    .ownKeys()` which is not available in SpiderMonkey 1.8.5,
    but all later ones we support.
    
    To fix the build for SM 1.8.5, we partly reverted[2] the use
    of `deepFreeze()`, but we missed a few spots in `main.js`
    where we seal our own new globals like `Couch`[3].
    
    This commit reinstates the use of `deepFreeze()` everywhere
    and adds a safe fallback inside `deepFreeze()` to do the
    right thing for SM 1.8.5 and > 1.8.5.
    
    Tested on macOS with SM 128:
    
    ```
    > ./src/couch/priv/couchjs -V
    couchjs - Apache CouchDB 3.4.2-70fc2cc-dirty (SpiderMonkey 128.6.0)
    [...]
    
    > ./src/couch/priv/couchjs share/server/main.js
    ["reset"]
    true
    ```
    
    And Linux with SpiderMonkey 1.8.5:
    
    ```
    > src/couch/priv/couchjs -V
    couchjs - Apache CouchDB 3.4.2-121ac7c-dirty (SpiderMonkey 1.8.5)
    [...]
    
    > src/couch/priv/couchjs share/server/main.js
    ["reset"]
    true
    ```
    
    [1]: 
https://github.com/apache/couchdb/pull/5321/files#diff-71d0f32fee8a14f6a8d1775801cbb1851784c7050d844d2938d8fb8d71ecafe0R151
    [2]: 
https://github.com/apache/couchdb/pull/5364/files#diff-71d0f32fee8a14f6a8d1775801cbb1851784c7050d844d2938d8fb8d71ecafe0R119
    [3]: 
https://github.com/apache/couchdb/blob/main/share/server/loop.js#L180-L188
---
 share/server/dispatch-quickjs.js |  4 ----
 share/server/util.js             | 24 +++++++++++++++---------
 2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/share/server/dispatch-quickjs.js b/share/server/dispatch-quickjs.js
index 78ece8b59..5f69f6188 100644
--- a/share/server/dispatch-quickjs.js
+++ b/share/server/dispatch-quickjs.js
@@ -44,10 +44,6 @@ function create_nouveau_sandbox() {
   return sandbox;
 };
 
-function seal(obj, flag) {
-  Object.freeze(obj);
-};
-
 // This is a copy from loop.js
 var DDoc = (function() {
   var ddoc_dispatch = {
diff --git a/share/server/util.js b/share/server/util.js
index 2de3eb096..5508cc0e0 100644
--- a/share/server/util.js
+++ b/share/server/util.js
@@ -116,14 +116,7 @@ var Couch = {
         "Expression does not eval to a function. (" + source.toString() + 
")"]);
     };
   },
-  recursivelySeal: function (obj) {
-    seal(obj);
-    for (var propname in obj) {
-      if (typeof obj[propname] == "object") {
-        arguments.callee(obj[propname]);
-      }
-    }
-  },
+  recursivelySeal: deepFreeze,
 };
 
 function errstr(e) {
@@ -155,13 +148,26 @@ function isArray(obj) {
   return toString.call(obj) === "[object Array]";
 }
 
+function getPropNames(object) {
+  if (typeof Reflect === 'undefined') {
+    return Object.getOwnPropertyNames(object);
+  } else {
+    return Reflect.ownKeys(object);
+  }
+}
+
 function deepFreeze(object) {
     if (Object.isFrozen(object)) {
         return object;
     }
     Object.freeze(object);
     // Retrieve the property names defined on object
-    const propNames = Reflect.ownKeys(object);
+    // `Reflect.ownKeys()` gives us all own property name strings as well as
+    // symbols, so it is a bit more complete, but it is a newer JS API, so we
+    // fall back on `Object.getOwnPropertyNames()` in JS engines that don’t
+    // understand symbols yet (SpiderMonkey 1.8.5). It is a safe fallback
+    // because until then object keys can only be strings.
+    const propNames = getPropNames(object);
 
     // Freeze properties before freezing self
     for (var i in propNames) {

Reply via email to