Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/wasm/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 23 kB image not shown  

Quelle  WasmGcObject.cpp   Sprache: C

 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "wasm/WasmGcObject-inl.h"

#include "mozilla/DebugOnly.h"

#include "gc/Tracer.h"
#include "js/CharacterEncoding.h"
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h"
#include "js/ScalarType.h"  // js::Scalar::Type
#include "js/Vector.h"
#include "util/StringBuilder.h"
#include "vm/GlobalObject.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"  // js::PlainObject
#include "vm/PropertyResult.h"
#include "vm/Realm.h"
#include "vm/SelfHosting.h"
#include "vm/StringType.h"
#include "vm/TypedArrayObject.h"
#include "vm/Uint8Clamped.h"

#include "gc/BufferAllocator-inl.h"
#include "gc/GCContext-inl.h"  // GCContext::removeCellMemory
#include "gc/ObjectKind-inl.h"
#include "vm/JSContext-inl.h"

using namespace js;
using namespace wasm;

// [SMDOC] Management of OOL storage areas for Wasm{Array,Struct}Object.
//
// WasmArrayObject always has its payload data stored in a block which is
// pointed to from the WasmArrayObject. The same is true for WasmStructObject in
// the case where the fields cannot fit in the object itself. These blocks are
// in some places referred to as "trailer blocks".
//
// These blocks are allocated in the same way as JSObects slots and element
// buffers, either using the GC's buffer allocator or directly in the nursery if
// they are small enough.
//
// They require the use of WasmArrayObject/WasmStructObject::obj_moved hooks to
// update the pointer and mark the allocation when the object gets tenured, and
// also update the pointer in case it is an internal pointer when the object is
// moved.
//
// The blocks are freed by the GC when no longer referenced.

//=========================================================================
// WasmGcObject

const ObjectOps WasmGcObject::objectOps_ = {
    WasmGcObject::obj_lookupProperty,            // lookupProperty
    WasmGcObject::obj_defineProperty,            // defineProperty
    WasmGcObject::obj_hasProperty,               // hasProperty
    WasmGcObject::obj_getProperty,               // getProperty
    WasmGcObject::obj_setProperty,               // setProperty
    WasmGcObject::obj_getOwnPropertyDescriptor,  // getOwnPropertyDescriptor
    WasmGcObject::obj_deleteProperty,            // deleteProperty
    nullptr,                                     // getElements
    nullptr,                                     // funToString
};

/* static */
bool WasmGcObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
                                      HandleId id, MutableHandleObject objp,
                                      PropertyResult* propp) {
  objp.set(nullptr);
  propp->setNotFound();
  return true;
}

bool WasmGcObject::obj_defineProperty(JSContext* cx, HandleObject obj,
                                      HandleId id,
                                      Handle<PropertyDescriptor> desc,
                                      ObjectOpResult& result) {
  result.failReadOnly();
  return true;
}

bool WasmGcObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
                                   bool* foundp) {
  *foundp = false;
  return true;
}

bool WasmGcObject::obj_getProperty(JSContext* cx, HandleObject obj,
                                   HandleValue receiver, HandleId id,
                                   MutableHandleValue vp) {
  vp.setUndefined();
  return true;
}

bool WasmGcObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
                                   HandleValue v, HandleValue receiver,
                                   ObjectOpResult& result) {
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                            JSMSG_WASM_MODIFIED_GC_OBJECT);
  return false;
}

bool WasmGcObject::obj_getOwnPropertyDescriptor(
    JSContext* cx, HandleObject obj, HandleId id,
    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
  desc.reset();
  return true;
}

bool WasmGcObject::obj_deleteProperty(JSContext* cx, HandleObject obj,
                                      HandleId id, ObjectOpResult& result) {
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                            JSMSG_WASM_MODIFIED_GC_OBJECT);
  return false;
}

