StructuredClone algorithm should be aware of BigInt
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Apr 2020 09:54:34 +0000 (09:54 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Apr 2020 09:54:34 +0000 (09:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=210728

Reviewed by Mark Lam.

LayoutTests/imported/w3c:

* web-platform-tests/IndexedDB/bigint_value-expected.txt:
* web-platform-tests/html/infrastructure/safe-passing-of-structured-data/structured_clone_bigint-expected.txt:

Source/JavaScriptCore:

* CMakeLists.txt:
* runtime/BigIntObject.h:
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::digit): Deleted.
(JSC::JSBigInt::setDigit): Deleted.
* runtime/JSBigInt.h:
(JSC::JSBigInt::digit):
(JSC::JSBigInt::setDigit):

Source/WebCore:

This patch adds structured-cloning for BigInt and BigIntObject.
The logic is adding BigIntTag & BigIntObjectTag. And then we put content of BigInt with length.
And deserialization reads them to reconstruct BigInt or BigIntObject.

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneSerializer::dumpImmediate):
(WebCore::CloneSerializer::dumpBigIntData):
(WebCore::CloneSerializer::dumpBigInt32Data):
(WebCore::CloneSerializer::dumpHeapBigIntData):
(WebCore::CloneSerializer::dumpIfTerminal):
(WebCore::CloneDeserializer::readBigInt):
(WebCore::CloneDeserializer::readTerminal):

LayoutTests:

* inspector/model/remote-object/number-expected.txt:
* inspector/model/remote-object/object-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260358 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
LayoutTests/ChangeLog
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/IndexedDB/bigint_value-expected.txt
LayoutTests/imported/w3c/web-platform-tests/html/infrastructure/safe-passing-of-structured-data/structured_clone_bigint-expected.txt
LayoutTests/inspector/model/remote-object/number-expected.txt
LayoutTests/inspector/model/remote-object/object-expected.txt
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/BigIntObject.h
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp

index ecabf4a..1611d44 100644 (file)
@@ -1,3 +1,13 @@
+2020-04-20  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        StructuredClone algorithm should be aware of BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=210728
+
+        Reviewed by Mark Lam.
+
+        * inspector/model/remote-object/number-expected.txt:
+        * inspector/model/remote-object/object-expected.txt:
+
 2020-04-20  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK][WPE] Enable resource load statistics
index 0af30f9..18765e7 100644 (file)
@@ -1,3 +1,13 @@
+2020-04-20  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        StructuredClone algorithm should be aware of BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=210728
+
+        Reviewed by Mark Lam.
+
+        * web-platform-tests/IndexedDB/bigint_value-expected.txt:
+        * web-platform-tests/html/infrastructure/safe-passing-of-structured-data/structured_clone_bigint-expected.txt:
+
 2020-04-19  Emilio Cobos Ãlvarez  <emilio@crisal.io>
 
         Don't use the inherited custom properties to store environment variables.
index e317aaa..c698a42 100644 (file)
@@ -1,5 +1,8 @@
-CONSOLE MESSAGE: SyntaxError: No identifiers allowed directly after numeric literal
-
-Harness Error (FAIL), message = SyntaxError: No identifiers allowed directly after numeric literal
 
+PASS BigInts as values in IndexedDB - primitive BigInt 
+PASS BigInts as values in IndexedDB - BigInt object 
+PASS BigInts as values in IndexedDB - primitive BigInt inside object 
+PASS BigInts as values in IndexedDB - BigInt object inside object 
+PASS BigInts as keys in IndexedDB - primitive BigInt 
+PASS BigInts as keys in IndexedDB - BigInt object 
 
index ab6fa65..7fb9b55 100644 (file)
@@ -100,6 +100,6 @@ EXPRESSION: 123n
 {
   "_type": "bigint",
   "_description": "123n",
-  "_value": "123n [BigInt Not Enabled in Web Inspector]"
+  "_value": "<filtered 123n>"
 }
 
index 80770c0..db00d83 100644 (file)
@@ -41,9 +41,37 @@ EXPRESSION: ({a: 1})
 EXPRESSION: ({a: 1, b: 0, c: -0, d: 456n})
 {
   "_type": "object",
-  "_subtype": "error",
   "_objectId": "<filtered>",
-  "_description": "SyntaxError: No identifiers allowed directly after numeric literal"
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "b",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "c",
+        "_type": "number",
+        "_value": "-0"
+      },
+      {
+        "_name": "d",
+        "_type": "bigint",
+        "_value": "456n"
+      }
+    ],
+    "_entries": null
+  }
 }
 
 -----------------------------------------------------
index afb2547..22bf1a3 100644 (file)
@@ -789,6 +789,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/AuxiliaryBarrierInlines.h
     runtime/BasicBlockLocation.h
     runtime/BatchedTransitionOptimizer.h
