Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  WasmAnyRef.h   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:
 *
 * Copyright 2023 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#ifndef wasm_anyref_h
#define wasm_anyref_h

#include "mozilla/FloatingPoint.h"

#include <utility>

#include "js/HeapAPI.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/Value.h"

// #include "NamespaceImports.h"

class JSObject;
class JSString;

namespace js {
namespace gc {
struct Cell;
};  // namespace gc

namespace wasm {

// [SMDOC] AnyRef
//
// An AnyRef is a boxed value that can represent any wasm reference type and any
// host type that the host system allows to flow into and out of wasm
// transparently.  It is a pointer-sized datum that has the same representation
// as all its subtypes (funcref, externref, eqref, (ref T), et al) due to the
// non-coercive subtyping of the wasm type system.
//
// The C++/wasm boundary always uses a 'void*' type to express AnyRef values, to
// emphasize the pointer-ness of the value.  The C++ code must transform the
// void* into an AnyRef by calling AnyRef::fromCompiledCode(), and transform an
// AnyRef into a void* by calling AnyRef::toCompiledCode().  Once in C++, we use
// AnyRef everywhere.  A JS Value is transformed into an AnyRef by calling
// AnyRef::fromJSValue(), and the AnyRef is transformed into a JS Value by
// calling AnyRef::toJSValue().
//
// NOTE that AnyRef values may point to GC'd storage and as such need to be
// rooted if they are kept live in boxed form across code that may cause GC!
// Use RootedAnyRef / HandleAnyRef / MutableHandleAnyRef where necessary.
//
// The lowest bits of the pointer value are used for tagging, to allow for some
// representation optimizations and to distinguish various types.
//
// The current tagging scheme is:
//   if (pointer == 0) then 'null'
//   if (pointer & 0x1) then 'i31'
//   if (pointer & 0x2) then 'string'
//   else 'object'
//
// NOTE: there is sequencing required when checking tags. If bit 0x1 is set,
// then bit 0x2 is part of the i31 value and does not imply string.
//
// An i31ref value has no sign interpretation within wasm, where instructions
// specify the signedness. When converting to/from a JS value, an i31ref value
// is treated as a signed 31-bit value.

// The kind of value stored in an AnyRef. This is not 1:1 with the pointer tag
// of AnyRef as this separates the 'Null' and 'Object' cases which are
// collapsed in the pointer tag.
enum class AnyRefKind : uint8_t {
  Null,
  Object,
  String,
  I31,
};

// The pointer tag of an AnyRef.
enum class AnyRefTag : uint8_t {
  // This value is either a JSObject& or a null pointer.
  ObjectOrNull = 0x0,
  // This value is a 31-bit integer.
  I31 = 0x1,
  // This value is a JSString*.
  String = 0x2,
};

// A reference to any wasm reference type or host (JS) value. AnyRef is
// optimized for efficient access to objects, strings, and 31-bit integers.
//
// See the above documentation comment for more details.
class AnyRef {
  uintptr_t value_;

  // Get the pointer tag stored in value_.
  AnyRefTag pointerTag() const { return GetUintptrTag(value_); }

  explicit AnyRef(uintptr_t value) : value_(value) {}

  static constexpr uintptr_t TagUintptr(uintptr_t value, AnyRefTag tag) {
    MOZ_ASSERT(!(value & TagMask));
    return value | uintptr_t(tag);
  }
  static constexpr uintptr_t UntagUintptr(uintptr_t value) {
    return value & ~TagMask;
  }
  static constexpr AnyRefTag GetUintptrTag(uintptr_t value) {
    // Mask off all but the lowest two-bits (the tag)
    uintptr_t rawTag = value & TagMask;
    // If the lowest bit is set, we want to normalize and only return
    // AnyRefTag::I31. Mask off the high-bit iff the low-bit was set.
    uintptr_t normalizedI31 = rawTag & ~(value << 1);
    return AnyRefTag(normalizedI31);
  }