bool WasmGcObject::lookUpProperty(JSContext* cx, Handle<WasmGcObject*> obj,
                                  jsid id, WasmGcObject::PropOffset* offset,
                                  StorageType* type) {
  switch (obj->kind()) {
    case wasm::TypeDefKind::Struct: {
      const auto& structType = obj->typeDef().structType();
      uint32_t index;
      if (!IdIsIndex(id, &index)) {
        return false;
      }
      MOZ_ASSERT(structType.fields_.length() ==
                 structType.fieldAccessPaths_.length());
      if (index >= structType.fields_.length()) {
        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                 JSMSG_WASM_OUT_OF_BOUNDS);
        return false;
      }
      offset->set(index);
      *type = structType.fields_[index].type;
      return true;
    }
    case wasm::TypeDefKind::Array: {
      const auto& arrayType = obj->typeDef().arrayType();

      uint32_t index;
      if (!IdIsIndex(id, &index)) {
        return false;
      }
      uint32_t numElements = obj->as<WasmArrayObject>().numElements_;
      if (index >= numElements) {
        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                 JSMSG_WASM_OUT_OF_BOUNDS);
        return false;
      }
      uint64_t scaledIndex =
          uint64_t(index) * uint64_t(arrayType.elementType().size());
      if (scaledIndex >= uint64_t(UINT32_MAX)) {
        // It's unrepresentable as an WasmGcObject::PropOffset. Give up.
        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                 JSMSG_WASM_OUT_OF_BOUNDS);
        return false;
      }
      offset->set(uint32_t(scaledIndex));
      *type = arrayType.elementType();
      return true;
    }
    default:
      MOZ_ASSERT_UNREACHABLE();
      return false;
  }
}

bool WasmGcObject::loadValue(JSContext* cx, Handle<WasmGcObject*> obj, jsid id,
                             MutableHandleValue vp) {
  WasmGcObject::PropOffset offset;
  StorageType type;
  if (!lookUpProperty(cx, obj, id, &offset, &type)) {
    return false;
  }

#ifdef ENABLE_WASM_JSPI
  // Temporary hack: We don't reject continuations in ValType::isExposable()
  // because that is also used on the JSPI-internal paths that legitimately move
  // continuations across the boundary. But we do want to generally disallow
  // them from JS otherwise.
  if (type.isTypeRef() &&
      type.refType().hierarchy() == RefTypeHierarchy::Cont) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                             JSMSG_WASM_BAD_VAL_TYPE);
    return false;
  }
#endif

  if (!type.isExposable()) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                             JSMSG_WASM_BAD_VAL_TYPE);
    return false;
  }

  if (obj->is<WasmStructObject>()) {
    // `offset` is the field index.
    WasmStructObject& structObj = obj->as<WasmStructObject>();
    MOZ_RELEASE_ASSERT(structObj.kind() == TypeDefKind::Struct);
    // The above call to `lookUpProperty` will reject a request for a struct
    // field whose index is out of range.  Hence the following will be safe
    // providing the FieldAccessPaths are correct.
    return ToJSValue(cx, structObj.fieldIndexToAddress(offset.get()), type, vp);
  }

  MOZ_ASSERT(obj->is<WasmArrayObject>());
  const WasmArrayObject& arrayObj = obj->as<WasmArrayObject>();
  return ToJSValue(cx, arrayObj.data_ + offset.get(), type, vp);
}

bool WasmGcObject::isRuntimeSubtypeOf(
    const wasm::TypeDef* parentTypeDef) const {
  return TypeDef::isSubTypeOf(&typeDef(), parentTypeDef);
}

bool WasmGcObject::obj_newEnumerate(JSContext* cx, HandleObject obj,
                                    MutableHandleIdVector properties,
                                    bool enumerableOnly) {
  return true;
}

static void WriteValTo(WasmGcObject* owner, const Val& val, StorageType ty,
                       void* dest) {
  switch (ty.kind()) {
    case StorageType::I8:
      *((uint8_t*)dest) = val.i32();
      break;
    case StorageType::I16:
      *((uint16_t*)dest) = val.i32();
      break;
    case StorageType::I32:
      *((uint32_t*)dest) = val.i32();
      break;
    case StorageType::I64:
      *((uint64_t*)dest) = val.i64();
      break;
    case StorageType::F32:
      *((float*)dest) = val.f32();
      break;
    case StorageType::F64:
      *((double*)dest) = val.f64();
      break;
    case StorageType::V128:
      *((V128*)dest) = val.v128();
      break;
    case StorageType::Ref:
      BarrieredSet(owner, dest, val.ref());
      break;
  }
}