+    runtime/BigIntObject.h
     runtime/BigIntPrototype.h
     runtime/BooleanObject.h
     runtime/BooleanPrototype.h
index d7682fc..fe86818 100644 (file)
@@ -1,3 +1,19 @@
+2020-04-20  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        StructuredClone algorithm should be aware of BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=210728
+
+        Reviewed by Mark Lam.
+
+        * CMakeLists.txt:
+        * runtime/BigIntObject.h:
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::digit): Deleted.
+        (JSC::JSBigInt::setDigit): Deleted.
+        * runtime/JSBigInt.h:
+        (JSC::JSBigInt::digit):
+        (JSC::JSBigInt::setDigit):
+
 2020-04-19  Ross Kirsling  <ross.kirsling@sony.com>
 
         [ECMA-402] Intl.RelativeTimeFormat missing in WebKit
index dea81d2..deccfde 100644 (file)
@@ -40,7 +40,7 @@ public:
         return vm.bigIntObjectSpace<mode>();
     }
 
-    static BigIntObject* create(VM&, JSGlobalObject*, JSValue);
+    JS_EXPORT_PRIVATE static BigIntObject* create(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_EXPORT_INFO;
 
index 1c239cb..d399231 100644 (file)
@@ -1936,18 +1936,6 @@ JSValue JSBigInt::parseInt(JSGlobalObject* globalObject, VM& vm, CharType* data,
     return heapResult->rightTrim(vm);
 }
 
-inline JSBigInt::Digit JSBigInt::digit(unsigned n)
-{
-    ASSERT(n < length());
-    return dataStorage()[n];
-}
-
-inline void JSBigInt::setDigit(unsigned n, Digit value)
-{
-    ASSERT(n < length());
-    dataStorage()[n] = value;
-}
-
 JSObject* JSBigInt::toObject(JSGlobalObject* globalObject) const
 {
     return BigIntObject::create(globalObject->vm(), globalObject, const_cast<JSBigInt*>(this));
index 8e2c148..7dfbbff 100644 (file)
@@ -60,7 +60,7 @@ public:
 
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
     static JSBigInt* createZero(VM&);
-    static JSBigInt* tryCreateWithLength(JSGlobalObject*, unsigned length);
+    JS_EXPORT_PRIVATE static JSBigInt* tryCreateWithLength(JSGlobalObject*, unsigned length);
     static JSBigInt* createWithLengthUnchecked(VM&, unsigned length);
 
     static JSBigInt* createFrom(VM&, int32_t value);
@@ -154,6 +154,9 @@ public:
     static JSBigInt* leftShift(JSGlobalObject*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* signedRightShift(JSGlobalObject*, JSBigInt* x, JSBigInt* y);
 
+    Digit digit(unsigned);
+    void setDigit(unsigned, Digit); // Use only when initializing.
+
 private:
     JSBigInt(VM&, Structure*, Digit*, unsigned length);
 
@@ -261,9 +264,6 @@ private:
 
     inline Digit* dataStorage() { return m_data.get(m_length); }
 
-    Digit digit(unsigned);
-    void setDigit(unsigned, Digit);
-
     const unsigned m_length;
     bool m_sign { false };
     CagedUniquePtr<Gigacage::Primitive, Digit> m_data;
@@ -275,4 +275,16 @@ inline JSBigInt* asHeapBigInt(JSValue value)
     return jsCast<JSBigInt*>(value.asCell());
 }
 
+inline JSBigInt::Digit JSBigInt::digit(unsigned n)
+{
+    ASSERT(n < length());
+    return dataStorage()[n];
+}
+
+inline void JSBigInt::setDigit(unsigned n, Digit value)
+{
+    ASSERT(n < length());
+    dataStorage()[n] = value;
+}
+
 } // namespace JSC
index a6eeff4..c6b6f91 100644 (file)
@@ -1,3 +1,23 @@
+2020-04-20  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        StructuredClone algorithm should be aware of BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=210728
+
+        Reviewed by Mark Lam.
+
+        This patch adds structured-cloning for BigInt and BigIntObject.
+        The logic is adding BigIntTag & BigIntObjectTag. And then we put content of BigInt with length.
+        And deserialization reads them to reconstruct BigInt or BigIntObject.
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CloneSerializer::dumpImmediate):
+        (WebCore::CloneSerializer::dumpBigIntData):
+        (WebCore::CloneSerializer::dumpBigInt32Data):
+        (WebCore::CloneSerializer::dumpHeapBigIntData):
+        (WebCore::CloneSerializer::dumpIfTerminal):
+        (WebCore::CloneDeserializer::readBigInt):
+        (WebCore::CloneDeserializer::readTerminal):
+
 2020-04-20  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK][WPE] Enable resource load statistics