  // Given a 32-bit signed integer within 31-bit signed bounds, turn it into
  // an AnyRef.
  static AnyRef fromInt32(int32_t value) {
    MOZ_ASSERT(!int32NeedsBoxing(value));
    return AnyRef::fromUint32Truncate(uint32_t(value));
  }

 public:
  static constexpr uintptr_t TagMask = 0x3;
  static constexpr uintptr_t TagShift = 2;
  static_assert(TagShift <= gc::CellAlignShift, "not enough free bits");
  // A mask for getting the GC thing an AnyRef represents.
  static constexpr uintptr_t GCThingMask = ~TagMask;
  // A combined mask for getting the gc::Chunk for an AnyRef that is a GC
  // thing.
  static constexpr uintptr_t GCThingChunkMask =
      GCThingMask & ~js::gc::ChunkMask;

  // The representation of a null reference value throughout the compiler for
  // when we need an integer constant. This is asserted to be equivalent to
  // nullptr in wasm::Init.
  static constexpr uintptr_t NullRefValue = 0;
  static constexpr uintptr_t InvalidRefValue = UINTPTR_MAX << TagShift;

  // The inclusive maximum 31-bit signed integer, 2^30 - 1.
  static constexpr int32_t MaxI31Value = (2 << 29) - 1;
  // The inclusive minimum 31-bit signed integer, -2^30.
  static constexpr int32_t MinI31Value = -(2 << 29);

  explicit AnyRef() : value_(NullRefValue) {}
  MOZ_IMPLICIT AnyRef(std::nullptr_t) : value_(NullRefValue) {}

  // The null AnyRef value.
  static AnyRef null() { return AnyRef(NullRefValue); }

  // An invalid AnyRef cannot arise naturally from wasm and so can be used as
  // a sentinel value to indicate failure from an AnyRef-returning function.
  static AnyRef invalid() { return AnyRef(InvalidRefValue); }

  // Given a JSObject* that comes from JS, turn it into AnyRef.
  static AnyRef fromJSObjectOrNull(JSObject* objectOrNull) {
    MOZ_ASSERT(GetUintptrTag((uintptr_t)objectOrNull) ==
               AnyRefTag::ObjectOrNull);
    return AnyRef((uintptr_t)objectOrNull);
  }

  // Given a JSObject& that comes from JS, turn it into AnyRef.
  static AnyRef fromJSObject(JSObject& object) {
    MOZ_ASSERT(GetUintptrTag((uintptr_t)&object) == AnyRefTag::ObjectOrNull);
    return AnyRef((uintptr_t)&object);
  }

  // Given a JSString* that comes from JS, turn it into AnyRef.
  static AnyRef fromJSString(JSString* string) {
    return AnyRef(TagUintptr((uintptr_t)string, AnyRefTag::String));
  }

  // Given a void* that comes from compiled wasm code, turn it into AnyRef.
  static AnyRef fromCompiledCode(void* pointer) {
    return AnyRef((uintptr_t)pointer);
  }

  // Given a JS value, turn it into AnyRef. This returns false if boxing the
  // value failed due to an OOM.
  static bool fromJSValue(JSContext* cx, JS::HandleValue value,
                          JS::MutableHandle<AnyRef> result);

