Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/jsapi-tests/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 13 kB image not shown  

Quelle  testArrayBuffer.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 */


#include "builtin/TestingFunctions.h"
#include "js/Array.h"        // JS::NewArrayObject
#include "js/ArrayBuffer.h"  // JS::{GetArrayBuffer{ByteLength,Data},IsArrayBufferObject,NewArrayBuffer{,WithContents},StealArrayBufferContents}
#include "js/ArrayBufferMaybeShared.h"
#include "js/CallAndConstruct.h"
#include "js/Exception.h"
#include "js/experimental/TypedData.h"  // JS_New{Int32,Uint8}ArrayWithBuffer
#include "js/friend/ErrorMessages.h"    // JSMSG_*
#include "js/MemoryFunctions.h"
#include "js/PropertyAndElement.h"  // JS_GetElement, JS_GetProperty, JS_SetElement
#include "js/Realm.h"
#include "jsapi-tests/tests.h"

#include "vm/Realm-inl.h"

BEGIN_TEST(testArrayBuffer_bug720949_steal) {
  static const unsigned NUM_TEST_BUFFERS = 2;
  static const unsigned MAGIC_VALUE_1 = 3;
  static const unsigned MAGIC_VALUE_2 = 17;

  JS::RootedObject buf_len1(cx), buf_len200(cx);
  JS::RootedObject tarray_len1(cx), tarray_len200(cx);

  uint32_t sizes[NUM_TEST_BUFFERS] = {sizeof(uint32_t), 200 * sizeof(uint32_t)};
  JS::HandleObject testBuf[NUM_TEST_BUFFERS] = {buf_len1, buf_len200};
  JS::HandleObject testArray[NUM_TEST_BUFFERS] = {tarray_len1, tarray_len200};

  // Single-element ArrayBuffer (uses fixed slots for storage)
  CHECK(buf_len1 = JS::NewArrayBuffer(cx, sizes[0]));
  CHECK(tarray_len1 = JS_NewInt32ArrayWithBuffer(cx, testBuf[0], 0, -1));

  CHECK(JS_SetElement(cx, testArray[0], 0, MAGIC_VALUE_1));

  // Many-element ArrayBuffer (uses dynamic storage)
  CHECK(buf_len200 = JS::NewArrayBuffer(cx, 200 * sizeof(uint32_t)));
  CHECK(tarray_len200 = JS_NewInt32ArrayWithBuffer(cx, testBuf[1], 0, -1));

  for (unsigned i = 0; i < NUM_TEST_BUFFERS; i++) {
    JS::HandleObject obj = testBuf[i];
    JS::HandleObject view = testArray[i];
    uint32_t size = sizes[i];
    JS::RootedValue v(cx);

    // Byte lengths should all agree
    CHECK(JS::IsArrayBufferObject(obj));
    CHECK_EQUAL(JS::GetArrayBufferByteLength(obj), size);
    CHECK(JS_GetProperty(cx, obj, "byteLength", &v));
    CHECK(v.isInt32(size));
    CHECK(JS_GetProperty(cx, view, "byteLength", &v));
    CHECK(v.isInt32(size));

    // Modifying the underlying data should update the value returned through
    // the view
    {
      JS::AutoCheckCannotGC nogc;
      bool sharedDummy;
      uint8_t* data = JS::GetArrayBufferData(obj, &sharedDummy, nogc);
      CHECK(data != nullptr);
      *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
    }
    CHECK(JS_GetElement(cx, view, 0, &v));
    CHECK(v.isInt32(MAGIC_VALUE_2));

    // Steal the contents
    mozilla::UniquePtr<void, JS::FreePolicy> contents{
        JS::StealArrayBufferContents(cx, obj)};
    CHECK(contents != nullptr);

    CHECK(JS::IsDetachedArrayBufferObject(obj));

    // Transfer to a new ArrayBuffer
    JS::RootedObject dst(
        cx, JS::NewArrayBufferWithContents(cx, size, std::move(contents)));
    CHECK(JS::IsArrayBufferObject(dst));
    {
      JS::AutoCheckCannotGC nogc;
      bool sharedDummy;
      (void)JS::GetArrayBufferData(obj, &sharedDummy, nogc);
    }

    JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1));
    CHECK(dstview != nullptr);

    CHECK_EQUAL(JS::GetArrayBufferByteLength(dst), size);
    {
      JS::AutoCheckCannotGC nogc;
      bool sharedDummy;
      uint8_t* data = JS::GetArrayBufferData(dst, &sharedDummy, nogc);
      CHECK(data != nullptr);
      CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2);
    }
    CHECK(JS_GetElement(cx, dstview, 0, &v));
    CHECK(v.isInt32(MAGIC_VALUE_2));
  }

  return true;
}
END_TEST(testArrayBuffer_bug720949_steal)