index 12c76cf..bc3862e 100644 (file)
@@ -56,6 +56,7 @@
 #include "SharedBuffer.h"
 #include "WebCoreJSClientData.h"
 #include <JavaScriptCore/APICast.h>
+#include <JavaScriptCore/BigIntObject.h>
 #include <JavaScriptCore/BooleanObject.h>
 #include <JavaScriptCore/CatchScope.h>
 #include <JavaScriptCore/DateInstance.h>
@@ -177,6 +178,8 @@ enum SerializationTag {
 #if ENABLE(OFFSCREEN_CANVAS)
     OffscreenCanvasTransferTag = 46,
 #endif
+    BigIntTag = 47,
+    BigIntObjectTag = 48,
     ErrorTag = 255
 };
 
@@ -343,6 +346,7 @@ static const unsigned StringDataIs8BitFlag = 0x80000000;
  *    | String
  *    | EmptyStringTag
  *    | EmptyStringObjectTag
+ *    | BigInt
  *    | File
  *    | FileList
  *    | ImageData
@@ -383,6 +387,13 @@ static const unsigned StringDataIs8BitFlag = 0x80000000;
  *      StringPoolTag <cpIndex:IndexType>
  *      (not (TerminatorTag | StringPoolTag))<is8Bit:uint32_t:1><length:uint32_t:31><characters:CharType{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
  *
+ * BigInt :-
+ *      BigIntTag BigIntData
+ *      BigIntObjectTag BigIntData
+ *
+ * BigIntData :-
+ *      <sign:uint8_t> <lengthInUint64:uint32_t> <contents:uint64_t{lengthInUint64}>
+ *
  * File :-
  *    FileTag FileData
  *
@@ -744,11 +755,15 @@ private:
 
     void dumpImmediate(JSValue value)
     {
-        if (value.isNull())
+        if (value.isNull()) {
             write(NullTag);
-        else if (value.isUndefined())
+            return;
+        }
+        if (value.isUndefined()) {
             write(UndefinedTag);
-        else if (value.isNumber()) {
+            return;
+        }
+        if (value.isNumber()) {
             if (value.isInt32()) {
                 if (!value.asInt32())
                     write(ZeroTag);
@@ -762,12 +777,22 @@ private:
                 write(DoubleTag);
                 write(value.asDouble());
             }
-        } else if (value.isBoolean()) {
+            return;
+        }
+        if (value.isBoolean()) {
             if (value.isTrue())
                 write(TrueTag);
             else
                 write(FalseTag);
+            return;
         }
+#if USE(BIGINT32)
+        if (value.isBigInt32()) {
+            write(BigIntTag);
+            dumpBigIntData(value);
+            return;
+        }
+#endif
     }
 
     void dumpString(const String& string)
@@ -790,6 +815,60 @@ private:
         }
     }
 
+    void dumpBigIntData(JSValue value)
+    {
+        ASSERT(value.isBigInt());
+#if USE(BIGINT32)
+        if (value.isBigInt32()) {
+            dumpBigInt32Data(value.bigInt32AsInt32());
+            return;
+        }
+#endif
+        dumpHeapBigIntData(jsCast<JSBigInt*>(value));
+    }
+
+#if USE(BIGINT32)
+    void dumpBigInt32Data(int32_t integer)
+    {
+        static_assert(sizeof(uint64_t) == sizeof(unsigned long long));
+        write(static_cast<uint8_t>(integer < 0));
+        write(static_cast<uint32_t>(1)); // Length-in-uint64_t
+        int64_t value = static_cast<int64_t>(integer);
+        if (value < 0)
+            value = -value;
+        write(static_cast<unsigned long long>(value));
+    }
+#endif
+
+    void dumpHeapBigIntData(JSBigInt* bigInt)
+    {
+        static_assert(sizeof(uint64_t) == sizeof(unsigned long long));
+        write(static_cast<uint8_t>(bigInt->sign()));
+        if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
+            write(static_cast<uint32_t>(bigInt->length()));
+            for (unsigned index = 0; index < bigInt->length(); ++index)
+                write(static_cast<unsigned long long>(bigInt->digit(index)));
+        } else {
+            ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
+            uint32_t lengthInUint64 = bigInt->length() / 2;
+            if (bigInt->length() & 0x1)
+                ++lengthInUint64;
+            write(lengthInUint64);
+            uint64_t value = 0;
+            for (unsigned index = 0; index < bigInt->length(); ++index) {
+                if (!(index & 0x1))
+                    value = bigInt->digit(index);
+                else {
+                    value = (static_cast<uint64_t>(bigInt->digit(index)) << 32) | value;
+                    write(static_cast<unsigned long long>(value));
+                    value = 0;
+                }
+            }
+            if (bigInt->length() & 0x1)
+                write(static_cast<unsigned long long>(value));
+        }
+    }
+
     JSC::JSValue toJSArrayBuffer(ArrayBuffer& arrayBuffer)
     {
         auto& vm = m_lexicalGlobalObject->vm();
@@ -992,6 +1071,12 @@ private:
             return true;
         }
 