  // fromUint32Truncate will produce an i31 from an int32 by truncating the
  // highest bit. For values in the 31-bit range, this losslessly preserves the
  // value. For values outside the 31-bit range, this performs 31-bit
  // wraparound.
  //
  // There are four cases here based on the two high bits:
  //   00 - [0, MaxI31Value]
  //   01 - (MaxI31Value, INT32_MAX]
  //   10 - [INT32_MIN, MinI31Value)
  //   11 - [MinI31Value, -1]
  //
  // The middle two cases can be ruled out if the value is guaranteed to be
  // within the i31 range. Therefore if we truncate the high bit upon converting
  // to i31 and perform a signed widening upon converting back to i32, we can
  // losslessly represent all i31 values.
  static AnyRef fromUint32Truncate(uint32_t value) {
    // See 64-bit GPRs carrying 32-bit values invariants in MacroAssember.h
#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_X64) || \
    defined(JS_CODEGEN_ARM64)
    // Truncate the value to the 31-bit value size.
    uintptr_t wideValue = uintptr_t(value & 0x7FFFFFFF);
#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \
    defined(JS_CODEGEN_RISCV64)
    // Sign extend the value to the native pointer size.
    uintptr_t wideValue = uintptr_t(int64_t((uint64_t(value) << 33)) >> 33);
#elif !defined(JS_64BIT)
    // Transfer 32-bit value as is.
    uintptr_t wideValue = (uintptr_t)value;
#else
#  error "unknown architecture"
#endif

    // Left shift the value by 1, truncating the high bit.
    uintptr_t shiftedValue = wideValue << 1;
    uintptr_t taggedValue = shiftedValue | (uintptr_t)AnyRefTag::I31;
#ifdef JS_64BIT
    debugAssertCanonicalInt32(taggedValue);
#endif
    return AnyRef(taggedValue);
  }

#ifdef JS_64BIT
  // Ensure the value fits into a 32-bits integer on 64-bits platforms.
  static void debugAssertCanonicalInt32(uintptr_t value) {
#  ifdef DEBUG
#    if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
    MOZ_ASSERT(value <= UINT32_MAX);
#    endif
#  endif
  }
#endif

  static bool int32NeedsBoxing(int32_t value) {
    // We can represent every signed 31-bit number without boxing
    return value < MinI31Value || value > MaxI31Value;
  }

  static bool doubleNeedsBoxing(double value) {
    int32_t intValue;
    if (!mozilla::NumberIsInt32(value, &intValue)) {
      return true;
    }
    return int32NeedsBoxing(value);
  }

  // Returns whether a JS value will need to be boxed.
  static bool valueNeedsBoxing(JS::HandleValue value) {
    if (value.isObjectOrNull() || value.isString()) {
      return false;
    }
    if (value.isInt32()) {
      return int32NeedsBoxing(value.toInt32());
    }
    if (value.isDouble()) {
      return doubleNeedsBoxing(value.toDouble());
    }
    return true;
  }

  // Box a JS Value that needs boxing.
  static JSObject* boxValue(JSContext* cx, JS::HandleValue value);

  bool operator==(const AnyRef& rhs) const {
    return this->value_ == rhs.value_;
  }
  bool operator!=(const AnyRef& rhs) const { return !(*this == rhs); }

  // Check if this AnyRef is the invalid value.
  bool isInvalid() const { return *this == AnyRef::invalid(); }

  AnyRefKind kind() const {
    if (value_ == NullRefValue) {
      return AnyRefKind::Null;
    }
    switch (pointerTag()) {
      case AnyRefTag::ObjectOrNull: {
        // The invalid pattern uses the ObjectOrNull tag, check for it here.
        MOZ_ASSERT(!isInvalid());
        // We ruled out the null case above
        return AnyRefKind::Object;
      }
      case AnyRefTag::String: {
        return AnyRefKind::String;
      }
      case AnyRefTag::I31: {
        return AnyRefKind::I31;
      }
      default: {
        MOZ_CRASH("unknown AnyRef tag");
      }
    }
  }

  bool isNull() const { return value_ == NullRefValue; }
  bool isGCThing() const { return !isNull() && !isI31(); }
  bool isJSObject() const { return kind() == AnyRefKind::Object; }
  bool isJSString() const { return kind() == AnyRefKind::String; }
  bool isI31() const { return kind() == AnyRefKind::I31; }

