You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2020/04/17 20:41:31 UTC

[couchdb] 03/03: Changes to minimize diff from 60

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

davisp pushed a commit to branch feat/spidermonkey-68-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 0c6992865ce6054044724c7f35157ab1b82bf1ee
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Fri Apr 17 15:41:13 2020 -0500

    Changes to minimize diff from 60
---
 src/couch/priv/couch_js/68/http.cpp | 214 +++++++++----------------
 src/couch/priv/couch_js/68/main.cpp |  74 +++++++--
 src/couch/priv/couch_js/68/utf8.cpp | 309 ------------------------------------
 src/couch/priv/couch_js/68/utf8.h   |  19 ---
 src/couch/priv/couch_js/68/util.cpp | 201 +++++++++++------------
 src/couch/priv/couch_js/68/util.h   |  22 +--
 6 files changed, 245 insertions(+), 594 deletions(-)

diff --git a/src/couch/priv/couch_js/68/http.cpp b/src/couch/priv/couch_js/68/http.cpp
index a0c73bd..5153a12 100644
--- a/src/couch/priv/couch_js/68/http.cpp
+++ b/src/couch/priv/couch_js/68/http.cpp
@@ -19,7 +19,6 @@
 #include <js/Initialization.h>
 #include <js/MemoryFunctions.h>
 #include "config.h"
-#include "utf8.h"
 #include "util.h"
 
 // Soft dependency on cURL bindings because they're
@@ -101,7 +100,6 @@ http_check_enabled()
 #ifdef XP_WIN
 #define strcasecmp _strcmpi
 #define strncasecmp _strnicmp
-#define snprintf _snprintf
 #endif
 
 
@@ -110,7 +108,7 @@ typedef struct curl_slist CurlHeaders;
 
 typedef struct {
     int             method;
-    char*           url;
+    std::string     url;
     CurlHeaders*    req_headers;
     int16_t          last_status;
 } HTTPData;
@@ -128,22 +126,15 @@ const char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", "OPTION
 #define OPTIONS 6
 
 