// Varying number of views of a buffer, to test the detachment weak pointers
BEGIN_TEST(testArrayBuffer_bug720949_viewList) {
  JS::RootedObject buffer(cx);

  // No views
  buffer = JS::NewArrayBuffer(cx, 2000);
  buffer = nullptr;
  GC(cx);

  // One view.
  {
    buffer = JS::NewArrayBuffer(cx, 2000);
    JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
    void* contents = JS::StealArrayBufferContents(cx, buffer);
    CHECK(contents != nullptr);
    JS_free(nullptr, contents);
    GC(cx);
    CHECK(hasDetachedBuffer(view));
    CHECK(JS::IsDetachedArrayBufferObject(buffer));
    view = nullptr;
    GC(cx);
    buffer = nullptr;
    GC(cx);
  }

  // Two views
  {
    buffer = JS::NewArrayBuffer(cx, 2000);

    JS::RootedObject view1(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
    JS::RootedObject view2(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200));

    // Remove, re-add a view
    view2 = nullptr;
    GC(cx);
    view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200);

    // Detach
    void* contents = JS::StealArrayBufferContents(cx, buffer);
    CHECK(contents != nullptr);
    JS_free(nullptr, contents);

    CHECK(hasDetachedBuffer(view1));
    CHECK(hasDetachedBuffer(view2));
    CHECK(JS::IsDetachedArrayBufferObject(buffer));

    view1 = nullptr;
    GC(cx);
    view2 = nullptr;
    GC(cx);
    buffer = nullptr;
    GC(cx);
  }

  return true;
}

static void GC(JSContext* cx) {
  JS_GC(cx);
  JS_GC(cx);  // Trigger another to wait for background finalization to end
}

bool hasDetachedBuffer(JS::HandleObject obj) {
  JS::RootedValue v(cx);
  return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0;
}

END_TEST(testArrayBuffer_bug720949_viewList)