//=========================================================================
// WasmArrayObject

/* static */
size_t js::WasmArrayObject::sizeOfExcludingThis() const {
  if (isDataInline()) {
    return 0;
  }
  OOLDataHeader* oolHeader = oolDataHeaderFromDataPointer(data_);
  if (!gc::IsBufferAlloc(oolHeader)) {
    return 0;
  }

  return gc::GetAllocSize(zone(), oolHeader);
}

/* static */
void WasmArrayObject::obj_trace(JSTracer* trc, JSObject* object) {
  WasmArrayObject& arrayObj = object->as<WasmArrayObject>();
  uint8_t* data = arrayObj.data_;

  // data_ may be null if the array was only partially initialized due to OOM
  // during createArrayOOL.
  if (!data) {
    MOZ_ASSERT(arrayObj.numElements_ == 0);
    return;
  }

  if (!arrayObj.isDataInline()) {
    OOLDataHeader* oolHeader = oolDataHeaderFromDataPointer(arrayObj.data_);
    OOLDataHeader* prior = oolHeader;
    TraceBufferEdge(trc, &oolHeader, "WasmArrayObject storage");
    if (oolHeader != prior) {
      arrayObj.data_ = oolDataHeaderToDataPointer(oolHeader);
    }
  }

  const auto& typeDef = arrayObj.typeDef();
  const auto& arrayType = typeDef.arrayType();
  if (!arrayType.elementType().isRefRepr()) {
    return;
  }

  uint32_t numElements = arrayObj.numElements_;
  uint32_t elemSize = arrayType.elementType().size();
  for (uint32_t i = 0; i < numElements; i++) {
    AnyRef* elementPtr = reinterpret_cast<AnyRef*>(data + i * elemSize);
    TraceManuallyBarrieredEdge(trc, elementPtr, "wasm-array-element");
  }
}

/* static */
size_t WasmArrayObject::obj_moved(JSObject* objNew, JSObject* objOld) {
  // This gets called for array objects, both with and without OOL areas.
  // Dealing with the no-OOL case is simple.  Thereafter, the logic for the OOL
  // case is essentially the same as for WasmStructObject::obj_moved, since
  // that routine is only used for WasmStructObjects that have OOL storage.
  MOZ_ASSERT(objNew != objOld);

  WasmArrayObject& arrayNew = objNew->as<WasmArrayObject>();
  WasmArrayObject& arrayOld = objOld->as<WasmArrayObject>();

  const TypeDef* typeDefNew = &arrayNew.typeDef();
  mozilla::DebugOnly<const TypeDef*> typeDefOld = &arrayOld.typeDef();
  MOZ_ASSERT(typeDefNew->isArrayType());
  MOZ_ASSERT(typeDefOld == typeDefNew);

  // At this point, the object has been copied, but the OOL storage area, if
  // any, has not been copied, nor has the data_ pointer been updated.  Hence:
  MOZ_ASSERT(arrayNew.data_ == arrayOld.data_);

  if (arrayOld.isDataInline()) {
    // The old array had inline storage, which has been copied.  Fix up the
    // data pointer in the new array to point to it, and we're done.
    arrayNew.data_ = WasmArrayObject::addressOfInlineArrayData(&arrayNew);
    MOZ_ASSERT(arrayNew.isDataInline());
    return 0;
  }

  // The array has OOL storage.  This means the logic that follows is similar
  // to that for WasmStructObject::obj_moved, since that routine is only used
  // for WasmStructObjects that have OOL storage.

  bool newIsInNursery = IsInsideNursery(objNew);
  bool oldIsInNursery = IsInsideNursery(objOld);

  // Tenured -> Tenured
  if (!oldIsInNursery && !newIsInNursery) {
    // The object already was in the tenured heap and has merely been moved
    // somewhere else in the the tenured heap.  This isn't interesting to us.
    return 0;
  }

  // Tenured -> Nursery: this transition isn't possible.
  MOZ_RELEASE_ASSERT(oldIsInNursery);

  // Nursery -> Nursery and Nursery -> Tenured
  // The object is being moved, either within the nursery or from the nursery
  // to the tenured heap.  Either way, we have to ask the nursery if it wants
  // to move the OOL block too, and if so set up a forwarding record for it.

  // arrayNew.numElements_ was validated not to overflow when constructing
  // the array.
  size_t oolBlockSize = calcArrayDataBytesUnchecked(
      typeDefNew->arrayType().elementType().size(), arrayNew.numElements_);
  // Ensured by WasmArrayObject::createArrayOOL.
  MOZ_RELEASE_ASSERT(oolBlockSize <= size_t(MaxArrayPayloadBytes));
  oolBlockSize += sizeof(WasmArrayObject::OOLDataHeader);

  // Ask the nursery if it wants to relocate the OOL block, and if so capture
  // its new location in `oolHeaderNew`.  Note, at this point `arrayNew.data_`
  // has not been updated; hence the computation for `oolHeaderOld` is correct.
  OOLDataHeader* oolHeaderOld = oolDataHeaderFromDataPointer(arrayNew.data_);
  OOLDataHeader* oolHeaderNew = oolHeaderOld;
  Nursery& nursery = objNew->runtimeFromMainThread()->gc.nursery();
  nursery.maybeMoveBufferOnPromotion(&oolHeaderNew, objNew, oolBlockSize);

  if (oolHeaderNew != oolHeaderOld) {
    // The OOL block has been moved.  Fix up the data pointer in the new
    // object.
    arrayNew.data_ = oolDataHeaderToDataPointer(oolHeaderNew);
    // Set up forwarding for the OOL block.  Use direct forwarding.  Write the
    // address of the new OOL block to OOLDataHeader::word in the old OOL
    // block.  This will be later used by Instance::updateFrameForMovingGC. See
    // SMDOC on definition of WasmArrayObject.
    //
    // Note, > rather than >=, because the OOL block must be big enough to hold
    // the data header plus at least one byte of array data.
    MOZ_RELEASE_ASSERT(oolBlockSize > sizeof(OOLDataHeader));
    if (nursery.isInside(oolHeaderOld)) {
      // Store the forwarding word, with bit 0 set.
      MOZ_ASSERT((uintptr_t(oolHeaderNew) & 1) == 0);
      oolHeaderOld->word = uintptr_t(oolHeaderNew) | 1;
      oolHeaderNew->word = WasmArrayObject::OOLDataHeader_Magic;
    }
  }

  return 0;
}