-static bool
-go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
-
-
-/*static JSString*
-str_from_binary(JSContext* cx, char* data, size_t length);
-*/
+static bool go(JSContext* cx, JSObject* obj, HTTPData* http, std::string& body);
 
 
 bool
 http_ctor(JSContext* cx, JSObject* req)
 {
-    HTTPData* http = NULL;
+    HTTPData* http = new HTTPData();
     bool ret = false;
 
-    http = (HTTPData*) malloc(sizeof(HTTPData));
     if(!http)
     {
         JS_ReportErrorUTF8(cx, "Failed to create CouchHTTP instance.");
@@ -151,7 +142,6 @@ http_ctor(JSContext* cx, JSObject* req)
     }
 
     http->method = -1;
-    http->url = NULL;
     http->req_headers = NULL;
     http->last_status = -1;
 
@@ -161,7 +151,7 @@ http_ctor(JSContext* cx, JSObject* req)
     goto success;
 
 error:
-    if(http) free(http);
+    if(http) delete http;
 
 success:
     return ret;
@@ -173,9 +163,8 @@ http_dtor(JSFreeOp* fop, JSObject* obj)
 {
     HTTPData* http = (HTTPData*) JS_GetPrivate(obj);
     if(http) {
-        if(http->url) free(http->url);
         if(http->req_headers) curl_slist_free_all(http->req_headers);
-        free(http);
+        delete http;
     }
 }
 
@@ -184,56 +173,50 @@ bool
 http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc)
 {
     HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-    char* method = NULL;
     int methid;
-    bool ret = false;
 
     if(!http) {
         JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
-        goto done;
+        return false;
     }
 
-    if(mth.isUndefined()) {
-        JS_ReportErrorUTF8(cx, "You must specify a method.");
-        goto done;
+    if(!mth.isString()) {
+        JS_ReportErrorUTF8(cx, "Method must be a string.");
+        return false;
     }
 
-    method = enc_string(cx, mth, NULL);
-    if(!method) {
+    std::string method;
+    if(!js_to_string(cx, JS::RootedValue(cx, mth), method)) {
         JS_ReportErrorUTF8(cx, "Failed to encode method.");
-        goto done;
+        return false;
     }
 
     for(methid = 0; METHODS[methid] != NULL; methid++) {
-        if(strcasecmp(METHODS[methid], method) == 0) break;
+        if(strcasecmp(METHODS[methid], method.c_str()) == 0) break;
     }
 
     if(methid > OPTIONS) {
         JS_ReportErrorUTF8(cx, "Invalid method specified.");
-        goto done;
+        return false;
     }
 
     http->method = methid;
 
-    if(url.isUndefined()) {
-        JS_ReportErrorUTF8(cx, "You must specify a URL.");
-        goto done;
-    }
-
-    if(http->url != NULL) {
-        free(http->url);
-        http->url = NULL;
+    if(!url.isString()) {
+        JS_ReportErrorUTF8(cx, "URL must be a string");
+        return false;
     }
 
-    http->url = enc_string(cx, url, NULL);
-    if(http->url == NULL) {
+    std::string urlstr;
+    if(!js_to_string(cx, JS::RootedValue(cx, url), urlstr)) {
         JS_ReportErrorUTF8(cx, "Failed to encode URL.");
-        goto done;
+        return false;
     }
+    http->url = urlstr;
 
     if(snc.isBoolean() && snc.isTrue()) {
         JS_ReportErrorUTF8(cx, "Synchronous flag must be false.");
-        goto done;
+        return false;
     }
 
     if(http->req_headers) {
@@ -244,11 +227,7 @@ http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value
     // Disable Expect: 100-continue
     http->req_headers = curl_slist_append(http->req_headers, "Expect:");
 
-    ret = true;
-
-done:
-    if(method) free(method);
-    return ret;
+    return true;
 }
 
 
@@ -256,88 +235,60 @@ bool
 http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val)
 {
     HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-    char* keystr = NULL;
-    char* valstr = NULL;
-    char* hdrbuf = NULL;
-    size_t hdrlen = -1;
-    bool ret = false;
 
     if(!http) {
         JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
-        goto done;
+        return false;
     }
 
-    if(name.isUndefined())
+    if(!name.isString())
     {
-        JS_ReportErrorUTF8(cx, "You must speciy a header name.");
-        goto done;
+        JS_ReportErrorUTF8(cx, "Header names must be strings.");
+        return false;
     }
 
-    keystr = enc_string(cx, name, NULL);
-    if(!keystr)
+    std::string keystr;
+    if(!js_to_string(cx, JS::RootedValue(cx, name), keystr))
     {
         JS_ReportErrorUTF8(cx, "Failed to encode header name.");
-        goto done;
+        return false;
     }
 
-    if(val.isUndefined())
+    if(!val.isString())
     {
-        JS_ReportErrorUTF8(cx, "You must specify a header value.");
-        goto done;
+        JS_ReportErrorUTF8(cx, "Header values must be strings.");
+        return false;
     }
 
-    valstr = enc_string(cx, val, NULL);
-    if(!valstr)
-    {
+    std::string valstr;
+    if(!js_to_string(cx, JS::RootedValue(cx, val), valstr)) {
         JS_ReportErrorUTF8(cx, "Failed to encode header value.");
-        goto done;
-    }
-
-    hdrlen = strlen(keystr) + strlen(valstr) + 3;
-    hdrbuf = (char*) malloc(hdrlen * sizeof(char));
-    if(!hdrbuf) {
-        JS_ReportErrorUTF8(cx, "Failed to allocate header buffer.");
-        goto done;
+        return false;
     }
 
-    snprintf(hdrbuf, hdrlen, "%s: %s", keystr, valstr);
-    http->req_headers = curl_slist_append(http->req_headers, hdrbuf);
-
-    ret = true;
+    std::string header = keystr + ": " + valstr;
+    http->req_headers = curl_slist_append(http->req_headers, header.c_str());
 
-done:
-    if(keystr) free(keystr);
-    if(valstr) free(valstr);
-    if(hdrbuf) free(hdrbuf);
-    return ret;
+    return true;
 }
 
 bool
 http_send(JSContext* cx, JSObject* req, JS::Value body)
 {
     HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-    char* bodystr = NULL;
-    size_t bodylen = 0;
-    bool ret = false;
 
     if(!http) {
         JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
-        goto done;
+        return false;
     }
 
-    if(!body.isUndefined()) {
-        bodystr = enc_string(cx, body, &bodylen);
-        if(!bodystr) {
-            JS_ReportErrorUTF8(cx, "Failed to encode body.");
-            goto done;
-        }
+    std::string bodystr;
+    if(!js_to_string(cx, JS::RootedValue(cx, body), bodystr)) {
+        JS_ReportErrorUTF8(cx, "Failed to encode body.");
+        return false;
     }
 
-    ret = go(cx, req, http, bodystr, bodylen);
-
-done:
-    if(bodystr) free(bodystr);
-    return ret;
+    return go(cx, req, http, bodystr);
 }
 
 int
@@ -397,7 +348,7 @@ typedef struct {
     HTTPData*   http;
     JSContext*  cx;
     JSObject*   resp_headers;
-    char*       sendbuf;
+    const char* sendbuf;
     size_t      sendlen;
     size_t      sent;
     int         sent_once;
@@ -419,10 +370,9 @@ static size_t recv_body(void *ptr, size_t size, size_t nmem, void *data);
 static size_t recv_header(void *ptr, size_t size, size_t nmem, void *data);
 
 static bool
-go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
+go(JSContext* cx, JSObject* obj, HTTPData* http, std::string& body)
 {
     CurlState state;
-    char* referer;
     JSString* jsbody;
     bool ret = false;
     JS::Value tmp;
@@ -433,8 +383,8 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
     state.cx = cx;
     state.http = http;
 
-    state.sendbuf = body;
-    state.sendlen = bodylen;
+    state.sendbuf = body.c_str();
+    state.sendlen = body.size();
     state.sent = 0;
     state.sent_once = 0;
 
@@ -465,13 +415,13 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
 
     tmp = JS_GetReservedSlot(obj, 0);
 
-    if(!(referer = enc_string(cx, tmp, NULL))) {
+    std::string referer;
+    if(!js_to_string(cx, JS::RootedValue(cx, tmp), referer)) {
         JS_ReportErrorUTF8(cx, "Failed to encode referer.");
         if(state.recvbuf) JS_free(cx, state.recvbuf);
-          return ret;
+        return ret;
     }
-    curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer);
-    free(referer);
+    curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer.c_str());
 
     if(http->method < 0 || http->method > OPTIONS) {
         JS_ReportErrorUTF8(cx, "INTERNAL: Unknown method.");
@@ -492,15 +442,15 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
         curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
     }
 
-    if(body && bodylen) {
-        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen);
+    if(body.size() > 0) {
+        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, body.size());
     } else {
         curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0);
     }
 
     // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);
 
-    curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url);
+    curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url.c_str());
     curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
     curl_easy_setopt(HTTP_HANDLE, CURLOPT_READDATA, &state);
     curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKDATA, &state);
@@ -534,7 +484,8 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
 
     if(state.recvbuf) {
         state.recvbuf[state.read] = '\0';
-        jsbody = dec_string(cx, state.recvbuf, state.read+1);
+        std::string bodystr(state.recvbuf, state.read);
+        jsbody = string_to_js(cx, bodystr);
         if(!jsbody) {
             // If we can't decode the body as UTF-8 we forcefully
             // convert it to a string by just forcing each byte
@@ -574,7 +525,7 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
 static size_t
 send_body(void *ptr, size_t size, size_t nmem, void *data)
 {
-    CurlState* state = (CurlState*) data;
+    CurlState* state = static_cast<CurlState*>(data);
     size_t length = size * nmem;
     size_t towrite = state->sendlen - state->sent;
 
@@ -600,19 +551,19 @@ send_body(void *ptr, size_t size, size_t nmem, void *data)
 static int
 seek_body(void* ptr, curl_off_t offset, int origin)
 {
-    CurlState* state = (CurlState*) ptr;
+    CurlState* state = static_cast<CurlState*>(ptr);
     if(origin != SEEK_SET) return -1;
 
-    state->sent = (size_t) offset;
-    return (int) state->sent;
+    state->sent = static_cast<size_t>(offset);
+    return static_cast<int>(state->sent);
 }
 
 static size_t
 recv_header(void *ptr, size_t size, size_t nmem, void *data)
 {
-    CurlState* state = (CurlState*) data;
+    CurlState* state = static_cast<CurlState*>(data);
     char code[4];
-    char* header = (char*) ptr;
+    char* header = static_cast<char*>(ptr);
     size_t length = size * nmem;
     JSString* hdr = NULL;
     uint32_t hdrlen;
@@ -640,7 +591,8 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data)
     }
 
     // Append the new header to our array.
-    hdr = dec_string(state->cx, header, length);
+    std::string hdrstr(header, length);
+    hdr = string_to_js(state->cx, hdrstr);
     if(!hdr) {
         return CURLE_WRITE_ERROR;
     }
@@ -661,14 +613,17 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data)
 static size_t
 recv_body(void *ptr, size_t size, size_t nmem, void *data)
 {
-    CurlState* state = (CurlState*) data;
+    CurlState* state = static_cast<CurlState*>(data);
     size_t length = size * nmem;
     char* tmp = NULL;
 
     if(!state->recvbuf) {
         state->recvlen = 4096;
         state->read = 0;
-        state->recvbuf = static_cast<char *>(JS_malloc(state->cx, state->recvlen));
+        state->recvbuf = static_cast<char*>(JS_malloc(
+                state->cx,
+                state->recvlen
+            ));
     }
 
     if(!state->recvbuf) {
@@ -678,7 +633,12 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data)
     // +1 so we can add '\0' back up in the go function.
     size_t oldlen = state->recvlen;
     while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
-    tmp = static_cast<char *>(JS_realloc(state->cx, state->recvbuf, oldlen, state->recvlen));
+    tmp = static_cast<char*>(JS_realloc(
+            state->cx,
+            state->recvbuf,
+            oldlen,
+            state->recvlen
+        ));
     if(!tmp) return CURLE_WRITE_ERROR;
     state->recvbuf = tmp;
 
@@ -687,24 +647,4 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data)
     return length;
 }
 
-/*JSString*
-str_from_binary(JSContext* cx, char* data, size_t length)
-{
-    char16_t* conv = static_cast<char16_t *>(JS_malloc(cx, length * sizeof(char16_t)));
-    JSString* ret = NULL;
-    size_t i;
-
-    if(!conv) return NULL;
-
-    for(i = 0; i < length; i++) {
-        conv[i] = (char16_t) data[i];
-    }
-
-    ret = JS_NewUCString(cx, conv, length);
-    if(!ret) JS_free(cx, conv);
-
-    return ret;
-}
-*/
-
 #endif /* HAVE_CURL */
diff --git a/src/couch/priv/couch_js/68/main.cpp b/src/couch/priv/couch_js/68/main.cpp
index 3860a01..1f853f4 100644
--- a/src/couch/priv/couch_js/68/main.cpp
+++ b/src/couch/priv/couch_js/68/main.cpp
@@ -31,7 +31,6 @@
 
 #include "config.h"
 #include "http.h"
-#include "utf8.h"
 #include "util.h"
 
 static bool enableSharedMemory = true;
@@ -102,9 +101,14 @@ req_ctor(JSContext* cx, unsigned int argc, JS::Value* vp)
 static bool
 req_open(JSContext* cx, unsigned int argc, JS::Value* vp)
 {
-    GET_THIS(cx, argc, vp, args, obj)
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedObject obj(cx);
     bool ret = false;
 
+    if(!args.computeThis(cx, &obj)) {
+        return false;
+    }
+
     if(argc == 2) {
         ret = http_open(cx, obj, args[0], args[1], JS::BooleanValue(false));
     } else if(argc == 3) {
@@ -121,9 +125,14 @@ req_open(JSContext* cx, unsigned int argc, JS::Value* vp)
 static bool
 req_set_hdr(JSContext* cx, unsigned int argc, JS::Value* vp)
 {
-    GET_THIS(cx, argc, vp, args, obj)
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedObject obj(cx);
     bool ret = false;
 
+    if(!args.computeThis(cx, &obj)) {
+        return false;
+    }
+
     if(argc == 2) {
         ret = http_set_hdr(cx, obj, args[0], args[1]);
     } else {
@@ -138,9 +147,14 @@ req_set_hdr(JSContext* cx, unsigned int argc, JS::Value* vp)
 static bool
 req_send(JSContext* cx, unsigned int argc, JS::Value* vp)
 {
-    GET_THIS(cx, argc, vp, args, obj)
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedObject obj(cx);
     bool ret = false;
 
+    if(!args.computeThis(cx, &obj)) {
+        return false;
+    }
+
     if(argc == 1) {
         ret = http_send(cx, obj, args[0]);
     } else {
@@ -154,7 +168,13 @@ req_send(JSContext* cx, unsigned int argc, JS::Value* vp)
 static bool
 req_status(JSContext* cx, unsigned int argc, JS::Value* vp)
 {
-    GET_THIS(cx, argc, vp, args, obj)
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedObject obj(cx);
+
+    if(!args.computeThis(cx, &obj)) {
+        return false;
+    }
+
     int status = http_status(cx, obj);
 
     if(status < 0)
@@ -167,8 +187,14 @@ req_status(JSContext* cx, unsigned int argc, JS::Value* vp)
 static bool
 base_url(JSContext *cx, unsigned int argc, JS::Value* vp)
 {
-    GET_THIS(cx, argc, vp, args, obj)
-    couch_args *cargs = (couch_args*)JS_GetContextPrivate(cx);
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedObject obj(cx);
+
+    if(!args.computeThis(cx, &obj)) {
+        return false;
+    }
+
+    couch_args *cargs = static_cast<couch_args*>(JS_GetContextPrivate(cx));
     JS::Value uri_val;
     bool rc = http_uri(cx, obj, cargs, &uri_val);
     args.rval().set(uri_val);
@@ -278,7 +304,19 @@ static bool
 print(JSContext* cx, unsigned int argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    couch_print(cx, argc, args);
+
+    bool use_stderr = false;
+    if(argc > 1 && args[1].isTrue()) {
+        use_stderr = true;
+    }
+
+    if(!args[0].isString()) {
+        JS_ReportErrorUTF8(cx, "Unable to print non-string value.");
+        return false;
+    }
+
+    couch_print(cx, args[0], use_stderr);
+
     args.rval().setUndefined();
     return true;
 }
@@ -381,7 +419,7 @@ static JSFunctionSpec global_functions[] = {
 static bool
 csp_allows(JSContext* cx, JS::HandleValue code)
 {
-    couch_args *args = (couch_args*)JS_GetContextPrivate(cx);
+    couch_args* args = static_cast<couch_args*>(JS_GetContextPrivate(cx));
     if(args->eval) {
         return true;
     } else {
@@ -476,13 +514,27 @@ main(int argc, const char* argv[])
         script = JS::CompileUtf8File(cx, options, fp);
         fclose(fp);
         if (!script) {
-            fprintf(stderr, "Failed to compile file: %s\n", filename);
+            JS::RootedValue exc(cx);
+            if(!JS_GetPendingException(cx, &exc)) {
+                fprintf(stderr, "Failed to compile script.\n");
+            } else {
+                JS::RootedObject exc_obj(cx, &exc.toObject());
+                JSErrorReport* report = JS_ErrorFromException(cx, exc_obj);
+                couch_error(cx, report);
+            }
             return 1;
         }
 
         JS::RootedValue result(cx);
         if(JS_ExecuteScript(cx, script, &result) != true) {
-            fprintf(stderr, "Failed to execute script.\n");
+            JS::RootedValue exc(cx);
+            if(!JS_GetPendingException(cx, &exc)) {
+                fprintf(stderr, "Failed to execute script.\n");
+            } else {
+                JS::RootedObject exc_obj(cx, &exc.toObject());
+                JSErrorReport* report = JS_ErrorFromException(cx, exc_obj);
+                couch_error(cx, report);
+            }
             return 1;
         }
 
diff --git a/src/couch/priv/couch_js/68/utf8.cpp b/src/couch/priv/couch_js/68/utf8.cpp
deleted file mode 100644
index c28e026..0000000
--- a/src/couch/priv/couch_js/68/utf8.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#include <jsapi.h>
-#include <js/Initialization.h>
-#include <js/Conversions.h>
-#include <js/MemoryFunctions.h>
-#include <js/Wrapper.h>
-#include "config.h"
-#include "util.h"
-
-static int
-enc_char(uint8_t *utf8Buffer, uint32_t ucs4Char)
-{
-    int utf8Length = 1;
-
-    if (ucs4Char < 0x80)
-    {
-        *utf8Buffer = (uint8_t)ucs4Char;
-    }
-    else
-    {
-        int i;
-        uint32_t a = ucs4Char >> 11;
-        utf8Length = 2;
-        while(a)
-        {
-            a >>= 5;
-            utf8Length++;
-        }
-        i = utf8Length;
-        while(--i)
-        {
-            utf8Buffer[i] = (uint8_t)((ucs4Char & 0x3F) | 0x80);
-            ucs4Char >>= 6;
-        }
-        *utf8Buffer = (uint8_t)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
-    }
-
-    return utf8Length;
-}
-
-static bool
-enc_charbuf(const char16_t* src, size_t srclen, char* dst, size_t* dstlenp)
-{
-    size_t i;
-    size_t utf8Len;
-    size_t dstlen = *dstlenp;
-    size_t origDstlen = dstlen;
-    char16_t c;
-    char16_t c2;
-    uint32_t v;
-    uint8_t utf8buf[6];
-
-    if(!dst)
-    {
-        dstlen = origDstlen = (size_t) -1;
-    }
-
-    while(srclen)
-    {
-        c = *src++;
-        srclen--;
-
-        if(c <= 0xD7FF || c >= 0xE000)
-        {
-            v = (uint32_t) c;
-        }
-        else if(c >= 0xD800 && c <= 0xDBFF)
-        {
-            if(srclen < 1) goto buffer_too_small;
-            c2 = *src++;
-            srclen--;
-            if(c2 >= 0xDC00 && c2 <= 0xDFFF)
-            {
-                v = (uint32_t) (((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000);
-            }
-            else
-            {
-                // Invalid second half of surrogate pair
-                v = (uint32_t) 0xFFFD;
-                // Undo our character advancement
-                src--;
-                srclen++;
-            }
-        }
-        else
-        {
-            // Invalid first half surrogate pair
-            v = (uint32_t) 0xFFFD;
-        }
-
-        if(v < 0x0080)
-        {
-            // no encoding necessary - performance hack
-            if(!dstlen) goto buffer_too_small;
-            if(dst) *dst++ = (char) v;
-            utf8Len = 1;
-        }
-        else
-        {
-            utf8Len = enc_char(utf8buf, v);
-            if(utf8Len > dstlen) goto buffer_too_small;
-            if(dst)
-            {
-                for (i = 0; i < utf8Len; i++)
-                {
-                    *dst++ = (char) utf8buf[i];
-                }
-            }
-        }
-        dstlen -= utf8Len;
-    }
-
-    *dstlenp = (origDstlen - dstlen);
-    return true;
-
-buffer_too_small:
-    *dstlenp = (origDstlen - dstlen);
-    return false;
-}
-
-char*
-enc_string(JSContext* cx, JS::Value arg, size_t* buflen)
-{
-    JSString* str = NULL;
-    const char16_t* src = NULL;
-    char* bytes = NULL;
-    size_t srclen = 0;
-    size_t byteslen = 0;
-    JS::AutoStableStringChars rawChars(cx);
-
-    str = arg.toString();
-    if(!str) goto error;
-
-    if (!rawChars.initTwoByte(cx, str))
-        return NULL;
-
-    src = rawChars.twoByteRange().begin().get();
-    srclen = JS_GetStringLength(str);
-
-    if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error;
-
-    bytes = js_pod_malloc<char>(byteslen + 1);
-    bytes[byteslen] = 0;
-
-    if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error;
-
-    if(buflen) *buflen = byteslen;
-    goto success;
-
-error:
-    if(bytes != NULL) JS_free(cx, bytes);
-    bytes = NULL;
-
-success:
-/*
-    JS::RootedString str(cx, arg.toString());
-    JS::UniqueChars chars = JS_EncodeStringToUTF8(cx, str);
-
-    if(buflen) *buflen = strlen(chars.get());
-
-    return JS_NewUCStringCopyN(cs, chars.get(), buflen);
-*/
-    return bytes;
-}
-
-static uint32_t
-dec_char(const uint8_t *utf8Buffer, int utf8Length)
-{
-    uint32_t ucs4Char;
-    uint32_t minucs4Char;
-
-    // from Unicode 3.1, non-shortest form is illegal
-    static const uint32_t minucs4Table[] = {
-        0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
-    };
-
-    if (utf8Length == 1)
-    {
-        ucs4Char = *utf8Buffer;
-    }
-    else
-    {
-        ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
-        minucs4Char = minucs4Table[utf8Length-2];
-        while(--utf8Length)
-        {
-            ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
-        }
-        if(ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
-        {
-            ucs4Char = 0xFFFD;
-        }
-    }
-
-    return ucs4Char;
-}
-
-static bool
-dec_charbuf(const char *src, size_t srclen, char16_t *dst, size_t *dstlenp)
-{
-    uint32_t v;
-    size_t offset = 0;
-    size_t j;
-    size_t n;
-    size_t dstlen = *dstlenp;
-    size_t origDstlen = dstlen;
-
-    if(!dst) dstlen = origDstlen = (size_t) -1;
-
-    while(srclen)
-    {
-        v = (uint8_t) *src;
-        n = 1;
-
-        if(v & 0x80)
-        {
-            while(v & (0x80 >> n))
-            {
-                n++;
-            }
-
-            if(n > srclen) goto buffer_too_small;
-            if(n == 1 || n > 6) goto bad_character;
-
-            for(j = 1; j < n; j++)
-            {
-                if((src[j] & 0xC0) != 0x80) goto bad_character;
-            }
-
-            v = dec_char((const uint8_t *) src, n);
-            if(v >= 0x10000)
-            {
-                v -= 0x10000;
-
-                if(v > 0xFFFFF || dstlen < 2)
-                {
-                    *dstlenp = (origDstlen - dstlen);
-                    return false;
-                }
-
-                if(dstlen < 2) goto buffer_too_small;
-
-                if(dst)
-                {
-                    *dst++ = (char16_t)((v >> 10) + 0xD800);
-                    v = (char16_t)((v & 0x3FF) + 0xDC00);
-                }
-                dstlen--;
-            }
-        }
-
-        if(!dstlen) goto buffer_too_small;
-        if(dst) *dst++ = (char16_t) v;
-
-        dstlen--;
-        offset += n;
-        src += n;
-        srclen -= n;
-    }
-
-    *dstlenp = (origDstlen - dstlen);
-    return true;
-
-bad_character:
-    *dstlenp = (origDstlen - dstlen);
-    return false;
-
-buffer_too_small:
-    *dstlenp = (origDstlen - dstlen);
-    return false;
-}
-
-JSString*
-dec_string(JSContext* cx, const char* bytes, size_t byteslen)
-{
-    JSString* str = NULL;
-    size_t charslen;
-
-    if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) return NULL;
-
-    JS::UniqueTwoByteChars chars(js_pod_malloc<char16_t>(charslen + 1));
-    if(!chars) return NULL;
-    chars.get()[charslen] = 0;
-
-    if(!dec_charbuf(bytes, byteslen, chars.get(), &charslen)) goto error;
-
-    str = JS_NewUCString(cx, std::move(chars), charslen - 1);
-    if(!str) goto error;
-
-    goto success;
-
-error:
-    if(chars != NULL) JS_free(cx, chars.get());
-    str = NULL;
-
-success:
-    return str;
-}
diff --git a/src/couch/priv/couch_js/68/utf8.h b/src/couch/priv/couch_js/68/utf8.h
deleted file mode 100644
index c8b1f4d..0000000
--- a/src/couch/priv/couch_js/68/utf8.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#ifndef COUCH_JS_UTF_8_H
-#define COUCH_JS_UTF_8_H
-
-char* enc_string(JSContext* cx, JS::Value arg, size_t* buflen);
-JSString* dec_string(JSContext* cx, const char* buf, size_t buflen);
-
-#endif
diff --git a/src/couch/priv/couch_js/68/util.cpp b/src/couch/priv/couch_js/68/util.cpp
index de126ab..782d98a 100644
--- a/src/couch/priv/couch_js/68/util.cpp
+++ b/src/couch/priv/couch_js/68/util.cpp
@@ -13,61 +13,67 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <sstream>
+
 #include <jsapi.h>
 #include <js/Conversions.h>
 #include <js/Initialization.h>
 #include <js/MemoryFunctions.h>
-#include <js/RegExp.h>
 
 #include "help.h"
 #include "util.h"
-#include "utf8.h"
 
-/*
 std::string
 js_to_string(JSContext* cx, JS::HandleValue val)
 {
+    JS::AutoSaveExceptionState exc_state(cx);
     JS::RootedString sval(cx);
     sval = val.toString();
 
     JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, sval));
     if(!chars) {
         JS_ClearPendingException(cx);
-        fprintf(stderr, "Error converting value to string.\n");
-        exit(3);
+        return std::string();
     }
 
     return chars.get();
 }
 
-std::string
-js_to_string(JSContext* cx, JSString *str)
+bool
+js_to_string(JSContext* cx, JS::HandleValue val, std::string& str)
 {
-    JS::UniqueChars chars(JS_EncodeString(cx, str));
-    if(!chars) {
-        JS_ClearPendingException(cx);
-        fprintf(stderr, "Error converting  to string.\n");
-        exit(3);
+    if(!val.isString()) {
+        return false;
     }
 
-    return chars.get();
+    if(JS_GetStringLength(val.toString()) == 0) {
+        str = "";
+        return true;
+    }
+
+    std::string conv = js_to_string(cx, val);
+    if(!conv.size()) {
+        return false;
+    }
+
+    str = conv;
+    return true;
 }
-*/
 
 JSString*
-string_to_js(JSContext* cx, const std::string& s)
+string_to_js(JSContext* cx, const std::string& raw)
 {
-/*
-
-    JSString* ret = JS_NewStringCopyN(cx, s.c_str(), s.size());
-    if(ret != nullptr) {
-        return ret;
+    JS::UTF8Chars utf8(raw.c_str(), raw.size());
+    JS::UniqueTwoByteChars utf16;
+    size_t len;
+
+    auto tmp = JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &len, js::MallocArena);
+    utf16.reset(tmp.get());
+    if(!utf16) {
+        return nullptr;
     }
 
-    fprintf(stderr, "Unable to allocate string object.\n");
-    exit(3);
-*/
-    return dec_string(cx, s.c_str(), s.size());
+    return JS_NewUCString(cx, std::move(utf16), len);
 }
 
 size_t
@@ -92,21 +98,21 @@ couch_readfile(const char* file, char** outbuf_p)
 
     while((nread = fread(fbuf, 1, 16384, fp)) > 0) {
         if(buf == NULL) {
-            buf = (char*) malloc(nread + 1);
+            buf = new char[nread + 1];
             if(buf == NULL) {
                 fprintf(stderr, "Out of memory.\n");
                 exit(3);
             }
             memcpy(buf, fbuf, nread);
         } else {
-            tmp = (char*) malloc(buflen + nread + 1);
+            tmp = new char[buflen + nread + 1];
             if(tmp == NULL) {
                 fprintf(stderr, "Out of memory.\n");
                 exit(3);
             }
             memcpy(tmp, buf, buflen);
             memcpy(tmp+buflen, fbuf, nread);
-            free(buf);
+            delete buf;
             buf = tmp;
         }
         buflen += nread;
@@ -122,12 +128,17 @@ couch_parse_args(int argc, const char* argv[])
     couch_args* args;
     int i = 1;
 
-    args = (couch_args*) malloc(sizeof(couch_args));
+    args = new couch_args();
     if(args == NULL)
         return NULL;
 
-    memset(args, '\0', sizeof(couch_args));
+    args->eval = 0;
+    args->use_http = 0;
+    args->use_test_funs = 0;
     args->stack_size = 64L * 1024L * 1024L;
+    args->scripts = nullptr;
+    args->uri_file = nullptr;
+    args->uri = nullptr;
 
     while(i < argc) {
         if(strcmp("-h", argv[i]) == 0) {
@@ -200,7 +211,6 @@ couch_readline(JSContext* cx, FILE* fp)
     size_t byteslen = 256;
     size_t oldbyteslen = 256;
     size_t readlen = 0;
-    bool sawNewline = false;
 
     bytes = static_cast<char *>(JS_malloc(cx, byteslen));
     if(bytes == NULL) return NULL;
@@ -210,14 +220,13 @@ couch_readline(JSContext* cx, FILE* fp)
 
         if(bytes[used-1] == '\n') {
             bytes[used-1] = '\0';
-            sawNewline = true;
             break;
         }
 
         // Double our buffer and read more.
         oldbyteslen = byteslen;
         byteslen *= 2;
-        tmp = static_cast<char *>(JS_realloc(cx, bytes, oldbyteslen, byteslen));
+        tmp = static_cast<char*>(JS_realloc(cx, bytes, oldbyteslen, byteslen));
         if(!tmp) {
             JS_free(cx, bytes);
             return NULL;
@@ -241,37 +250,23 @@ couch_readline(JSContext* cx, FILE* fp)
     bytes = tmp;
     byteslen = used;
 
-    str = string_to_js(cx, std::string(tmp, byteslen));
+    str = string_to_js(cx, std::string(tmp));
     JS_free(cx, bytes);
     return str;
 }
 
 
 void
-couch_print(JSContext* cx, unsigned int argc, JS::CallArgs argv)
+couch_print(JSContext* cx, JS::HandleValue obj, bool use_stderr)
 {
-    FILE *stream = stdout;
-
-    if (argc) {
-        if (argc > 1 && argv[1].isTrue()) {
-          stream = stderr;
-        }
-        JS::AutoSaveExceptionState exc_state(cx);
-        JS::RootedString sval(cx, JS::ToString(cx, argv[0]));
-        exc_state.restore();
-        if (!sval) {
-            fprintf(stream, "couch_print: <cannot convert value to string>\n");
-            fflush(stream);
-            return;
-        }
-        JS::UniqueChars bytes(JS_EncodeStringToUTF8(cx, sval));
-        if (!bytes)
-            return;
+    FILE* stream = stdout;
 
-        fprintf(stream, "%s", bytes.get());
+    if(use_stderr) {
+        stream = stderr;
     }
 
-    fputc('\n', stream);
+    std::string val = js_to_string(cx, obj);
+    fprintf(stream, "%s\n", val.c_str());
     fflush(stream);
 }
 
@@ -279,54 +274,64 @@ couch_print(JSContext* cx, unsigned int argc, JS::CallArgs argv)
 void
 couch_error(JSContext* cx, JSErrorReport* report)
 {
-    JS::RootedValue v(cx), stack(cx), replace(cx);
-    char* bytes;
-    JSObject* regexp;
-
-    if(!report || !JSREPORT_IS_WARNING(report->flags))
-    {
-        fprintf(stderr, "%s\n", report->message().c_str());
-
-        // Print a stack trace, if available.
-        if (JSREPORT_IS_EXCEPTION(report->flags) &&
-            JS_GetPendingException(cx, &v))
-        {
-            // Clear the exception before an JS method calls or the result is
-            // infinite, recursive error report generation.
-            JS_ClearPendingException(cx);
-
-            // Use JS regexp to indent the stack trace.
-            // If the regexp can't be created, don't JS_ReportErrorUTF8 since it is
-            // probably not productive to wind up here again.
-            JS::RootedObject vobj(cx, v.toObjectOrNull());
-
-            if(JS_GetProperty(cx, vobj, "stack", &stack) &&
-               (regexp = JS::NewRegExpObject(
-                   cx, "^(?=.)", 6, JS::RegExpFlag::Global | JS::RegExpFlag::Multiline)))
-
-            {
-                // Set up the arguments to ``String.replace()``
-                JS::RootedValueVector re_args(cx);
-                JS::RootedValue arg0(cx, JS::ObjectValue(*regexp));
-                auto arg1 = JS::StringValue(string_to_js(cx, "\t"));
-
-                if (re_args.append(arg0) && re_args.append(arg1)) {
-                    // Perform the replacement
-                    JS::RootedObject sobj(cx, stack.toObjectOrNull());
-                    if(JS_GetProperty(cx, sobj, "replace", &replace) &&
-                       JS_CallFunctionValue(cx, sobj, replace, re_args, &v))
-                    {
-                        // Print the result
-                        bytes = enc_string(cx, v, NULL);
-                        fprintf(stderr, "Stacktrace:\n%s", bytes);
-                        JS_free(cx, bytes);
-                    }
-                }
-            }
+    if(!report) {
+        return;
+    }
+
+    if(JSREPORT_IS_WARNING(report->flags)) {
+        return;
+    }
+
+    std::ostringstream msg;
+    msg << "error: " << report->message().c_str();
+
+    mozilla::Maybe<JSAutoRealm> ac;
+    JS::RootedValue exc(cx);
+    JS::RootedObject exc_obj(cx);
+    JS::RootedObject stack_obj(cx);
+    JS::RootedString stack_str(cx);
+    JS::RootedValue stack_val(cx);
+
+    if(!JS_GetPendingException(cx, &exc)) {
+        goto done;
+    }
+
+    // Clear the exception before an JS method calls or the result is
+    // infinite, recursive error report generation.
+    JS_ClearPendingException(cx);
+
+    exc_obj.set(exc.toObjectOrNull());
+    stack_obj.set(JS::ExceptionStackOrNull(exc_obj));
+
+    if(!stack_obj) {
+        // Compilation errors don't have a stack
+
+        msg << " at ";
+
+        if(report->filename) {
+            msg << report->filename;
+        } else {
+            msg << "<unknown>";
+        }
+
+        if(report->lineno) {
+            msg << ':' << report->lineno << ':' << report->column;
         }
+
+        goto done;
     }
-}
 
+    if(!JS::BuildStackString(cx, nullptr, stack_obj, &stack_str, 2)) {
+        goto done;
+    }
+
+    stack_val.set(JS::StringValue(stack_str));
+    msg << std::endl << std::endl << js_to_string(cx, stack_val).c_str();
+
+done:
+    msg << std::endl;
+    fprintf(stderr, "%s", msg.str().c_str());
+}
 
 void
 couch_oom(JSContext* cx, void* data)
diff --git a/src/couch/priv/couch_js/68/util.h b/src/couch/priv/couch_js/68/util.h
index dc8a3a7..4c27f0f 100644
--- a/src/couch/priv/couch_js/68/util.h
+++ b/src/couch/priv/couch_js/68/util.h
@@ -25,36 +25,18 @@ typedef struct {
     JSString*    uri;
 } couch_args;
 
-/*
 std::string js_to_string(JSContext* cx, JS::HandleValue val);
-std::string js_to_string(JSContext* cx, JSString *str);
+bool js_to_string(JSContext* cx, JS::HandleValue val, std::string& str);
 JSString* string_to_js(JSContext* cx, const std::string& s);
-*/
 
 couch_args* couch_parse_args(int argc, const char* argv[]);
 int couch_fgets(char* buf, int size, FILE* fp);
 JSString* couch_readline(JSContext* cx, FILE* fp);
 size_t couch_readfile(const char* file, char** outbuf_p);
-void couch_print(JSContext* cx, unsigned int argc, JS::CallArgs argv);
+void couch_print(JSContext* cx, JS::HandleValue str, bool use_stderr);
 void couch_error(JSContext* cx, JSErrorReport* report);
 void couch_oom(JSContext* cx, void* data);
 bool couch_load_funcs(JSContext* cx, JS::HandleObject obj, JSFunctionSpec* funcs);
 
-/*
- * GET_THIS:
- * @cx: JSContext pointer passed into JSNative function
- * @argc: Number of arguments passed into JSNative function
- * @vp: Argument value array passed into JSNative function
- * @args: Name for JS::CallArgs variable defined by this code snippet
- * @to: Name for JS::RootedObject variable referring to function's this
- *
- * A convenience macro for getting the 'this' object a function was called with.
- * Use in any JSNative function.
- */
-#define GET_THIS(cx, argc, vp, args, to)              \
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
-    JS::RootedObject to(cx);                          \
-    if (!args.computeThis(cx, &to))                   \
-        return false;
 
 #endif // Included util.h