BEGIN_TEST(testArrayBuffer_customFreeFunc) {
  ExternalData data("One two three four");
  auto dataPointer = data.pointer();

  // The buffer takes ownership of the data.
  JS::RootedObject buffer(
      cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
  CHECK(buffer);
  CHECK(!data.wasFreed());

  size_t len;
  bool isShared;
  uint8_t* bufferData;
  JS::GetArrayBufferLengthAndData(buffer, &len, &isShared, &bufferData);
  CHECK_EQUAL(len, data.len());
  CHECK(bufferData == data.contents());
  CHECK(strcmp(reinterpret_cast<char*>(bufferData), data.asString()) == 0);

  buffer = nullptr;
  JS_GC(cx);
  JS_GC(cx);
  CHECK(data.wasFreed());

  return true;
}
END_TEST(testArrayBuffer_customFreeFunc)

BEGIN_TEST(testArrayBuffer_staticContents) {
  ExternalData data("One two three four");

  JS::RootedObject buffer(cx, JS::NewArrayBufferWithUserOwnedContents(
                                  cx, data.len(), data.contents()));
  CHECK(buffer);
  CHECK(!data.wasFreed());

  size_t len;
  bool isShared;
  uint8_t* bufferData;
  JS::GetArrayBufferLengthAndData(buffer, &len, &isShared, &bufferData);
  CHECK_EQUAL(len, data.len());
  CHECK(bufferData == data.contents());
  CHECK(strcmp(reinterpret_cast<char*>(bufferData), data.asString()) == 0);

  buffer = nullptr;
  JS_GC(cx);
  JS_GC(cx);
  CHECK(!data.wasFreed());

  data.free();
  return true;
}
END_TEST(testArrayBuffer_staticContents)

BEGIN_TEST(testArrayBuffer_stealDetachExternal) {
  static const char dataBytes[] = "One two three four";
  ExternalData data(dataBytes);
  auto dataPointer = data.pointer();
  JS::RootedObject buffer(
      cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
  CHECK(buffer);
  CHECK(!data.wasFreed());

  void* stolenContents = JS::StealArrayBufferContents(cx, buffer);

  // External buffers are stealable: the data is copied into freshly allocated
  // memory, and the buffer's data pointer is cleared (immediately freeing the
  // data) and the buffer is marked as detached.
  CHECK(stolenContents != data.contents());
  CHECK(strcmp(reinterpret_cast<char*>(stolenContents), dataBytes) == 0);
  CHECK(data.wasFreed());
  CHECK(JS::IsDetachedArrayBufferObject(buffer));

  JS_free(cx, stolenContents);
  return true;
}
END_TEST(testArrayBuffer_stealDetachExternal)

BEGIN_TEST(testArrayBuffer_serializeExternal) {
  JS::RootedValue serializeValue(cx);

  {
    JS::RootedFunction serialize(cx);
    serialize =
        JS_NewFunction(cx, js::testingFunc_serialize, 1, 0, "serialize");
    CHECK(serialize);

    serializeValue.setObject(*JS_GetFunctionObject(serialize));
  }

  ExternalData data("One two three four");
  auto dataPointer = data.pointer();
  JS::RootedObject externalBuffer(
      cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
  CHECK(externalBuffer);
  CHECK(!data.wasFreed());

  JS::RootedValue v(cx, JS::ObjectValue(*externalBuffer));
  JS::RootedObject transferMap(cx,
                               JS::NewArrayObject(cx, JS::HandleValueArray(v)));
  CHECK(transferMap);

  JS::RootedValueArray<2> args(cx);
  args[0].setObject(*externalBuffer);
  args[1].setObject(*transferMap);

  // serialize(externalBuffer, [externalBuffer]) should throw for an unhandled
  // BufferContents kind.
  CHECK(!JS::Call(cx, JS::UndefinedHandleValue, serializeValue,
                  JS::HandleValueArray(args), &v));

  JS::ExceptionStack exnStack(cx);
  CHECK(JS::StealPendingExceptionStack(cx, &exnStack));

  JS::ErrorReportBuilder report(cx);
  CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects));

  CHECK_EQUAL(report.report()->errorNumber,
              static_cast<unsigned int>(JSMSG_SC_NOT_TRANSFERABLE));

  // Data should have been left alone.
  CHECK(!data.wasFreed());

  v.setNull();
  transferMap = nullptr;
  args[0].setNull();
  args[1].setNull();
  externalBuffer = nullptr;

  JS_GC(cx);
  JS_GC(cx);
  CHECK(data.wasFreed());

  return true;
}
END_TEST(testArrayBuffer_serializeExternal)

BEGIN_TEST(testArrayBuffer_copyData) {
  ExternalData data1("One two three four");
  JS::RootedObject buffer1(cx, JS::NewArrayBufferWithUserOwnedContents(
                                   cx, data1.len(), data1.contents()));

  CHECK(buffer1);

  ExternalData data2("Six");
  JS::RootedObject buffer2(cx, JS::NewArrayBufferWithUserOwnedContents(
                                   cx, data2.len(), data2.contents()));

  CHECK(buffer2);

  // Check we can't copy from a larger to a smaller buffer.
  CHECK(!JS::ArrayBufferCopyData(cx, buffer2, 0, buffer1, 0, data1.len()));

  // Verify expected exception is thrown.
  {
    JS::ExceptionStack exnStack(cx);
    CHECK(JS::StealPendingExceptionStack(cx, &exnStack));

    JS::ErrorReportBuilder report(cx);
    CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects));

    CHECK_EQUAL(report.report()->errorNumber,
                static_cast<unsigned int>(JSMSG_ARRAYBUFFER_COPY_RANGE));
  }

  CHECK(JS::ArrayBufferCopyData(
      cx, buffer1, 0, buffer2, 0,
      data2.len() - 1 /* don't copy null terminator */));

  {
    size_t len;
    bool isShared;
    uint8_t* bufferData;
    JS::GetArrayBufferLengthAndData(buffer1, &len, &isShared, &bufferData);

    ExternalData expected1("Six two three four");

    fprintf(stderr, "expected %s actual %s\n", expected1.asString(),
            bufferData);

    CHECK_EQUAL(len, expected1.len());
    CHECK_EQUAL(memcmp(expected1.contents(), bufferData, expected1.len()), 0);
  }

  return true;
}
END_TEST(testArrayBuffer_copyData)