void WasmArrayObject::storeVal(const Val& val, uint32_t itemIndex) {
  const ArrayType& arrayType = typeDef().arrayType();
  size_t elementSize = arrayType.elementType().size();
  MOZ_ASSERT(itemIndex < numElements_);
  uint8_t* data = data_ + elementSize * itemIndex;
  WriteValTo(this, val, arrayType.elementType(), data);
}

void WasmArrayObject::fillVal(const Val& val, uint32_t itemIndex,
                              uint32_t len) {
  const ArrayType& arrayType = typeDef().arrayType();
  size_t elementSize = arrayType.elementType().size();
  uint8_t* data = data_ + elementSize * itemIndex;
  MOZ_ASSERT(itemIndex <= numElements_ && len <= numElements_ - itemIndex);
  for (uint32_t i = 0; i < len; i++) {
    WriteValTo(this, val, arrayType.elementType(), data);
    data += elementSize;
  }
}

static const JSClassOps WasmArrayObjectClassOps = {
    .newEnumerate = WasmGcObject::obj_newEnumerate,
    .trace = WasmArrayObject::obj_trace,
};
static const ClassExtension WasmArrayObjectClassExt = {
    WasmArrayObject::obj_moved, /* objectMovedOp */
};
const JSClass WasmArrayObject::class_ = {
    "WasmArrayObject",
    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    &WasmArrayObjectClassOps,
    JS_NULL_CLASS_SPEC,
    &WasmArrayObjectClassExt,
    &WasmGcObject::objectOps_,
};

//=========================================================================
// WasmStructObject

/* static */
size_t js::WasmStructObject::sizeOfExcludingThis() const {
  if (!hasOOLPointer()) {
    return 0;
  }
  const uint8_t* oolPointer = getOOLPointer();
  if (!gc::IsBufferAlloc((void*)oolPointer)) {
    return 0;
  }

  return gc::GetAllocSize(zone(), oolPointer);
}

