[gjs: 3/14] byteArray: Replace with native Uint8Array



commit 942252dde542dcad9916a87b836a9e952c212ce1
Author: Philip Chimento <philip chimento gmail com>
Date:   Sat Oct 28 18:30:50 2017 +0700

    byteArray: Replace with native Uint8Array
    
    It's possible to replace ByteArray with Uint8Array and have almost the
    same functionality, making GJS code closer to standard Javascript, and
    removing a large amount of code.
    
    This is necessary because getProperty/setProperty hooks are removed from
    JSClass, which the old ByteArray relied on for its array access.
    
    Closes #5.

 NEWS                                    |  31 ++
 doc/ByteArray.md                        | 160 ++------
 gi/arg.cpp                              |  40 +-
 gjs/byteArray.cpp                       | 690 ++++----------------------------
 gjs/byteArray.h                         |  17 +-
 gjs/context.cpp                         |   7 +-
 gjs/importer.cpp                        |   2 +-
 installed-tests/js/testByteArray.js     |  93 +----
 installed-tests/js/testGIMarshalling.js |  10 +-
 modules/byteArray.js                    |  19 +
 modules/modules.gresource.xml           |   1 +
 modules/overrides/GLib.js               |   4 +-
 12 files changed, 187 insertions(+), 887 deletions(-)
---
diff --git a/NEWS b/NEWS
index e05751a3..dd73c021 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,37 @@ NEXT
 
 - GJS now depends on SpiderMonkey 60 and requires a compiler capable of C++14.
 
+- Deprecation: the custom ByteArray is now discouraged. Instead of ByteArray,
+  use Javascript's native Uint8Array. The ByteArray module still contains
+  functions for converting between byte arrays, strings, and GLib.Bytes
+  instances. With a few exceptions, old code will continue to work.
+
+  * ByteArray.ByteArray -> Uint8Array
+  * ByteArray.fromArray() -> Uint8Array.from()
+  * ByteArray.ByteArray.prototype.toString() -> ByteArray.toString()
+  * ByteArray.ByteArray.prototype.toGBytes() -> ByteArray.toGBytes()
+  * ByteArray.fromString(), ByteArray.fromGBytes() remain the same
+
+  * Unlike ByteArray, Uint8Array's length is fixed. Assigning an element past
+    the end of a ByteArray would lengthen the array. Now, it is ignored.
+    Instead use Uint8Array.of(), for example, this code:
+
+        let a = ByteArray.fromArray([97, 98, 99, 100]);
+        a[4] = 101;
+
+    should be replaced by this code:
+
+        let a = Uint8Array.from([97, 98, 99, 100]);
+        a = Uint8Array.of(...a, 101);
+
+    The length of the byte array must be set at creation time. This code will
+    not work anymore:
+
+        let a = new ByteArray.ByteArray();
+        a[0] = 255;
+
+    Instead, use "new Uint8Array(1)" to reserve the correct length.
+
 Version 1.53.4
 --------------
 
diff --git a/doc/ByteArray.md b/doc/ByteArray.md
index 3c5dbf99..53b65fe2 100644
--- a/doc/ByteArray.md
+++ b/doc/ByteArray.md
@@ -1,16 +1,10 @@
-The ByteArray type in the `imports.byteArray` module is based on an
-ECMAScript 4 proposal that was never adopted. This can be found at:
-http://wiki.ecmascript.org/doku.php?id=proposals:bytearray
-and the wikitext of that page is appended to this file.
+The `imports.byteArray` module was originally based on an
+ECMAScript 4 proposal that was never adopted.
+Now that ES6 has typed arrays, we use `Uint8Array` to represent byte
+arrays in GJS and add some extra functions for conversion to and from
+strings and `GLib.Bytes`.
 
-The main difference from the ECMA proposal is that gjs's ByteArray is
-inside a module, and `toString()`/`fromString()` default to UTF-8 and take
-optional encoding arguments.
-
-There are a number of more elaborate byte array proposals in the
-Common JS project at http://wiki.commonjs.org/wiki/Binary
-
-We went with the ECMA proposal because it seems simplest, and the main
+Unlike the old custom `ByteArray`, `Uint8Array` is not resizable. The main
 goal for most gjs users will be to shovel bytes between various C
 APIs, for example reading from an IO stream and then pushing the bytes
 into a parser. Actually manipulating bytes in JS is likely to be
@@ -20,138 +14,30 @@ messing with bytes, can be done in C.
 
 ---
 
-ECMAScript proposal follows; remember it's almost but not quite like
-gjs ByteArray, in particular we use UTF-8 instead of busted Latin-1 as
-default encoding.
-
----
-
-# ByteArray #
-
-(Also see the [discussion page][1] for this proposal)
-
-## Overview ##
-
-In previous versions of ECMAScript, there wasn't a good way to efficiently represent a packed array of 
arbitrary bytes. The predefined core object Array is inefficient for this purpose; some developers have 
(mis)used character strings for this purpose, which may be slightly more efficient for some implementations, 
but still a misuse of the string type and either a less efficient use of memory (if one byte per character 
was stored) or cycles (if two bytes per char).
-
-Edition 4 will add a new predefined core object, ByteArray. A ByteArray can be thought of as similar to an 
Array of uint ([uint]) with each element truncated to the integer range of 0..255.
-
-## Creating a ByteArray ##
-
-To create a ByteArray object:
-
-```js
-byteArrayObject = new ByteArray(byteArrayLength:uint)
-```
-
-byteArrayLength is the initial length of the ByteArray, in bytes. If omitted, the initial length is zero.
-
-All elements in a ByteArray are initialized to the value of zero.
-
-Unlike Array, there is no special form that allows you to list the initial values for the ByteArray's 
elements. However, the ByteArray class has an `intrinsic::to` static method that can convert an Array to a 
ByteArray, and implementations are free to optimize away the Array instance if it is used exclusively to 
initialize a ByteArray:
-
-```js
-var values:ByteArray = [1, 2, 3, 4];   // legal by virtue of ByteArray.intrinsic::to
-```
-
-## Populating a ByteArray ##
-
-You can populate a ByteArray by assigning values to its elements. Each element can hold only an unsigned 
integer in the range 0..255 (inclusive). Values will be converted to unsigned integer (if necessary), then 
truncated to the least-significant 8 bits.
-
-For example,
-
-```js
-var e = new ByteArray(3);
-
-e[0] = 0x12;           // stores 18
-e[1] = Math.PI;                // stores 3
-e[2] = "foo";          // stores 0 (generates compile error in strict mode)
-e[2] = "42";           // stores 42 (generates compile error in strict mode)
-e[2] = null;           // stores 0
-e[2] = undefined;      // stores 0
-```
-
-## Referring to ByteArray Elements ##
-
-You refer to a ByteArray's elements by using the element's ordinal number; you refer to the first element of 
the array as `myArray[0]` and the second element of the array as `myArray[1]`, etc. The index of the elements 
begins with zero (0), but the length of array (for example, `myArray.length`) reflects the number of elements 
in the array.
-
-## ByteArray Methods ##
-
-The ByteArray object has the follow methods:
-
-### `static function fromString(s:String):ByteArray` ###
-
-Convert a String into newly constructed ByteArray; this creates a new ByteArray of the same length as the 
String, then assigns each ByteArray entry the corresponding charCodeAt() value of the String. As with other 
ByteArray assignments, only the lower 8 bits of the charCode value will be retained.
-
-```js
-class ByteArray {
-  ...
-  static function fromString(s:String):ByteArray
-  {
-    var a:ByteArray = new Bytearray(s.length);
-    for (var i:int = 0; i < s.length; ++i)
-      a[i] = s.charCodeAt(i);
-    return a;
-  }
-  ...
-}
-```
-
-### `static function fromArray(s:Array):ByteArray` ###
-
-Converts an Array into a newly constructed ByteArray; this creates a new ByteArray of the same length as the 
input Array, then assigns each ByteArray entry the corresponding entry value of the Array. As with other 
ByteArray assignments, only the lower 8 bits of the charCode value will be retained.
-
-```js
-class ByteArray {
-  ...
-  static function fromArray(s:Array):ByteArray
-  {
-    var a:ByteArray = new Bytearray(s.length);
-    for (var i:int = 0; i < s.length; ++i)
-      a[i] = s[i];
-    return a;
-  ...
-}
-```
-
-### `function toString():String` ###
-
-Converts the ByteArray into a literal string, with each character entry set to the value of the 
corresponding ByteArray entry.
-
-The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to 
`ByteArray.fromString()`, i.e., `b === ByteArray.fromString(b.toString())`. (Note that the reverse is not 
guaranteed to be true: `ByteArray.fromString(s).toString != s` unless all character codes in `s` are <= 255)
-
-```js
-class ByteArray {
-  ...
-  function toString():String
-  {
-    // XXX return String.fromCharCode.apply(String, this);
-    var s:String = "";
-    for (var i:int = 0; i < this.length; ++i)
-      s += String.fromCharCode(this[i]);
-    return s;
-  }
-  ...
-}
-```
+## ByteArray Functions ##
 
-## ByteArray Properties ##
+The ByteArray module has the following functions:
 
-The ByteArray object has the following properties:
+### `fromString(s:String, encoding:String):Uint8Array` ###
 
-### `length:uint` ###
+Convert a String into a newly constructed `Uint8Array`; this creates a
+new `Uint8Array` of the same length as the String, then assigns each
+`Uint8Array` entry the corresponding byte value of the String encoded
+according to the given encoding (or UTF-8 if not given).
 
-This property contains the number of bytes in the ByteArray. Setting the length property to a smaller value 
decreases the size of the ByteArray, discarding the unused bytes. Setting the length property to a larger 
value increases the size of the ByteArray, initializing all newly-allocated elements to zero.
+### `toString(a:Uint8Array, encoding:String):String` ###
 
-### `prototype:Object` ###
+Converts the `Uint8Array` into a literal string. The bytes are
+interpreted according to the given encoding (or UTF-8 if not given).
 
-This property contains the methods of ByteArray.
+The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to 
`ByteArray.fromString()`, i.e., `b === ByteArray.fromString(ByteArray.toString(b, encoding), encoding)`.
 
-## Prior Art ##
+### `fromGBytes(b:GLib.Bytes):Uint8Array` ###
 
-Adobe's ActionScript 3.0 provides [`flash.utils.ByteArray`][2], which was the primary inspiration for this 
proposal. Note that the ActionScript version of ByteArray includes additional functionality which has been 
omitted here for the sake of allowing small implementations; it is anticipated that the extra functionality 
can be written in ES4 itself and provided as a standard library.
+Convert a `GLib.Bytes` instance into a newly constructed `Uint8Array`.
+The contents are copied.
 
-[Synopsis of ActionScript's implementation too detailed and moved to [discussion][1] page]
+### `toGBytes(a:Uint8Array):GLib.Bytes` ###
 
-[1] http://wiki.ecmascript.org/doku.php?id=discussion:bytearray
-[2] http://livedocs.macromedia.com/flex/2/langref/flash/utils/ByteArray.html
+Converts the `Uint8Array` into a `GLib.Bytes` instance.
+The contents are copied.
diff --git a/gi/arg.cpp b/gi/arg.cpp
index f6c014ac..180c0872 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -1666,10 +1666,9 @@ gjs_value_to_g_argument(JSContext      *context,
                     /* Handle Struct/Union first since we don't necessarily need a GType for them */
                     /* We special case Closures later, so skip them here */
                     !g_type_is_a(gtype, G_TYPE_CLOSURE)) {
-
-                    if (g_type_is_a(gtype, G_TYPE_BYTES)
-                        && gjs_typecheck_bytearray(context, obj, false)) {
-                        arg->v_pointer = gjs_byte_array_get_bytes(context, obj);
+                    if (g_type_is_a(gtype, G_TYPE_BYTES) &&
+                        JS_IsUint8Array(obj)) {
+                        arg->v_pointer = gjs_byte_array_get_bytes(obj);
                     } else if (g_type_is_a(gtype, G_TYPE_ERROR)) {
                         if (!gjs_typecheck_gerror(context, obj, true)) {
                             arg->v_pointer = NULL;
@@ -1955,13 +1954,13 @@ _Pragma("GCC diagnostic pop")
         GIArrayType array_type = g_type_info_get_array_type(type_info);
 
         /* First, let's handle the case where we're passed an instance
-         * of our own byteArray class.
+         * of Uint8Array and it needs to be marshalled to GByteArray.
          */
         if (value.isObjectOrNull()) {
             JS::RootedObject bytearray_obj(context, value.toObjectOrNull());
-            if (gjs_typecheck_bytearray(context, bytearray_obj, false)
-                && array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
-                arg->v_pointer = gjs_byte_array_get_byte_array(context, bytearray_obj);
+            if (JS_IsUint8Array(bytearray_obj) &&
+                array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
+                arg->v_pointer = gjs_byte_array_get_byte_array(bytearray_obj);
                 break;
             } else {
                 /* Fall through, !handled */
@@ -2273,13 +2272,7 @@ gjs_array_from_carray_internal (JSContext             *context,
 
     /* Special case array(guint8) */
     if (element_type == GI_TYPE_TAG_UINT8) {
-        GByteArray gbytearray;
-
-        gbytearray.data = (guint8 *) array;
-        gbytearray.len = length;
-
-        JS::RootedObject obj(context,
-            gjs_byte_array_from_byte_array(context, &gbytearray));
+        JSObject* obj = gjs_byte_array_from_data(context, length, array);
         if (!obj)
             return false;
         value_p.setObject(*obj);
@@ -2487,13 +2480,8 @@ gjs_array_from_zero_terminated_c_array (JSContext             *context,
 
     /* Special case array(guint8) */
     if (element_type == GI_TYPE_TAG_UINT8) {
-        GByteArray gbytearray;
-
-        gbytearray.data = (guint8 *) c_array;
-        gbytearray.len = strlen((const char *) c_array);
-
-        JS::RootedObject obj(context,
-            gjs_byte_array_from_byte_array(context, &gbytearray));
+        size_t len = strlen(static_cast<char*>(c_array));
+        JSObject* obj = gjs_byte_array_from_data(context, len, c_array);
         if (!obj)
             return false;
         value_p.setObject(*obj);
@@ -3004,10 +2992,12 @@ gjs_value_from_g_argument (JSContext             *context,
                 return gjs_array_from_fixed_size_array(context, value_p, type_info, arg->v_pointer);
             }
         } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_BYTE_ARRAY) {
-            JSObject *array = gjs_byte_array_from_byte_array(context,
-                                                             (GByteArray*)arg->v_pointer);
+            auto* byte_array = static_cast<GByteArray*>(arg->v_pointer);
+            JSObject* array =
+                gjs_byte_array_from_byte_array(context, byte_array);
             if (!array) {
-                gjs_throw(context, "Couldn't convert GByteArray to a ByteArray");
+                gjs_throw(context,
+                          "Couldn't convert GByteArray to a Uint8Array");
                 return false;
             }
             value_p.setObject(*array);
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 59f55dfc..96368482 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -21,384 +21,27 @@
  * IN THE SOFTWARE.
  */
 
-#include <config.h>
-#include <string.h>
 #include <glib.h>
+
 #include "byteArray.h"
 #include "gi/boxed.h"
-#include "jsapi-class.h"
-#include "jsapi-wrapper.h"
 #include "jsapi-util-args.h"
-#include <girepository.h>
-#include <util/log.h>
-
-typedef struct {
-    GByteArray *array;
-    GBytes     *bytes;
-} ByteArrayInstance;
-
-extern struct JSClass gjs_byte_array_class;
-GJS_DEFINE_PRIV_FROM_JS(ByteArrayInstance, gjs_byte_array_class)
-
-static bool   byte_array_get_prop      (JSContext    *context,
-                                        JS::HandleObject obj,
-                                        JS::HandleId id,
-                                        JS::MutableHandleValue value_p);
-static bool   byte_array_set_prop      (JSContext    *context,
-                                        JS::HandleObject obj,
-                                        JS::HandleId id,
-                                        JS::MutableHandleValue value_p,
-                                        JS::ObjectOpResult&    result);
-GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array);
-static void   byte_array_finalize      (JSFreeOp     *fop,
-                                        JSObject     *obj);
-
-static JSObject *gjs_byte_array_get_proto(JSContext *);
-
-static const struct JSClassOps gjs_byte_array_class_ops = {
-    NULL,  /* addProperty */
-    NULL,  /* deleteProperty */
-    byte_array_get_prop,
-    byte_array_set_prop,
-    NULL,  /* enumerate */
-    NULL,  /* resolve */
-    nullptr,  /* mayResolve */
-    byte_array_finalize
-};
-
-struct JSClass gjs_byte_array_class = {
-    "ByteArray",
-    JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
-    &gjs_byte_array_class_ops
-};
-
-bool
-gjs_typecheck_bytearray(JSContext       *context,
-                        JS::HandleObject object,
-                        bool             throw_error)
-{
-    return do_base_typecheck(context, object, throw_error);
-}
-
-static JS::Value
-gjs_value_from_gsize(gsize v)
-{
-    if (v <= (gsize) JSVAL_INT_MAX) {
-        return JS::Int32Value(v);
-    }
-    return JS::NumberValue(v);
-}
-
-static void
-byte_array_ensure_array (ByteArrayInstance  *priv)
-{
-    if (priv->bytes) {
-        priv->array = g_bytes_unref_to_array(priv->bytes);
-        priv->bytes = NULL;
-    } else {
-        g_assert(priv->array);
-    }
-}
-
-static void
-byte_array_ensure_gbytes (ByteArrayInstance  *priv)
-{
-    if (priv->array) {
-        priv->bytes = g_byte_array_free_to_bytes(priv->array);
-        priv->array = NULL;
-    } else {
-        g_assert(priv->bytes);
-    }
-}
-
-static bool
-gjs_value_to_gsize(JSContext         *context,
-                   JS::HandleValue    value,
-                   gsize             *v_p)
-{
-    guint32 val32;
-
-    /* Just JS::ToUint32() would work. However, we special case ints for a nicer
-     * error message on negative indices.
-     */
-    if (value.isInt32()) {
-        int i = value.toInt32();
-        if (i < 0) {
-            gjs_throw(context, "Negative length or index %d is not allowed for ByteArray",
-                      i);
-            return false;
-        }
-        *v_p = i;
-        return true;
-    } else {
-        bool ret;
-        /* This is pretty liberal (it converts about anything to
-         * a number) but it's what we use elsewhere in gjs too.
-         */
-
-        ret = JS::ToUint32(context, value, &val32);
-        *v_p = val32;
-        return ret;
-    }
-}
-
-static bool
-gjs_value_to_byte(JSContext         *context,
-                  JS::HandleValue    value,
-                  guint8            *v_p)
-{
-    gsize v;
-
-    if (!gjs_value_to_gsize(context, value, &v))
-        return false;
-
-    if (v >= 256) {
-        gjs_throw(context,
-                  "Value %" G_GSIZE_FORMAT " is not a valid byte; must be in range [0,255]",
-                  v);
-        return false;
-    }
-
-    *v_p = v;
-    return true;
-}
-
-static bool
-byte_array_get_index(JSContext         *context,
-                     JS::HandleObject obj,
-                     ByteArrayInstance *priv,
-                     gsize              idx,
-                     JS::MutableHandleValue value_p)
-{
-    gsize len;
-    guint8 *data;
-    
-    gjs_byte_array_peek_data(context, obj, &data, &len);
-
-    if (idx >= len) {
-        gjs_throw(context,
-                  "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %lu",
-                  idx,
-                  (unsigned long)len);
-        return false;
-    }
-
-    value_p.setInt32(data[idx]);
-
-    return true;
-}
-
-/* a hook on getting a property; set value_p to override property's value.
- * Return value is false on OOM/exception.
- */
-static bool
-byte_array_get_prop(JSContext *context,
-                    JS::HandleObject obj,
-                    JS::HandleId id,
-                    JS::MutableHandleValue value_p)
-{
-    ByteArrayInstance *priv;
-
-    priv = priv_from_js(context, obj);
-
-    if (!priv)
-        return true; /* prototype, not an instance. */
-
-    JS::RootedValue id_value(context);
-    if (!JS_IdToValue(context, id, &id_value))
-        return false;
-
-    /* First handle array indexing */
-    if (id_value.isNumber()) {
-        gsize idx;
-        if (!gjs_value_to_gsize(context, id_value, &idx))
-            return false;
-        return byte_array_get_index(context, obj, priv, idx, value_p);
-    }
-
-    /* We don't special-case anything else for now. Regular JS arrays
-     * allow string versions of ints for the index, we don't bother.
-     */
-
-    return true;
-}
-
-static bool
-byte_array_length_getter(JSContext *context,
-                         unsigned   argc,
-                         JS::Value *vp)
-{
-    GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv);
-    gsize len = 0;
-
-    if (!priv)
-        return true; /* prototype, not an instance. */
-
-    if (priv->array != NULL)
-        len = priv->array->len;
-    else if (priv->bytes != NULL)
-        len = g_bytes_get_size (priv->bytes);
-    args.rval().set(gjs_value_from_gsize(len));
-    return true;
-}
-
-static bool
-byte_array_length_setter(JSContext *context,
-                         unsigned   argc,
-                         JS::Value *vp)
-{
-    GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv);
-    gsize len = 0;
-
-    if (!priv)
-        return true; /* prototype, not instance */
-
-    byte_array_ensure_array(priv);
-
-    if (!gjs_value_to_gsize(context, args[0], &len)) {
-        gjs_throw(context,
-                  "Can't set ByteArray length to non-integer");
-        return false;
-    }
-    g_byte_array_set_size(priv->array, len);
-    args.rval().setUndefined();
-    return true;
-}
-
-static bool
-byte_array_set_index(JSContext         *context,
-                     JS::HandleObject obj,
-                     ByteArrayInstance *priv,
-                     gsize              idx,
-                     JS::MutableHandleValue value_p,
-                     JS::ObjectOpResult&    result)
-{
-    guint8 v;
-
-    if (!gjs_value_to_byte(context, value_p, &v)) {
-        return false;
-    }
-
-    byte_array_ensure_array(priv);
-
-    /* grow the array if necessary */
-    if (idx >= priv->array->len) {
-        g_byte_array_set_size(priv->array,
-                              idx + 1);
-    }
-
-    g_array_index(priv->array, guint8, idx) = v;
-
-    /* Stop JS from storing a copy of the value */
-    value_p.setUndefined();
-
-    return result.succeed();
-}
-
-/* a hook on setting a property; set value_p to override property value to
- * be set. Return value is false on OOM/exception.
- */
-static bool
-byte_array_set_prop(JSContext *context,
-                    JS::HandleObject obj,
-                    JS::HandleId id,
-                    JS::MutableHandleValue value_p,
-                    JS::ObjectOpResult&    result)
-{
-    ByteArrayInstance *priv;
-
-    priv = priv_from_js(context, obj);
-
-    if (!priv)
-        return result.succeed(); /* prototype, not an instance. */
-
-    JS::RootedValue id_value(context);
-    if (!JS_IdToValue(context, id, &id_value))
-        return false;
-
-    /* First handle array indexing */
-    if (id_value.isNumber()) {
-        gsize idx;
-        if (!gjs_value_to_gsize(context, id_value, &idx))
-            return false;
-
-        return byte_array_set_index(context, obj, priv, idx, value_p, result);
-    }
+#include "jsapi-wrapper.h"
 
-    /* We don't special-case anything else for now */
+/* Callbacks to use with JS_NewExternalArrayBuffer() */
 
-    return result.succeed();
+static void gfree_arraybuffer_contents(void* contents, void* unused) {
+    g_free(contents);
 }
 
-static GByteArray *
-gjs_g_byte_array_new(int preallocated_length)
-{
-    GByteArray *array;
-
-    /* can't use g_byte_array_new() because we need to clear to zero.
-     * We nul-terminate too for ease of toString() and for security
-     * paranoia.
-     */
-    array =  (GByteArray*) g_array_sized_new (true, /* nul-terminated */
-                                              true, /* clear to zero */
-                                              1, /* element size */
-                                              preallocated_length);
-   if (preallocated_length > 0) {
-       /* we want to not only allocate the size, but have it
-        * already be the array's length.
-        */
-       g_byte_array_set_size(array, preallocated_length);
-   }
-
-   return array;
-}
-
-GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array)
-{
-    GJS_NATIVE_CONSTRUCTOR_VARIABLES(byte_array)
-    ByteArrayInstance *priv;
-    gsize preallocated_length;
-
-    GJS_NATIVE_CONSTRUCTOR_PRELUDE(byte_array);
-
-    preallocated_length = 0;
-    if (argc >= 1) {
-        if (!gjs_value_to_gsize(context, argv[0], &preallocated_length)) {
-            gjs_throw(context,
-                      "Argument to ByteArray constructor should be a positive number for array length");
-            return false;
-        }
-    }
-
-    priv = g_slice_new0(ByteArrayInstance);
-    priv->array = gjs_g_byte_array_new(preallocated_length);
-    g_assert(priv_from_js(context, object) == NULL);
-    JS_SetPrivate(object, priv);
-
-    GJS_NATIVE_CONSTRUCTOR_FINISH(byte_array);
-
-    return true;
+static void bytes_ref_arraybuffer(void* contents, void* user_data) {
+    auto* gbytes = static_cast<GBytes*>(user_data);
+    g_bytes_ref(gbytes);
 }
 
-static void
-byte_array_finalize(JSFreeOp *fop,
-                    JSObject *obj)
-{
-    ByteArrayInstance *priv;
-
-    priv = (ByteArrayInstance*) JS_GetPrivate(obj);
-
-    if (!priv)
-        return; /* prototype, not instance */
-
-    if (priv->array) {
-        g_byte_array_free(priv->array, true);
-        priv->array = NULL;
-    } else if (priv->bytes) {
-        g_clear_pointer(&priv->bytes, g_bytes_unref);
-    }
-
-    g_slice_free(ByteArrayInstance, priv);
+static void bytes_unref_arraybuffer(void* contents, void* user_data) {
+    auto* gbytes = static_cast<GBytes*>(user_data);
+    g_bytes_unref(gbytes);
 }
 
 /* implement toString() with an optional encoding arg */
@@ -407,22 +50,18 @@ to_string_func(JSContext *context,
                unsigned   argc,
                JS::Value *vp)
 {
-    GJS_GET_PRIV(context, argc, vp, argv, to, ByteArrayInstance, priv);
+    JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
     GjsAutoJSChar encoding;
+    JS::RootedObject byte_array(context);
     bool encoding_is_utf8;
-    gchar *data;
-
-    if (!priv)
-        return true; /* prototype, not instance */
-
-    byte_array_ensure_array(priv);
+    uint8_t* data;
 
-    if (argc >= 1 && argv[0].isString()) {
-        JS::RootedString str(context, argv[0].toString());
-        encoding = JS_EncodeStringToUTF8(context, str);
-        if (!encoding)
-            return false;
+    if (!gjs_parse_call_args(context, "toString", argv, "o|s",
+                             "byteArray", &byte_array,
+                             "encoding", &encoding))
+        return false;
 
+    if (encoding) {
         /* maybe we should be smarter about utf8 synonyms here.
          * doesn't matter much though. encoding_is_utf8 is
          * just an optimization anyway.
@@ -432,17 +71,16 @@ to_string_func(JSContext *context,
         encoding_is_utf8 = true;
     }
 
-    if (priv->array->len == 0)
-        /* the internal data pointer could be NULL in this case */
-        data = (gchar*)"";
-    else
-        data = (gchar*)priv->array->data;
+    uint32_t len;
+    bool is_shared_memory;
+    js::GetUint8ArrayLengthAndData(byte_array, &len, &is_shared_memory, &data);
 
     if (encoding_is_utf8) {
         /* optimization, avoids iconv overhead and runs
          * libmozjs hardwired utf8-to-utf16
          */
-        return gjs_string_from_utf8_n(context, data, priv->array->len, argv.rval());
+        return gjs_string_from_utf8_n(context, reinterpret_cast<char*>(data),
+                                      len, argv.rval());
     } else {
         bool ok = false;
         gsize bytes_written;
@@ -452,13 +90,9 @@ to_string_func(JSContext *context,
         char16_t *u16_out;
 
         error = NULL;
-        u16_str = g_convert(data,
-                           priv->array->len,
-                           "UTF-16",
-                           encoding,
-                           NULL, /* bytes read */
-                           &bytes_written,
-                           &error);
+        u16_str = g_convert(reinterpret_cast<char*>(data), len, "UTF-16",
+                            encoding, nullptr, /* bytes read */
+                            &bytes_written, &error);
         if (u16_str == NULL) {
             /* frees the GError */
             gjs_throw_g_error(context, error);
@@ -489,40 +123,25 @@ to_gbytes_func(JSContext *context,
                unsigned   argc,
                JS::Value *vp)
 {
-    GJS_GET_PRIV(context, argc, vp, rec, to, ByteArrayInstance, priv);
+    JS::CallArgs rec = JS::CallArgsFromVp(argc, vp);
     JSObject *ret_bytes_obj;
     GIBaseInfo *gbytes_info;
+    JS::RootedObject byte_array(context);
 
-    if (!priv)
-        return true; /* prototype, not instance */
-
-    byte_array_ensure_gbytes(priv);
+    if (!gjs_parse_call_args(context, "toGBytes", rec, "o",
+                             "byteArray", &byte_array))
+        return false;
 
+    GBytes* bytes = gjs_byte_array_get_bytes(byte_array);
     gbytes_info = g_irepository_find_by_gtype(NULL, G_TYPE_BYTES);
     ret_bytes_obj = gjs_boxed_from_c_struct(context, (GIStructInfo*)gbytes_info,
-                                            priv->bytes, GJS_BOXED_CREATION_NONE);
+                                            bytes, GJS_BOXED_CREATION_NONE);
+    g_bytes_unref(bytes);
 
     rec.rval().setObjectOrNull(ret_bytes_obj);
     return true;
 }
 
-static JSObject*
-byte_array_new(JSContext *context)
-{
-    ByteArrayInstance *priv;
-
-    JS::RootedObject proto(context, gjs_byte_array_get_proto(context));
-    JS::RootedObject array(context,
-        JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto));
-
-    priv = g_slice_new0(ByteArrayInstance);
-
-    g_assert(priv_from_js(context, array) == NULL);
-    JS_SetPrivate(array, priv);
-
-    return array;
-}
-
 /* fromString() function implementation */
 static bool
 from_string_func(JSContext *context,
@@ -530,33 +149,17 @@ from_string_func(JSContext *context,
                  JS::Value *vp)
 {
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-    ByteArrayInstance *priv;
     GjsAutoJSChar encoding;
+    GjsAutoJSChar utf8;
     bool encoding_is_utf8;
-    JS::RootedObject obj(context, byte_array_new(context));
+    JS::RootedObject obj(context), array_buffer(context);
 
-    if (!obj)
+    if (!gjs_parse_call_args(context, "fromString", argv, "s|s",
+                             "string", &utf8,
+                             "encoding", &encoding))
         return false;
 
-    priv = priv_from_js(context, obj);
-    g_assert (priv != NULL);
-
-    g_assert(argc > 0); /* because we specified min args 1 */
-
-    priv->array = gjs_g_byte_array_new(0);
-
-    if (!argv[0].isString()) {
-        gjs_throw(context,
-                  "byteArray.fromString() called with non-string as first arg");
-        return false;
-    }
-
-    if (argc > 1 && argv[1].isString()) {
-        JS::RootedString str(context, argv[1].toString());
-        encoding = JS_EncodeStringToUTF8(context, str);
-        if (!encoding)
-            return false;
-
+    if (argc > 1) {
         /* maybe we should be smarter about utf8 synonyms here.
          * doesn't matter much though. encoding_is_utf8 is
          * just an optimization anyway.
@@ -570,15 +173,9 @@ from_string_func(JSContext *context,
         /* optimization? avoids iconv overhead and runs
          * libmozjs hardwired utf16-to-utf8.
          */
-        JS::RootedString str(context, argv[0].toString());
-        GjsAutoJSChar utf8 = JS_EncodeStringToUTF8(context, str);
-        if (!utf8)
-            return false;
-
-        g_byte_array_set_size(priv->array, 0);
-        g_byte_array_append(priv->array,
-                            reinterpret_cast<const guint8*>(utf8.get()),
-                            strlen(utf8));
+        size_t len = strlen(utf8);
+        array_buffer =
+            JS_NewArrayBufferWithContents(context, len, utf8.release());
     } else {
         JSString *str = argv[0].toString();  /* Rooted by argv */
         GError *error = NULL;
@@ -622,77 +219,14 @@ from_string_func(JSContext *context,
             return false;
         }
 
-        g_byte_array_set_size(priv->array, 0);
-        g_byte_array_append(priv->array, (guint8*) encoded, bytes_written);
-
-        g_free(encoded);
-    }
-
-    argv.rval().setObject(*obj);
-    return true;
-}
-
-/* fromArray() function implementation */
-static bool
-from_array_func(JSContext *context,
-                unsigned   argc,
-                JS::Value *vp)
-{
-    JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-    ByteArrayInstance *priv;
-    guint32 len;
-    guint32 i;
-    bool is_array;
-    JS::RootedObject obj(context, byte_array_new(context));
-
-    if (!obj)
-        return false;
-
-    priv = priv_from_js(context, obj);
-    g_assert (priv != NULL);
-
-    g_assert(argc > 0); /* because we specified min args 1 */
-
-    priv->array = gjs_g_byte_array_new(0);
-
-    JS::RootedObject array_obj(context, &argv[0].toObject());
-    if (!JS_IsArrayObject(context, array_obj, &is_array))
-        return false;
-    if (!is_array) {
-        gjs_throw(context,
-                  "byteArray.fromArray() called with non-array as first arg");
-        return false;
+        array_buffer =
+            JS_NewExternalArrayBuffer(context, bytes_written, encoded, nullptr,
+                                      gfree_arraybuffer_contents, nullptr);
     }
 
-    if (!JS_GetArrayLength(context, array_obj, &len)) {
-        gjs_throw(context,
-                  "byteArray.fromArray() can't get length of first array arg");
+    if (!array_buffer)
         return false;
-    }
-
-    g_byte_array_set_size(priv->array, len);
-
-    JS::RootedValue elem(context);
-    for (i = 0; i < len; ++i) {
-        guint8 b;
-
-        elem = JS::UndefinedValue();
-        if (!JS_GetElement(context, array_obj, i, &elem)) {
-            /* this means there was an exception, while elem.isUndefined()
-             * means no element found
-             */
-            return false;
-        }
-
-        if (elem.isUndefined())
-            continue;
-
-        if (!gjs_value_to_byte(context, elem, &b))
-            return false;
-
-        g_array_index(priv->array, guint8, i) = b;
-    }
-
+    obj = JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1);
     argv.rval().setObject(*obj);
     return true;
 }
@@ -705,9 +239,8 @@ from_gbytes_func(JSContext *context,
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
     JS::RootedObject bytes_obj(context);
     GBytes *gbytes;
-    ByteArrayInstance *priv;
 
-    if (!gjs_parse_call_args(context, "overrides_gbytes_to_array", argv, "o",
+    if (!gjs_parse_call_args(context, "fromGBytes", argv, "o",
                              "bytes", &bytes_obj))
         return false;
 
@@ -716,120 +249,63 @@ from_gbytes_func(JSContext *context,
 
     gbytes = (GBytes*) gjs_c_struct_from_boxed(context, bytes_obj);
 
-    JS::RootedObject obj(context, byte_array_new(context));
-    if (!obj)
+    size_t len;
+    const void* data = g_bytes_get_data(gbytes, &len);
+    JS::RootedObject array_buffer(
+        context,
+        JS_NewExternalArrayBuffer(
+            context, len,
+            const_cast<void*>(data),  // the ArrayBuffer won't modify the data
+            bytes_ref_arraybuffer, bytes_unref_arraybuffer, gbytes));
+    if (!array_buffer)
         return false;
-    priv = priv_from_js(context, obj);
-    g_assert (priv != NULL);
 
-    priv->bytes = g_bytes_ref(gbytes);
+    JS::RootedObject obj(
+        context, JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1));
+    if (!obj)
+        return false;
 
     argv.rval().setObject(*obj);
     return true;
 }
 
-JSObject *
-gjs_byte_array_from_byte_array (JSContext *context,
-                                GByteArray *array)
-{
-    ByteArrayInstance *priv;
-
-    g_return_val_if_fail(context != NULL, NULL);
-    g_return_val_if_fail(array != NULL, NULL);
+JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data) {
+    JS::RootedObject array_buffer(
+        cx, JS_NewArrayBufferWithContents(cx, nbytes, g_memdup(data, nbytes)));
+    if (!array_buffer)
+        return nullptr;
 
-    JS::RootedObject proto(context, gjs_byte_array_get_proto(context));
-    JS::RootedObject object(context,
-        JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto));
-
-    if (!object) {
-        gjs_throw(context, "failed to create byte array");
-        return NULL;
-    }
-
-    priv = g_slice_new0(ByteArrayInstance);
-    g_assert(priv_from_js(context, object) == NULL);
-    JS_SetPrivate(object, priv);
-    priv->array = g_byte_array_new();
-    priv->array->data = (guint8*) g_memdup(array->data, array->len);
-    priv->array->len = array->len;
-
-    return object;
+    return JS_NewUint8ArrayWithBuffer(cx, array_buffer, 0, -1);
 }
 
-GBytes *
-gjs_byte_array_get_bytes (JSContext       *context,
-                          JS::HandleObject object)
-{
-    ByteArrayInstance *priv;
-    priv = priv_from_js(context, object);
-    g_assert(priv != NULL);
-
-    byte_array_ensure_gbytes(priv);
-
-    return g_bytes_ref (priv->bytes);
+JSObject* gjs_byte_array_from_byte_array(JSContext* cx, GByteArray* array) {
+    return gjs_byte_array_from_data(cx, array->len, array->data);
 }
 
-GByteArray *
-gjs_byte_array_get_byte_array (JSContext       *context,
-                               JS::HandleObject obj)
-{
-    ByteArrayInstance *priv;
-    priv = priv_from_js(context, obj);
-    g_assert(priv != NULL);
-
-    byte_array_ensure_array(priv);
+GBytes* gjs_byte_array_get_bytes(JS::HandleObject obj) {
+    bool is_shared_memory;
+    uint32_t len;
+    uint8_t* data;
 
-    return g_byte_array_ref (priv->array);
+    js::GetUint8ArrayLengthAndData(obj, &len, &is_shared_memory, &data);
+    return g_bytes_new(data, len);
 }
 
-void
-gjs_byte_array_peek_data (JSContext       *context,
-                          JS::HandleObject obj,
-                          guint8         **out_data,
-                          gsize           *out_len)
-{
-    ByteArrayInstance *priv;
-    priv = priv_from_js(context, obj);
-    g_assert(priv != NULL);
-    
-    if (priv->array != NULL) {
-        *out_data = (guint8*)priv->array->data;
-        *out_len = (gsize)priv->array->len;
-    } else if (priv->bytes != NULL) {
-        *out_data = (guint8*)g_bytes_get_data(priv->bytes, out_len);
-    } else {
-        g_assert_not_reached();
-    }
+GByteArray* gjs_byte_array_get_byte_array(JS::HandleObject obj) {
+    return g_bytes_unref_to_array(gjs_byte_array_get_bytes(obj));
 }
 
-static JSPropertySpec gjs_byte_array_proto_props[] = {
-    JS_PSGS("length", byte_array_length_getter, byte_array_length_setter,
-            JSPROP_PERMANENT),
-    JS_PS_END
-};
-
-static JSFunctionSpec gjs_byte_array_proto_funcs[] = {
-    JS_FN("toString", to_string_func, 0, 0),
-    JS_FN("toGBytes", to_gbytes_func, 0, 0),
-    JS_FS_END};
-
-static JSFunctionSpec *gjs_byte_array_static_funcs = nullptr;
-
 static JSFunctionSpec gjs_byte_array_module_funcs[] = {
-    JS_FN("fromString", from_string_func, 1, 0),
-    JS_FN("fromArray", from_array_func, 1, 0),
+    JS_FN("fromString", from_string_func, 2, 0),
     JS_FN("fromGBytes", from_gbytes_func, 1, 0),
+    JS_FN("toGBytes", to_gbytes_func, 1, 0),
+    JS_FN("toString", to_string_func, 2, 0),
     JS_FS_END};
 
-GJS_DEFINE_PROTO_FUNCS(byte_array)
-
 bool
 gjs_define_byte_array_stuff(JSContext              *cx,
                             JS::MutableHandleObject module)
 {
     module.set(JS_NewPlainObject(cx));
-
-    JS::RootedObject proto(cx);
-    return gjs_byte_array_define_proto(cx, module, &proto) &&
-        JS_DefineFunctions(cx, module, gjs_byte_array_module_funcs);
+    return JS_DefineFunctions(cx, module, gjs_byte_array_module_funcs);
 }
diff --git a/gjs/byteArray.h b/gjs/byteArray.h
index d16c0411..2b0626b0 100644
--- a/gjs/byteArray.h
+++ b/gjs/byteArray.h
@@ -30,26 +30,17 @@
 
 G_BEGIN_DECLS
 
-bool        gjs_typecheck_bytearray(JSContext       *context,
-                                    JS::HandleObject obj,
-                                    bool             throw_error);
-
 bool gjs_define_byte_array_stuff(JSContext              *context,
                                  JS::MutableHandleObject module);
 
+JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data);
+
 JSObject *    gjs_byte_array_from_byte_array (JSContext  *context,
                                               GByteArray *array);
 
-GByteArray *gjs_byte_array_get_byte_array(JSContext       *context,
-                                          JS::HandleObject object);
-
-GBytes     *gjs_byte_array_get_bytes(JSContext       *context,
-                                     JS::HandleObject object);
+GByteArray* gjs_byte_array_get_byte_array(JS::HandleObject obj);
 
-void        gjs_byte_array_peek_data(JSContext       *context,
-                                     JS::HandleObject object,
-                                     guint8         **out_data,
-                                     gsize           *out_len);
+GBytes* gjs_byte_array_get_bytes(JS::HandleObject obj);
 
 G_END_DECLS
 
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 5c4871d7..b29447df 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -320,7 +320,7 @@ gjs_context_class_init(GjsContextClass *klass)
     g_free (priv_typelib_dir);
     }
 
-    gjs_register_native_module("byteArray", gjs_define_byte_array_stuff);
+    gjs_register_native_module("_byteArrayNative", gjs_define_byte_array_stuff);
     gjs_register_native_module("_gi", gjs_define_private_gi_stuff);
     gjs_register_native_module("gi", gjs_define_repo);
 
@@ -529,11 +529,6 @@ gjs_context_constructed(GObject *object)
         g_error("Failed to define properties on global object");
     }
 
-    /* Pre-import the byteArray module. We depend on this module for some of
-     * our GObject introspection marshalling, so the ByteArray prototype
-     * defined in it needs to be always available. */
-    gjs_import_native_module(cx, importer, "byteArray");
-
     JS_EndRequest(cx);
 
     g_mutex_lock (&contexts_lock);
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index 2c9d2d18..72cd2160 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -479,7 +479,7 @@ do_import(JSContext       *context,
     JS::RootedValue elem(context);
     JS::RootedString str(context);
 
-    /* First try importing an internal module like byteArray */
+    /* First try importing an internal module like gi */
     if (priv->is_root && gjs_is_registered_native_module(context, obj, name)) {
         if (!gjs_import_native_module(context, obj, name))
             return false;
diff --git a/installed-tests/js/testByteArray.js b/installed-tests/js/testByteArray.js
index 57ec948b..1636505b 100644
--- a/installed-tests/js/testByteArray.js
+++ b/installed-tests/js/testByteArray.js
@@ -1,93 +1,6 @@
 const ByteArray = imports.byteArray;
 
 describe('Byte array', function () {
-    it('has length 0 for empty array', function () {
-        let a = new ByteArray.ByteArray();
-        expect(a.length).toEqual(0);
-    });
-
-    describe('initially sized to 10', function () {
-        let a;
-        beforeEach(function () {
-            a = new ByteArray.ByteArray(10);
-        });
-
-        it('has length 10', function () {
-            expect(a.length).toEqual(10);
-        });
-
-        it('is initialized to zeroes', function () {
-            for (let i = 0; i < a.length; ++i) {
-                expect(a[i]).toEqual(0);
-            }
-        });
-    });
-
-    it('assigns values correctly', function () {
-        let a = new ByteArray.ByteArray(256);
-
-        for (let i = 0; i < a.length; ++i) {
-            a[i] = 255 - i;
-        }
-
-        for (let i = 0; i < a.length; ++i) {
-            expect(a[i]).toEqual(255 - i);
-        }
-    });
-
-    describe('assignment past end', function () {
-        let a;
-        beforeEach(function () {
-            a = new ByteArray.ByteArray();
-            a[2] = 5;
-        });
-
-        it('implicitly lengthens the array', function () {
-            expect(a.length).toEqual(3);
-            expect(a[2]).toEqual(5);
-        });
-
-        it('implicitly creates zero bytes', function () {
-            expect(a[0]).toEqual(0);
-            expect(a[1]).toEqual(0);
-        });
-    });
-
-    it('changes the length when assigning to length property', function () {
-        let a = new ByteArray.ByteArray(20);
-        expect(a.length).toEqual(20);
-        a.length = 5;
-        expect(a.length).toEqual(5);
-    });
-
-    describe('conversions', function () {
-        let a;
-        beforeEach(function () {
-            a = new ByteArray.ByteArray();
-            a[0] = 255;
-        });
-
-        it('gives a byte 5 when assigning 5', function () {
-            a[0] = 5;
-            expect(a[0]).toEqual(5);
-        });
-
-        it('gives a byte 0 when assigning null', function () {
-            a[0] = null;
-            expect(a[0]).toEqual(0);
-        });
-
-        it('gives a byte 0 when assigning undefined', function () {
-            a[0] = undefined;
-            expect(a[0]).toEqual(0);
-        });
-
-        it('rounds off when assigning a double', function () {
-            a[0] = 3.14;
-            expect(a[0]).toEqual(3);
-        });
-    });
-
     it('can be created from a string', function () {
         let a = ByteArray.fromString('abcd');
         expect(a.length).toEqual(4);
@@ -118,13 +31,13 @@ describe('Byte array', function () {
         [1, 2, 3, 4].forEach((val, ix) => expect(a[ix]).toEqual(val));
     });
 
-    it('can be converted to a string of ASCII characters', function () {
-        let a = new ByteArray.ByteArray();
+    it('can be converted to a string of ASCII characters', function() {
+        let a = new Uint8Array(4);
         a[0] = 97;
         a[1] = 98;
         a[2] = 99;
         a[3] = 100;
-        let s = a.toString();
+        let s = ByteArray.toString(a);
         expect(s.length).toEqual(4);
         expect(s).toEqual('abcd');
     });
diff --git a/installed-tests/js/testGIMarshalling.js b/installed-tests/js/testGIMarshalling.js
index b7565c54..ed7a5b5d 100644
--- a/installed-tests/js/testGIMarshalling.js
+++ b/installed-tests/js/testGIMarshalling.js
@@ -214,8 +214,8 @@ describe('GArray', function () {
     });
 });
 
-describe('GByteArray', function () {
-    const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]);
+describe('GByteArray', function() {
+    const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]);
 
     it('can be passed in with transfer none', function () {
         expect(() => GIMarshallingTests.bytearray_none_in(refByteArray))
@@ -232,8 +232,8 @@ describe('GByteArray', function () {
     });
 });
 
-describe('GBytes', function () {
-    const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]);
+describe('GBytes', function() {
+    const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]);
 
     it('can be created from an array and passed in', function () {
         let bytes = GLib.Bytes.new([0, 49, 0xFF, 51]);
@@ -265,7 +265,7 @@ describe('GBytes', function () {
         expect(array[1]).toEqual(42);
         array[1] = 49;  // Flip the value back
         // Now convert back to GBytes
-        expect(() => GIMarshallingTests.gbytes_none_in(array.toGBytes()))
+        expect(() => GIMarshallingTests.gbytes_none_in(ByteArray.toGBytes(array)))
             .not.toThrow();
     });
 
diff --git a/modules/byteArray.js b/modules/byteArray.js
new file mode 100644
index 00000000..9c04528a
--- /dev/null
+++ b/modules/byteArray.js
@@ -0,0 +1,19 @@
+/* exported ByteArray, fromArray, fromGBytes, fromString, toGBytes, toString */
+
+var {fromGBytes, fromString, toGBytes, toString} = imports._byteArrayNative;
+
+// For backwards compatibility
+
+function fromArray(a) {
+    return Uint8Array.from(a);
+}
+
+var ByteArray = Uint8Array;
+
+Uint8Array.prototype.toString = function(encoding = 'UTF-8') {
+    return toString(this, encoding);
+};
+
+Uint8Array.prototype.toGBytes = function() {
+    return toGBytes(this);
+};
diff --git a/modules/modules.gresource.xml b/modules/modules.gresource.xml
index 85b70c5c..ed5138f9 100644
--- a/modules/modules.gresource.xml
+++ b/modules/modules.gresource.xml
@@ -15,6 +15,7 @@
     <file>modules/overrides/GObject.js</file>
     <file>modules/overrides/Gtk.js</file>
 
+    <file>modules/byteArray.js</file>
     <file>modules/cairo.js</file>
     <file>modules/gettext.js</file>
     <file>modules/lang.js</file>
diff --git a/modules/overrides/GLib.js b/modules/overrides/GLib.js
index f361bbe9..2e1d0c72 100644
--- a/modules/overrides/GLib.js
+++ b/modules/overrides/GLib.js
@@ -18,8 +18,6 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-const ByteArray = imports.byteArray;
-
 let GLib;
 
 const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g'];
@@ -67,7 +65,7 @@ function _read_single_type(signature, forceSimple) {
 }
 
 function _makeBytes(byteArray) {
-    if (byteArray instanceof ByteArray.ByteArray)
+    if (byteArray instanceof Uint8Array)
         return byteArray.toGBytes();
     else
         return new GLib.Bytes(byteArray);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]