BEGIN_TEST(testArrayBuffer_copyDataAcrossGlobals) {
  JS::RootedObject otherGlobal(cx, createGlobal(nullptr));
  if (!otherGlobal) {
    return false;
  }

  ExternalData data1("One two three four");
  JS::RootedObject buffer1(cx);
  {
    js::AutoRealm realm(cx, otherGlobal);
    buffer1 = JS::NewArrayBufferWithUserOwnedContents(cx, data1.len(),
                                                      data1.contents());
  }
  CHECK(buffer1);
  CHECK(JS_WrapObject(cx, &buffer1));

  ExternalData data2("Six");
  JS::RootedObject buffer2(cx, JS::NewArrayBufferWithUserOwnedContents(
                                   cx, data2.len(), data2.contents()));

  CHECK(buffer2);

  // Check we can't copy from a larger to a smaller buffer.
  CHECK(!JS::ArrayBufferCopyData(cx, buffer2, 0, buffer1, 0, data1.len()));

  // Verify expected exception is thrown.
  {
    JS::ExceptionStack exnStack(cx);
    CHECK(JS::StealPendingExceptionStack(cx, &exnStack));

    JS::ErrorReportBuilder report(cx);
    CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects));

    CHECK_EQUAL(report.report()->errorNumber,
                static_cast<unsigned int>(JSMSG_ARRAYBUFFER_COPY_RANGE));
  }

  CHECK(JS::ArrayBufferCopyData(
      cx, buffer1, 0, buffer2, 0,
      data2.len() - 1 /* don't copy null terminator */));

  {
    JS::RootedObject unwrappedBuffer1(
        cx, JS::UnwrapArrayBufferMaybeShared(buffer1));
    CHECK(unwrappedBuffer1);

    size_t len;
    bool isShared;
    uint8_t* bufferData;
    JS::GetArrayBufferLengthAndData(unwrappedBuffer1, &len, &isShared,
                                    &bufferData);

    ExternalData expected1("Six two three four");

    fprintf(stderr, "expected %s actual %s\n", expected1.asString(),
            bufferData);

    CHECK_EQUAL(len, expected1.len());
    CHECK_EQUAL(memcmp(expected1.contents(), bufferData, expected1.len()), 0);
  }

  return true;
}
END_TEST(testArrayBuffer_copyDataAcrossGlobals)

BEGIN_TEST(testArrayBuffer_ArrayBufferClone) {
  ExternalData data("One two three four");
  JS::RootedObject externalBuffer(cx, JS::NewArrayBufferWithUserOwnedContents(
                                          cx, data.len(), data.contents()));

  CHECK(externalBuffer);

  size_t lengthToCopy = 3;
  JS::RootedObject clonedBuffer(
      cx, JS::ArrayBufferClone(cx, externalBuffer, 4, lengthToCopy));
  CHECK(clonedBuffer);

  size_t len;
  bool isShared;
  uint8_t* bufferData;
  JS::GetArrayBufferLengthAndData(clonedBuffer, &len, &isShared, &bufferData);

  CHECK_EQUAL(len, lengthToCopy);

  ExternalData expectedData("two");
  CHECK_EQUAL(memcmp(expectedData.contents(), bufferData, len), 0);

  return true;
}
END_TEST(testArrayBuffer_ArrayBufferClone)

98%


¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.