  gc::Cell* toGCThing() const {
    MOZ_ASSERT(isGCThing());
    return (gc::Cell*)UntagUintptr(value_);
  }
  JSObject& toJSObject() const {
    MOZ_ASSERT(isJSObject());
    return *(JSObject*)value_;
  }
  JSObject* toJSObjectOrNull() const {
    MOZ_ASSERT(!isInvalid());
    MOZ_ASSERT(pointerTag() == AnyRefTag::ObjectOrNull);
    return (JSObject*)value_;
  }
  JSString* toJSString() const {
    MOZ_ASSERT(isJSString());
    return (JSString*)UntagUintptr(value_);
  }
  // Unpack an i31, interpreting the integer as signed.
  int32_t toI31() const {
    MOZ_ASSERT(isI31());
#ifdef JS_64BIT
    debugAssertCanonicalInt32(value_);
#endif
    // On 64-bit targets, we only care about the low 4-bytes.
    uint32_t truncatedValue;
    memcpy(&truncatedValue, &value_, sizeof(uint32_t));
    // Perform a right arithmetic shift (see AnyRef::fromI31 for more details),
    // avoiding undefined behavior by using an unsigned type.
    uint32_t shiftedValue = value_ >> 1;
    if ((truncatedValue & (1 << 31)) != 0) {
      shiftedValue |= (1 << 31);
    }
    // Perform a bitwise cast to see the result as a signed value.
    return mozilla::BitwiseCast<int32_t>(shiftedValue);
  }

  // Convert from AnyRef to a JS Value. This currently does not require any
  // allocation. If this changes in the future, this function will become
  // more complicated.
  JS::Value toJSValue() const;

  // Get the raw value for returning to wasm code.
  void* forCompiledCode() const { return (void*)value_; }

  // Get the raw value for diagnostics.
  uintptr_t rawValue() const { return value_; }

  // Internal details of the boxing format used by WasmStubs.cpp
  static const JSClass* valueBoxClass();
  static size_t valueBoxOffsetOfValue();
};

using RootedAnyRef = JS::Rooted<AnyRef>;
using HandleAnyRef = JS::Handle<AnyRef>;
using MutableHandleAnyRef = JS::MutableHandle<AnyRef>;

}  // namespace wasm

template <class Wrapper>
class WrappedPtrOperations<wasm::AnyRef, Wrapper> {
  const wasm::AnyRef& value() const {
    return static_cast<const Wrapper*>(this)->get();
  }

 public:
  bool isNull() const { return value().isNull(); }
  bool isI31() const { return value().isI31(); }
  bool isJSObject() const { return value().isJSObject(); }
  bool isJSString() const { return value().isJSString(); }
  JSObject& toJSObject() const { return value().toJSObject(); }
  JSString* toJSString() const { return value().toJSString(); }
};

// If the Value is a GC pointer type, call |f| with the pointer cast to that
// type and return the result wrapped in a Maybe, otherwise return None().
template <typename F>
auto MapGCThingTyped(const wasm::AnyRef& val, F&& f) {
  switch (val.kind()) {
    case wasm::AnyRefKind::Object:
      return mozilla::Some(f(&val.toJSObject()));
    case wasm::AnyRefKind::String:
      return mozilla::Some(f(val.toJSString()));
    case wasm::AnyRefKind::I31:
    case wasm::AnyRefKind::Null: {
      using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
      return mozilla::Maybe<ReturnType>();
    }
  }
  MOZ_CRASH();
}

template <typename F>
bool ApplyGCThingTyped(const wasm::AnyRef& val, F&& f) {
  return MapGCThingTyped(val,
                         [&f](auto t) {
                           f(t);
                           return true;
                         })
      .isSome();
}

}  // namespace js

namespace JS {

template <>
struct GCPolicy<js::wasm::AnyRef> {
  static void trace(JSTracer* trc, js::wasm::AnyRef* v, const char* name) {
    // This should only be called as part of root marking since that's the only
    // time we should trace unbarriered GC thing pointers. This will assert if
    // called at other times.
    TraceRoot(trc, v, name);
  }
  static bool isValid(const js::wasm::AnyRef& v) {
    return !v.isGCThing() || js::gc::IsCellPointerValid(v.toGCThing());
  }
};

}  // namespace JS

#endif  // wasm_anyref_h

Messung V0.5
C=90 H=95 G=92

¤ Dauer der Verarbeitung: 0.13 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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge