| 19 | |
| 20 | |
| 21 | static bool array_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) { |
| 22 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
| 23 | |
| 24 | JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv())); |
| 25 | if (!proxy) { |
| 26 | return false; |
| 27 | } |
| 28 | |
| 29 | JS::PersistentRootedObject *arrayBuffer = JS::GetMaybePtrFromReservedSlot<JS::PersistentRootedObject>(proxy, OtherSlot); |
| 30 | JS::RootedObject rootedArrayBuffer(cx, arrayBuffer->get()); |
| 31 | |
| 32 | auto byteLength = JS::GetArrayBufferByteLength(rootedArrayBuffer); |
| 33 | |
| 34 | bool isSharedMemory; |
| 35 | JS::AutoCheckCannotGC autoNoGC(cx); |
| 36 | uint8_t *data = JS::GetArrayBufferData(rootedArrayBuffer, &isSharedMemory, autoNoGC); |
| 37 | |
| 38 | size_t numberOfDigits = 0; |
| 39 | for (size_t i = 0; i < byteLength; i++) { |
| 40 | numberOfDigits += data[i] < 10 ? 1 : data[i] < 100 ? 2 : 3; |
| 41 | } |
| 42 | |
| 43 | const size_t STRING_LENGTH = byteLength + numberOfDigits; |
| 44 | JS::Latin1Char *buffer = (JS::Latin1Char *)malloc(sizeof(JS::Latin1Char) * STRING_LENGTH); |
| 45 | |
| 46 | if (snprintf((char *)&buffer[0], 3 + 1, "%hu", data[0]) < 0) { |
| 47 | return false; |
| 48 | } |
| 49 | size_t charIndex = data[0] < 10 ? 1 : data[0] < 100 ? 2 : 3; |
| 50 | |
| 51 | for (size_t dataIndex = 1; dataIndex < byteLength; dataIndex++) { |
| 52 | buffer[charIndex] = ','; |
| 53 | charIndex++; |
| 54 | if (snprintf((char *)&buffer[charIndex], 3 + 1, "%hu", data[dataIndex]) < 0) { |
| 55 | return false; |
| 56 | } |
| 57 | charIndex += data[dataIndex] < 10 ? 1 : data[dataIndex] < 100 ? 2 : 3; |
| 58 | } |
| 59 | |
| 60 | JS::UniqueLatin1Chars str(buffer); |
| 61 | args.rval().setString(JS_NewLatin1String(cx, std::move(str), STRING_LENGTH - 1)); // don't include the null terminating byte |
| 62 | return true; |
| 63 | } |
| 64 | |
| 65 | static bool array_toString(JSContext *cx, unsigned argc, JS::Value *vp) { |
| 66 | return array_valueOf(cx, argc, vp); |