/* static */
bool WasmStructObject::getField(JSContext* cx, uint32_t index,
                                MutableHandle<Value> val) {
  const StructType& resultType = typeDef().structType();
  MOZ_ASSERT(index <= resultType.fields_.length());
  const FieldType& field = resultType.fields_[index];
  StorageType ty = field.type.storageType();
  return ToJSValue(cx, fieldIndexToAddress(index), ty, val);
}

/* static */
uint8_t* WasmStructObject::fieldIndexToAddress(uint32_t fieldIndex) {
  const wasm::SuperTypeVector* stv = superTypeVector_;
  const wasm::TypeDef* typeDef = stv->typeDef();
  MOZ_ASSERT(typeDef->superTypeVector() == stv);
  const wasm::StructType& structType = typeDef->structType();
  const wasm::FieldAccessPathVector& fieldAccessPaths =
      structType.fieldAccessPaths_;
  MOZ_RELEASE_ASSERT(fieldIndex < fieldAccessPaths.length());
  wasm::FieldAccessPath path = fieldAccessPaths[fieldIndex];
  uint32_t ilOffset = path.ilOffset();
  MOZ_RELEASE_ASSERT(ilOffset != wasm::StructType::InvalidOffset);
  if (MOZ_LIKELY(!path.hasOOL())) {
    return (uint8_t*)this + ilOffset;
  }
  uint8_t* oolBlock = *(uint8_t**)((uint8_t*)this + ilOffset);
  uint32_t oolOffset = path.oolOffset();
  MOZ_RELEASE_ASSERT(oolOffset != wasm::StructType::InvalidOffset);
  return oolBlock + oolOffset;
}

/* static */
void WasmStructObject::obj_trace(JSTracer* trc, JSObject* object) {
  WasmStructObject& structObj = object->as<WasmStructObject>();

  const auto& structType = structObj.typeDef().structType();
  for (uint32_t offset : structType.inlineTraceOffsets_) {
    AnyRef* fieldPtr = reinterpret_cast<AnyRef*>((uint8_t*)&structObj + offset);
    TraceManuallyBarrieredEdge(trc, fieldPtr, "wasm-struct-field");
  }
  if (MOZ_UNLIKELY(structType.totalSizeOOL_ > 0)) {
    uint8_t** addressOfOOLPtr = structObj.addressOfOOLPointer();
    // *addressOfOOLPtr may be null if the struct was only partially initialized
    // due to OOM during createStructOOL.
    if (MOZ_LIKELY(*addressOfOOLPtr)) {
      TraceBufferEdge(trc, addressOfOOLPtr, "WasmStructObject outline data");
      uint8_t* oolBase = *addressOfOOLPtr;
      for (uint32_t offset : structType.outlineTraceOffsets_) {
        AnyRef* fieldPtr = reinterpret_cast<AnyRef*>(oolBase + offset);
        TraceManuallyBarrieredEdge(trc, fieldPtr, "wasm-struct-field");
      }
    }
  }
}