+        if (value.isHeapBigInt()) {
+            write(BigIntTag);
+            dumpBigIntData(value);
+            return true;
+        }
+
         if (value.isSymbol()) {
             code = SerializationReturnCode::DataCloneError;
             return true;
@@ -1028,6 +1113,15 @@ private:
                 write(numberObject->internalValue().asNumber());
                 return true;
             }
+            if (auto* bigIntObject = jsDynamicCast<BigIntObject*>(vm, obj)) {
+                if (!startObjectInternal(bigIntObject)) // handle duplicates
+                    return true;
+                JSValue bigIntValue = bigIntObject->internalValue();
+                ASSERT(bigIntValue.isBigInt());
+                write(BigIntObjectTag);
+                dumpBigIntData(bigIntValue);
+                return true;
+            }
             if (auto* file = JSFile::toWrapped(vm, obj)) {
                 write(FileTag);
                 write(*file);
@@ -2893,6 +2987,75 @@ private:
         return getJSValue(bitmap);
     }
 
+    JSValue readBigInt()
+    {
+        uint8_t sign = 0;
+        if (!read(sign))
+            return JSValue();
+        uint32_t lengthInUint64 = 0;
+        if (!read(lengthInUint64))
+            return JSValue();
+#if USE(BIGINT32)
+        static_assert(sizeof(JSBigInt::Digit) == sizeof(uint64_t));
+        if (lengthInUint64 == 1) {
+            static_assert(sizeof(unsigned long long) == sizeof(uint64_t));
+            unsigned long long digit64 = 0;
+            if (!read(digit64))
+                return JSValue();
+            if (sign) {
+                if (digit64 <= static_cast<uint64_t>(-static_cast<int64_t>(INT32_MIN)))
+                    return JSValue(JSValue::JSBigInt32, static_cast<int32_t>(-static_cast<int64_t>(digit64)));
+            } else {
+                if (digit64 <= INT32_MAX)
+                    return JSValue(JSValue::JSBigInt32, static_cast<int32_t>(digit64));
+            }
+            ASSERT(digit64 != 0);
+            JSBigInt* bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject, 1);
+            if (!bigInt) {
+                fail();
+                return JSValue();
+            }
+            bigInt->setDigit(0, digit64);
+            bigInt->setSign(sign);
+            m_gcBuffer.appendWithCrashOnOverflow(bigInt);
+            return bigInt;
+        }
+#endif
+        JSBigInt* bigInt = nullptr;
+        if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
+            bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject, lengthInUint64);
+            if (!bigInt) {
+                fail();
+                return JSValue();
+            }
+            for (uint32_t index = 0; index < lengthInUint64; ++index) {
+                static_assert(sizeof(unsigned long long) == sizeof(uint64_t));
+                unsigned long long digit64 = 0;
+                if (!read(digit64))
+                    return JSValue();
+                bigInt->setDigit(index, digit64);
+            }
+        } else {
+            ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
+            bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject, lengthInUint64 * 2);
+            if (!bigInt) {
+                fail();
+                return JSValue();
+            }
+            for (uint32_t index = 0; index < lengthInUint64; ++index) {
+                static_assert(sizeof(unsigned long long) == sizeof(uint64_t));
+                unsigned long long digit64 = 0;
+                if (!read(digit64))
+                    return JSValue();
+                bigInt->setDigit(index * 2, static_cast<uint32_t>(digit64));
+                bigInt->setDigit(index * 2 + 1, static_cast<uint32_t>(digit64 >> 32));
+            }
+        }
+        bigInt->setSign(sign);
+        m_gcBuffer.appendWithCrashOnOverflow(bigInt);
+        return bigInt;
+    }
+
     JSValue readTerminal()
     {
         SerializationTag tag = readTag();
@@ -2933,6 +3096,8 @@ private:
                 return JSValue();
             return jsNumber(d);
         }
+        case BigIntTag:
+            return readBigInt();
         case NumberObjectTag: {
             double d;
             if (!read(d))
@@ -2941,6 +3106,15 @@ private:
             m_gcBuffer.appendWithCrashOnOverflow(obj);
             return obj;
         }
+        case BigIntObjectTag: {
+            JSValue bigInt = readBigInt();
+            if (!bigInt)
+                return JSValue();
+            ASSERT(bigInt.isBigInt());
+            BigIntObject* obj = BigIntObject::create(m_lexicalGlobalObject->vm(), m_globalObject, bigInt);
+            m_gcBuffer.appendWithCrashOnOverflow(obj);
+            return obj;
+        }
         case DateTag: {
             double d;
             if (!read(d))