/* static */
size_t WasmStructObject::obj_moved(JSObject* objNew, JSObject* objOld) {
  // This gets called only for struct objects that have an OOL area.  Compare
  // WasmStructObjectInlineClassExt vs WasmStructObjectOutlineClassExt below.
  MOZ_ASSERT(objNew != objOld);

  WasmStructObject& structNew = objNew->as<WasmStructObject>();
  WasmStructObject& structOld = objOld->as<WasmStructObject>();
  MOZ_ASSERT(structNew.hasOOLPointer() && structOld.hasOOLPointer());

  const TypeDef* typeDefNew = &structNew.typeDef();
  mozilla::DebugOnly<const TypeDef*> typeDefOld = &structOld.typeDef();
  MOZ_ASSERT(typeDefNew == typeDefOld);
  MOZ_ASSERT(typeDefNew->isStructType());
  MOZ_ASSERT(typeDefOld == typeDefNew);

  // At this point, the object has been copied, but the OOL storage area has
  // not been copied, nor has the OOL pointer been updated.  Hence:
  MOZ_ASSERT(structNew.getOOLPointer() == structOld.getOOLPointer());

  bool newIsInNursery = IsInsideNursery(objNew);
  bool oldIsInNursery = IsInsideNursery(objOld);

  // Tenured -> Tenured
  if (!oldIsInNursery && !newIsInNursery) {
    // The object already was in the tenured heap and has merely been moved
    // somewhere else in the the tenured heap.  This isn't interesting to us.
    return 0;
  }

  // Tenured -> Nursery: this transition isn't possible.
  MOZ_RELEASE_ASSERT(oldIsInNursery);

  // Nursery -> Nursery and Nursery -> Tenured
  // The object is being moved, either within the nursery or from the nursery
  // to the tenured heap.  Either way, we have to ask the nursery if it wants
  // to move the OOL block too, and if so set up a forwarding record for it.

  const StructType& structType = typeDefNew->structType();
  uint32_t outlineBytes = structType.totalSizeOOL_;
  // These must always agree.
  MOZ_ASSERT((outlineBytes > 0) == structNew.hasOOLPointer());
  // Because the WasmStructObjectInlineClassExt doesn't reference this
  // method; only WasmStructObjectOutlineClassExt does.
  MOZ_ASSERT(outlineBytes > 0);

  // Ask the nursery if it wants to relocate the OOL area, and if so capture
  // its new location in `addressOfOOLPointerNew`.
  Nursery& nursery = structNew.runtimeFromMainThread()->gc.nursery();
  uint8_t** addressOfOOLPointerNew = structNew.addressOfOOLPointer();
  nursery.maybeMoveBufferOnPromotion(addressOfOOLPointerNew, objNew,
                                     outlineBytes);

  // Set up forwarding for the OOL area.  In order to be able to use direct
  // forwarding, the OOL data area needs to be at least one word long, so that
  // this call to setForwardingPointerWhileTenuring can write the forwarding
  // address directly at the start of the old OOL area.  This is ensured by
  // logic in StructType::init.  See also comments in
  // WasmArrayObject::obj_moved.  Note that because the first word of the OOL
  // area is overwritten, we must not access the area after this point, and in
  // particular not in Instance::updateFrameForMovingGC.
  uint8_t* oolPointerOld = structOld.getOOLPointer();
  uint8_t* oolPointerNew = structNew.getOOLPointer();
  MOZ_RELEASE_ASSERT(outlineBytes >= sizeof(uintptr_t));
  if (oolPointerOld != oolPointerNew) {
    nursery.setForwardingPointerWhileTenuring(oolPointerOld, oolPointerNew,
                                              /*direct=*/true);
  }

  return 0;
}

void WasmStructObject::storeVal(const Val& val, uint32_t fieldIndex) {
  const StructType& structType = typeDef().structType();
  MOZ_ASSERT(fieldIndex < structType.fields_.length());

  StorageType fieldType = structType.fields_[fieldIndex].type;
  uint8_t* data = fieldIndexToAddress(fieldIndex);

  WriteValTo(this, val, fieldType, data);
}

static const JSClassOps WasmStructObjectOutlineClassOps = {
    .newEnumerate = WasmGcObject::obj_newEnumerate,
    .trace = WasmStructObject::obj_trace,
};
static const ClassExtension WasmStructObjectOutlineClassExt = {
    WasmStructObject::obj_moved, /* objectMovedOp */
};
const JSClass WasmStructObject::classOutline_ = {
    "WasmStructObject",
    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    &WasmStructObjectOutlineClassOps,
    JS_NULL_CLASS_SPEC,
    &WasmStructObjectOutlineClassExt,
    &WasmGcObject::objectOps_,
};

// Structs that only have inline data get a different class without a
// finalizer. This class should otherwise be identical to the class for
// structs with outline data.
static const JSClassOps WasmStructObjectInlineClassOps = {
    .newEnumerate = WasmGcObject::obj_newEnumerate,
    .trace = WasmStructObject::obj_trace,
};
static const ClassExtension WasmStructObjectInlineClassExt = {
    nullptr, /* objectMovedOp */
};
const JSClass WasmStructObject::classInline_ = {
    "WasmStructObject",
    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    &WasmStructObjectInlineClassOps,
    JS_NULL_CLASS_SPEC,
    &WasmStructObjectInlineClassExt,
    &WasmGcObject::objectOps_,
};

Messung V0.5 in Prozent
C=80 H=97 G=88

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-09-30) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.