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

Quelle  JSObject.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:
 * 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/. */


/*
 * JS object implementation.
 */


#include "vm/JSObject-inl.h"

#include "mozilla/MemoryReporting.h"
#include "mozilla/Try.h"

#include <string.h>

#include "jsapi.h"
#include "jsdate.h"
#include "jsexn.h"
#include "jsfriendapi.h"
#include "jsnum.h"
#include "jstypes.h"

#include "builtin/BigInt.h"
#include "builtin/MapObject.h"
#include "builtin/Object.h"
#include "builtin/String.h"
#include "builtin/Symbol.h"
#include "builtin/WeakSetObject.h"
#include "gc/AllocKind.h"
#include "gc/GC.h"
#include "js/CharacterEncoding.h"
#include "js/friend/DumpFunctions.h"  // js::DumpObject
#include "js/friend/ErrorMessages.h"  // JSErrNum, js::GetErrorMessage, JSMSG_*
#include "js/friend/WindowProxy.h"    // js::IsWindow, js::ToWindowProxyIfWindow
#include "js/MemoryMetrics.h"
#include "js/Prefs.h"               // JS::Prefs
#include "js/Printer.h"             // js::GenericPrinter, js::Fprinter
#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
#include "js/PropertySpec.h"        // JSPropertySpec
#include "js/Proxy.h"
#include "js/Result.h"
#include "js/UbiNode.h"
#include "js/Wrapper.h"
#include "proxy/DeadObjectProxy.h"
#include "util/Memory.h"
#include "util/Text.h"
#include "util/WindowsWrapper.h"
#include "vm/ArgumentsObject.h"
#include "vm/ArrayBufferObject.h"
#include "vm/ArrayBufferViewObject.h"
#include "vm/BytecodeUtil.h"
#include "vm/Compartment.h"
#include "vm/DateObject.h"
#include "vm/Interpreter.h"
#include "vm/Iteration.h"
#include "vm/JSAtomUtils.h"  // Atomize
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSONPrinter.h"  // js::JSONPrinter
#include "vm/JSScript.h"
#include "vm/PromiseObject.h"
#include "vm/ProxyObject.h"
#include "vm/RegExpObject.h"
#include "vm/SelfHosting.h"
#include "vm/Shape.h"
#include "vm/TypedArrayObject.h"
#include "vm/Watchtower.h"
#include "vm/WrapperObject.h"
#ifdef ENABLE_RECORD_TUPLE
#  include "builtin/RecordObject.h"
#  include "builtin/TupleObject.h"
#  include "vm/RecordType.h"
#  include "vm/TupleType.h"
#endif

#include "gc/StableCellHasher-inl.h"
#include "vm/BooleanObject-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/JSAtomUtils-inl.h"  // AtomToId, PrimitiveValueToId, IndexToId
#include "vm/JSContext-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/NumberObject-inl.h"
#include "vm/ObjectFlags-inl.h"
#include "vm/Realm-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypedArrayObject-inl.h"
#include "wasm/WasmGcObject-inl.h"

using namespace js;

using mozilla::Maybe;

void js::ReportNotObject(JSContext* cx, JSErrNum err, int spindex,
                         HandleValue v) {
  MOZ_ASSERT(!v.isObject());
  ReportValueError(cx, err, spindex, v, nullptr);
}

void js::ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v) {
  ReportNotObject(cx, err, JSDVG_SEARCH_STACK, v);
}

void js::ReportNotObject(JSContext* cx, const Value& v) {
  RootedValue value(cx, v);
  ReportNotObject(cx, JSMSG_OBJECT_REQUIRED, value);
}

void js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun,
                            HandleValue v) {
  MOZ_ASSERT(!v.isObject());

  UniqueChars bytes;
  const char* chars = ValueToSourceForError(cx, v, bytes);
  MOZ_ASSERT(chars);
  JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                             JSMSG_OBJECT_REQUIRED_ARG, nth, fun, chars);
}

JS_PUBLIC_API const char* JS::InformalValueTypeName(const Value& v) {
  switch (v.type()) {
    case ValueType::Double:
    case ValueType::Int32:
      return "number";
    case ValueType::Boolean:
      return "boolean";
    case ValueType::Undefined:
      return "undefined";
    case ValueType::Null:
      return "null";
    case ValueType::String:
      return "string";
    case ValueType::Symbol:
      return "symbol";
    case ValueType::BigInt:
      return "bigint";
    case ValueType::Object:
#ifdef ENABLE_RECORD_TUPLE
    case ValueType::ExtendedPrimitive:
#endif
      return v.getObjectPayload().getClass()->name;
    case ValueType::Magic:
      return "magic";
    case ValueType::PrivateGCThing:
      break;
  }

  MOZ_CRASH("unexpected type");
}

// ES6 draft rev37 6.2.4.4 FromPropertyDescriptor
JS_PUBLIC_API bool JS::FromPropertyDescriptor(
    JSContext* cx, Handle<Maybe<PropertyDescriptor>> desc_,
    MutableHandleValue vp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(desc_);

  // Step 1.
  if (desc_.isNothing()) {
    vp.setUndefined();
    return true;
  }

  Rooted<PropertyDescriptor> desc(cx, *desc_);
  return FromPropertyDescriptorToObject(cx, desc, vp);
}

bool js::FromPropertyDescriptorToObject(JSContext* cx,
                                        Handle<PropertyDescriptor> desc,
                                        MutableHandleValue vp) {
  // Step 2-3.
  RootedObject obj(cx, NewPlainObject(cx));
  if (!obj) {
    return false;
  }

  const JSAtomState& names = cx->names();

  // Step 4.
  if (desc.hasValue()) {
    if (!DefineDataProperty(cx, obj, names.value, desc.value())) {
      return false;
    }
  }

  // Step 5.
  RootedValue v(cx);
  if (desc.hasWritable()) {
    v.setBoolean(desc.writable());
    if (!DefineDataProperty(cx, obj, names.writable, v)) {
      return false;
    }
  }

  // Step 6.
  if (desc.hasGetter()) {
    if (JSObject* get = desc.getter()) {
      v.setObject(*get);
    } else {
      v.setUndefined();
    }
    if (!DefineDataProperty(cx, obj, names.get, v)) {
      return false;
    }
  }

  // Step 7.
  if (desc.hasSetter()) {
    if (JSObject* set = desc.setter()) {
      v.setObject(*set);
    } else {
      v.setUndefined();
    }
    if (!DefineDataProperty(cx, obj, names.set, v)) {
      return false;
    }
  }

  // Step 8.
  if (desc.hasEnumerable()) {
    v.setBoolean(desc.enumerable());
    if (!DefineDataProperty(cx, obj, names.enumerable, v)) {
      return false;
    }
  }

  // Step 9.
  if (desc.hasConfigurable()) {
    v.setBoolean(desc.configurable());
    if (!DefineDataProperty(cx, obj, names.configurable, v)) {
      return false;
    }
  }

  vp.setObject(*obj);
  return true;
}

bool js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args,
                                  const char* method,
                                  MutableHandleObject objp) {
  if (!args.requireAtLeast(cx, method, 1)) {
    return false;
  }

  HandleValue v = args[0];
  if (!v.isObject()) {
    UniqueChars bytes =
        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
    if (!bytes) {
      return false;
    }
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                             JSMSG_UNEXPECTED_TYPE, bytes.get(),
                             "not an object");
    return false;
  }

  objp.set(&v.toObject());
  return true;
}

static bool GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id,
                                 MutableHandleValue vp, bool* foundp) {
  if (!HasProperty(cx, obj, id, foundp)) {
    return false;
  }
  if (!*foundp) {
    vp.setUndefined();
    return true;
  }

  return GetProperty(cx, obj, obj, id, vp);
}

bool js::Throw(JSContext* cx, HandleId id, unsigned errorNumber,
               const char* details) {
  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
  MOZ_ASSERT_IF(details, JS::StringIsASCII(details));

  UniqueChars bytes =
      IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
  if (!bytes) {
    return false;
  }

  if (details) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
                             bytes.get(), details);
  } else {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
                             bytes.get());
  }

  return false;
}

/*** PropertyDescriptor operations and DefineProperties *********************/

static Result<> CheckCallable(JSContext* cx, JSObject* obj,
                              const char* fieldName) {
  if (obj && !obj->isCallable()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_GET_SET_FIELD, fieldName);
    return cx->alreadyReportedError();
  }
  return Ok();
}

// 6.2.5.5 ToPropertyDescriptor(Obj)
bool js::ToPropertyDescriptor(JSContext* cx, HandleValue descval,
                              bool checkAccessors,
                              MutableHandle<PropertyDescriptor> desc_) {
  // Step 1.
  RootedObject obj(cx,
                   RequireObject(cx, JSMSG_OBJECT_REQUIRED_PROP_DESC, descval));
  if (!obj) {
    return false;
  }

  // Step 2.
  Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty());

  RootedId id(cx);
  RootedValue v(cx);

  // Steps 3-4.
  id = NameToId(cx->names().enumerable);
  bool hasEnumerable = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasEnumerable)) {
    return false;
  }
  if (hasEnumerable) {
    desc.setEnumerable(ToBoolean(v));
  }

  // Steps 5-6.
  id = NameToId(cx->names().configurable);
  bool hasConfigurable = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasConfigurable)) {
    return false;
  }
  if (hasConfigurable) {
    desc.setConfigurable(ToBoolean(v));
  }

  // Steps 7-8.
  id = NameToId(cx->names().value);
  bool hasValue = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasValue)) {
    return false;
  }
  if (hasValue) {
    desc.setValue(v);
  }

  // Steps 9-10.
  id = NameToId(cx->names().writable);
  bool hasWritable = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasWritable)) {
    return false;
  }
  if (hasWritable) {
    desc.setWritable(ToBoolean(v));
  }

  // Steps 11-12.
  id = NameToId(cx->names().get);
  bool hasGet = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasGet)) {
    return false;
  }
  RootedObject getter(cx);
  if (hasGet) {
    if (v.isObject()) {
      if (checkAccessors) {
        JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), "getter"));
      }
      getter = &v.toObject();
    } else if (v.isUndefined()) {
      getter = nullptr;
    } else {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_BAD_GET_SET_FIELD, "getter");
      return false;
    }
  }

  // Steps 13-14.
  id = NameToId(cx->names().set);
  bool hasSet = false;
  if (!GetPropertyIfPresent(cx, obj, id, &v, &hasSet)) {
    return false;
  }
  RootedObject setter(cx);
  if (hasSet) {
    if (v.isObject()) {
      if (checkAccessors) {
        JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), "setter"));
      }
      setter = &v.toObject();
    } else if (v.isUndefined()) {
      setter = nullptr;
    } else {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_BAD_GET_SET_FIELD, "setter");
      return false;
    }
  }

  // Step 15.
  if (hasGet || hasSet) {
    if (hasValue || hasWritable) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_INVALID_DESCRIPTOR);
      return false;
    }

    // We delay setGetter/setSetter after the previous check,
    // because otherwise we would assert.
    if (hasGet) {
      desc.setGetter(getter);
    }
    if (hasSet) {
      desc.setSetter(setter);
    }
  }

  desc.assertValid();
  desc_.set(desc);
  return true;
}

Result<> js::CheckPropertyDescriptorAccessors(JSContext* cx,
                                              Handle<PropertyDescriptor> desc) {
  if (desc.hasGetter()) {
    MOZ_TRY(CheckCallable(cx, desc.getter(), "getter"));
  }

  if (desc.hasSetter()) {
    MOZ_TRY(CheckCallable(cx, desc.setter(), "setter"));
  }

  return Ok();
}

// 6.2.5.6 CompletePropertyDescriptor(Desc)
void js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc) {
  // Step 1.
  desc.assertValid();

  // Step 2.
  // Let like be the Record { [[Value]]: undefined, [[Writable]]: false,
  //                          [[Get]]: undefined, [[Set]]: undefined,
  //                          [[Enumerable]]: false, [[Configurable]]: false }.

  // Step 3.
  if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
    // Step 3.a.
    if (!desc.hasValue()) {
      desc.setValue(UndefinedHandleValue);
    }
    // Step 3.b.
    if (!desc.hasWritable()) {
      desc.setWritable(false);
    }
  } else {
    // Step 4.a.
    if (!desc.hasGetter()) {
      desc.setGetter(nullptr);
    }
    // Step 4.b.
    if (!desc.hasSetter()) {
      desc.setSetter(nullptr);
    }
  }

  // Step 5.
  if (!desc.hasEnumerable()) {
    desc.setEnumerable(false);
  }

  // Step 6.
  if (!desc.hasConfigurable()) {
    desc.setConfigurable(false);
  }

  desc.assertComplete();
}

bool js::ReadPropertyDescriptors(
    JSContext* cx, HandleObject props, bool checkAccessors,
    MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs) {
  if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) {
    return false;
  }

  RootedId id(cx);
  for (size_t i = 0, len = ids.length(); i < len; i++) {
    id = ids[i];
    Rooted<PropertyDescriptor> desc(cx);
    RootedValue v(cx);
    if (!GetProperty(cx, props, props, id, &v) ||
        !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
        !descs.append(desc)) {
      return false;
    }
  }
  return true;
}

/*** Seal and freeze ********************************************************/

/* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */
bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj,
                           IntegrityLevel level) {
  cx->check(obj);

  // Steps 3-5. (Steps 1-2 are redundant assertions.)
  if (!PreventExtensions(cx, obj)) {
    return false;
  }

  // Steps 6-9, loosely interpreted.
  if (obj->is<NativeObject>() && !obj->is<TypedArrayObject>() &&
      !obj->is<MappedArgumentsObject>()) {
    Handle<NativeObject*> nobj = obj.as<NativeObject>();

    // Use a fast path to seal/freeze properties. This has the benefit of
    // creating shared property maps if possible, whereas the slower/generic
    // implementation below ends up converting non-empty objects to dictionary
    // mode.
    if (nobj->shape()->propMapLength() > 0) {
      if (!NativeObject::freezeOrSealProperties(cx, nobj, level)) {
        return false;
      }
    }

    // Ordinarily ArraySetLength handles this, but we're going behind its back
    // right now, so we must do this manually.
    if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
      obj->as<ArrayObject>().setNonWritableLength(cx);
    }
  } else {
    // Steps 6-7.
    RootedIdVector keys(cx);
    if (!GetPropertyKeys(
            cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys)) {
      return false;
    }

    RootedId id(cx);
    Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty());

    // 8.a/9.a. The two different loops are merged here.
    for (size_t i = 0; i < keys.length(); i++) {
      id = keys[i];

      if (level == IntegrityLevel::Sealed) {
        // 8.a.i.
        desc.setConfigurable(false);
      } else {
        // 9.a.i-ii.
        Rooted<Maybe<PropertyDescriptor>> currentDesc(cx);
        if (!GetOwnPropertyDescriptor(cx, obj, id, ¤tDesc)) {
          return false;
        }

        // 9.a.iii.
        if (currentDesc.isNothing()) {
          continue;
        }

        // 9.a.iii.1-2
        desc = PropertyDescriptor::Empty();
        if (currentDesc->isAccessorDescriptor()) {
          desc.setConfigurable(false);
        } else {
          desc.setConfigurable(false);
          desc.setWritable(false);
        }
      }

      // 8.a.i-ii. / 9.a.iii.3-4
      if (!DefineProperty(cx, obj, id, desc)) {
        return false;
      }
    }
  }

  // Finally, freeze or seal the dense elements.
  if (obj->is<NativeObject>()) {
    if (!ObjectElements::FreezeOrSeal(cx, obj.as<NativeObject>(), level)) {
      return false;
    }
  }

  return true;
}

static bool ResolveLazyProperties(JSContext* cx, Handle<NativeObject*> obj) {
  const JSClass* clasp = obj->getClass();
  if (JSEnumerateOp enumerate = clasp->getEnumerate()) {
    if (!enumerate(cx, obj)) {
      return false;
    }
  }
  if (clasp->getNewEnumerate() && clasp->getResolve()) {
    RootedIdVector properties(cx);
    if (!clasp->getNewEnumerate()(cx, obj, &properties,
                                  /* enumerableOnly = */ false)) {
      return false;
    }

    RootedId id(cx);
    for (size_t i = 0; i < properties.length(); i++) {
      id = properties[i];
      bool found;
      if (!HasOwnProperty(cx, obj, id, &found)) {
        return false;
      }
    }
  }
  return true;
}

// ES6 draft rev33 (12 Feb 2015) 7.3.15
bool js::TestIntegrityLevel(JSContext* cx, HandleObject obj,
                            IntegrityLevel level, bool* result) {
  // Steps 3-6. (Steps 1-2 are redundant assertions.)
  bool status;
  if (!IsExtensible(cx, obj, &status)) {
    return false;
  }
  if (status) {
    *result = false;
    return true;
  }

  // Fast path for native objects.
  if (obj->is<NativeObject>()) {
    Handle<NativeObject*> nobj = obj.as<NativeObject>();

    // Force lazy properties to be resolved.
    if (!ResolveLazyProperties(cx, nobj)) {
      return false;
    }

    // Typed array elements are configurable, writable properties, so if any
    // elements are present, the typed array can neither be sealed nor frozen.
    if (nobj->is<TypedArrayObject>() &&
        nobj->as<TypedArrayObject>().length().valueOr(0) > 0) {
      *result = false;
      return true;
    }

    bool hasDenseElements = false;
    for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
      if (nobj->containsDenseElement(i)) {
        hasDenseElements = true;
        break;
      }
    }

    if (hasDenseElements) {
      // Unless the sealed flag is set, dense elements are configurable.
      if (!nobj->denseElementsAreSealed()) {
        *result = false;
        return true;
      }

      // Unless the frozen flag is set, dense elements are writable.
      if (level == IntegrityLevel::Frozen && !nobj->denseElementsAreFrozen()) {
        *result = false;
        return true;
      }
    }

    // Steps 7-9.
    for (ShapePropertyIter<NoGC> iter(nobj->shape()); !iter.done(); iter++) {
      // Steps 9.c.i-ii.
      if (iter->configurable() ||
          (level == IntegrityLevel::Frozen && iter->isDataDescriptor() &&
           iter->writable())) {
        // Private fields on objects don't participate in the frozen state, and
        // so should be elided from checking for frozen state.
        if (iter->key().isPrivateName()) {
          continue;
        }

        *result = false;
        return true;
      }
    }
  } else {
    // Steps 7-8.
    RootedIdVector props(cx);
    if (!GetPropertyKeys(
            cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props)) {
      return false;
    }

    // Step 9.
    RootedId id(cx);
    Rooted<Maybe<PropertyDescriptor>> desc(cx);
    for (size_t i = 0, len = props.length(); i < len; i++) {
      id = props[i];

      // Steps 9.a-b.
      if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
        return false;
      }

      // Step 9.c.
      if (desc.isNothing()) {
        continue;
      }

      // Steps 9.c.i-ii.
      if (desc->configurable() ||
          (level == IntegrityLevel::Frozen && desc->isDataDescriptor() &&
           desc->writable())) {
        // Since we don't request JSITER_PRIVATE in GetPropertyKeys above, we
        // should never see a private name here.
        MOZ_ASSERT(!id.isPrivateName());
        *result = false;
        return true;
      }
    }
  }

  // Step 10.
  *result = true;
  return true;
}

/* * */

static MOZ_ALWAYS_INLINE NativeObject* NewObject(
    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
    gc::AllocKind kind, NewObjectKind newKind, ObjectFlags objFlags,
    gc::AllocSite* allocSite = nullptr) {
  MOZ_ASSERT(clasp->isNativeObject());

  // Some classes have specialized allocation functions and shouldn't end up
  // here.
  MOZ_ASSERT(clasp != &ArrayObject::class_);
  MOZ_ASSERT(clasp != &PlainObject::class_);
  MOZ_ASSERT(!clasp->isJSFunction());

  MOZ_ASSERT_IF(allocSite, allocSite->zone() == cx->zone());

  // Computing nfixed based on the AllocKind isn't right for objects which can
  // store fixed data inline (TypedArrays and ArrayBuffers) so for simplicity
  // and performance reasons we don't support such objects here.
  MOZ_ASSERT(!ClassCanHaveFixedData(clasp));
  size_t nfixed = GetGCKindSlots(kind);

  if (CanChangeToBackgroundAllocKind(kind, clasp)) {
    kind = ForegroundToBackgroundAllocKind(kind);
  }

  Rooted<SharedShape*> shape(
      cx, SharedShape::getInitialShape(cx, clasp, cx->realm(), proto, nfixed,
                                       objFlags));
  if (!shape) {
    return nullptr;
  }

  gc::Heap heap = GetInitialHeap(newKind, clasp, allocSite);
  NativeObject* obj = NativeObject::create(cx, kind, heap, shape, allocSite);
  if (!obj) {
    return nullptr;
  }

  probes::CreateObject(cx, obj);
  return obj;
}

NativeObject* js::NewObjectWithGivenTaggedProto(
    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
    gc::AllocKind allocKind, NewObjectKind newKind, ObjectFlags objFlags) {
  return NewObject(cx, clasp, proto, allocKind, newKind, objFlags);
}

NativeObject* js::NewObjectWithGivenTaggedProtoAndAllocSite(
    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
    gc::AllocKind allocKind, NewObjectKind newKind, ObjectFlags objFlags,
    gc::AllocSite* site) {
  return NewObject(cx, clasp, proto, allocKind, newKind, objFlags, site);
}

NativeObject* js::NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
                                          HandleObject protoArg,
                                          gc::AllocKind allocKind,
                                          NewObjectKind newKind,
                                          ObjectFlags objFlags) {
  if (protoArg) {
    return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg),
                                         allocKind, newKind, objFlags);
  }

  // Find the appropriate proto for clasp. Built-in classes have a cached
  // proto on cx->global(); all others get %ObjectPrototype%.
  JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
  if (protoKey == JSProto_Null) {
    protoKey = JSProto_Object;
  }

  JSObject* proto = GlobalObject::getOrCreatePrototype(cx, protoKey);
  if (!proto) {
    return nullptr;
  }

  Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
  return NewObject(cx, clasp, taggedProto, allocKind, newKind, objFlags);
}

bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget,
                                     JSProtoKey intrinsicDefaultProto,
                                     MutableHandleObject proto) {
  RootedValue protov(cx);
  if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
    return false;
  }
  if (protov.isObject()) {
    proto.set(&protov.toObject());
  } else if (newTarget->is<JSFunction>() &&
             newTarget->as<JSFunction>().realm() == cx->realm()) {
    // Steps 4.a-b fetch the builtin prototype of the current realm, which we
    // represent as nullptr.
    proto.set(nullptr);
  } else if (intrinsicDefaultProto == JSProto_Null) {
    // Bug 1317416. The caller did not pass a reasonable JSProtoKey, so let the
    // caller select a prototype object. Most likely they will choose one from
    // the wrong realm.
    proto.set(nullptr);
  } else {
    // Step 4.a: Let realm be ? GetFunctionRealm(constructor);
    Realm* realm = JS::GetFunctionRealm(cx, newTarget);
    if (!realm) {
      return false;
    }

    // Step 4.b: Set proto to realm's intrinsic object named
    //           intrinsicDefaultProto.
    {
      Maybe<AutoRealm> ar;
      if (cx->realm() != realm) {
        ar.emplace(cx, realm->maybeGlobal());
      }
      proto.set(GlobalObject::getOrCreatePrototype(cx, intrinsicDefaultProto));
    }
    if (!proto) {
      return false;
    }
    if (!cx->compartment()->wrap(cx, proto)) {
      return false;
    }
  }
  return true;
}

/* static */
bool JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj,
                                    HandleId id, HandleValue v,
                                    HandleValue receiver,
                                    ObjectOpResult& result) {
  return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
}

/* static */
bool JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj,
                                   uint32_t index, HandleValue v,
                                   HandleValue receiver,
                                   ObjectOpResult& result) {
  RootedId id(cx);
  if (!IndexToId(cx, index, &id)) {
    return false;
  }
  return nonNativeSetProperty(cx, obj, id, v, receiver, result);
}

static bool CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target,
                             HandleObject obj) {
  // |target| must not be a CCW because we need to enter its realm below and
  // CCWs are not associated with a single realm.
  MOZ_ASSERT(!IsCrossCompartmentWrapper(target));

  // |obj| and |cx| are generally not same-compartment with |target| here.
  cx->check(obj, id);
  Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);

  if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
    return false;
  }
  MOZ_ASSERT(desc.isSome());

  JSAutoRealm ar(cx, target);
  cx->markId(id);
  RootedId wrappedId(cx, id);
  if (!cx->compartment()->wrap(cx, &desc)) {
    return false;
  }

  Rooted<PropertyDescriptor> desc_(cx, *desc);
  return DefineProperty(cx, target, wrappedId, desc_);
}

JS_PUBLIC_API bool JS_CopyOwnPropertiesAndPrivateFields(JSContext* cx,
                                                        HandleObject target,
                                                        HandleObject obj) {
  // Both |obj| and |target| must not be CCWs because we need to enter their
  // realms below and CCWs are not associated with a single realm.
  MOZ_ASSERT(!IsCrossCompartmentWrapper(obj));
  MOZ_ASSERT(!IsCrossCompartmentWrapper(target));

  JSAutoRealm ar(cx, obj);

  RootedIdVector props(cx);
  if (!GetPropertyKeys(
          cx, obj,
          JSITER_PRIVATE | JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
          &props)) {
    return false;
  }

  for (size_t i = 0; i < props.length(); ++i) {
    if (!CopyPropertyFrom(cx, props[i], target, obj)) {
      return false;
    }
  }

  return true;
}

static bool InitializePropertiesFromCompatibleNativeObject(
    JSContext* cx, Handle<NativeObject*> dst, Handle<NativeObject*> src) {
  cx->check(src, dst);
  MOZ_ASSERT(src->getClass() == dst->getClass());
  MOZ_ASSERT(dst->shape()->objectFlags().isEmpty());
  MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
  MOZ_ASSERT(!src->inDictionaryMode());
  MOZ_ASSERT(!dst->inDictionaryMode());

  if (!dst->ensureElements(cx, src->getDenseInitializedLength())) {
    return false;
  }

  uint32_t initialized = src->getDenseInitializedLength();
  for (uint32_t i = 0; i < initialized; ++i) {
    dst->setDenseInitializedLength(i + 1);
    dst->initDenseElement(i, src->getDenseElement(i));
  }

  // If there are no properties to copy, we're done.
  if (!src->sharedShape()->propMap()) {
    return true;
  }

  Rooted<SharedShape*> shape(cx);
  if (src->staticPrototype() == dst->staticPrototype()) {
    shape = src->sharedShape();
  } else {
    // We need to generate a new shape for dst that has dst's proto but all
    // the property information from src.  Note that we asserted above that
    // dst's object flags are empty.
    SharedShape* srcShape = src->sharedShape();
    ObjectFlags objFlags;
    objFlags = CopyPropMapObjectFlags(objFlags, srcShape->objectFlags());
    Rooted<SharedPropMap*> map(cx, srcShape->propMap());
    uint32_t mapLength = srcShape->propMapLength();
    shape = SharedShape::getPropMapShape(cx, dst->shape()->base(),
                                         dst->numFixedSlots(), map, mapLength,
                                         objFlags);
    if (!shape) {
      return false;
    }
  }

  uint32_t oldSpan = dst->sharedShape()->slotSpan();
  uint32_t newSpan = shape->slotSpan();
  if (!dst->setShapeAndAddNewSlots(cx, shape, oldSpan, newSpan)) {
    return false;
  }
  for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < newSpan; i++) {
    dst->setSlot(i, src->getSlot(i));
  }

  return true;
}

JS_PUBLIC_API bool JS_InitializePropertiesFromCompatibleNativeObject(
    JSContext* cx, HandleObject dst, HandleObject src) {
  return InitializePropertiesFromCompatibleNativeObject(
      cx, dst.as<NativeObject>(), src.as<NativeObject>());
}

bool js::ObjectMayBeSwapped(const JSObject* obj) {
  const JSClass* clasp = obj->getClass();

  // We want to optimize Window/globals and Gecko doesn't require transplanting
  // them (only the WindowProxy around them). A Window may be a DOMClass, so we
  // explicitly check if this is a global.
  if (clasp->isGlobal()) {
    return false;
  }

  // WindowProxy, Wrapper, DeadProxyObject, DOMProxy, and DOMClass (non-global)
  // types may be swapped. It is hard to detect DOMProxy from shell, so target
  // proxies in general.
  return clasp->isProxyObject() || clasp->isDOMClass();
}

bool NativeObject::prepareForSwap(JSContext* cx, JSObject* other,
                                  MutableHandleValueVector slotValuesOut) {
  MOZ_ASSERT(slotValuesOut.empty());

  for (size_t i = 0; i < slotSpan(); i++) {
    if (!slotValuesOut.append(getSlot(i))) {
      return false;
    }
  }

  if (hasDynamicSlots()) {
    setEmptyDynamicSlots(0);
  }

  // Copy elements if we're swapping between tenured and nursery objects to
  // prevent:
  //  1. Tenured objects with pointers to direct nursery allocations
  //  2. Tenured objects with pointers to buffers marked as nursery-owned
  if (hasDynamicElements() && IsInsideNursery(this) != IsInsideNursery(other)) {
    ObjectElements* elements = getElementsHeader();
    size_t count = elements->numAllocatedElements();
    size_t size = count * sizeof(HeapSlot);
    void* buffer = AllocBuffer(cx->zone(), size, IsInsideNursery(other));
    if (!buffer) {
      return false;
    }

    memmove(buffer, getUnshiftedElementsHeader(), size);

    uint32_t numShifted = elements->numShiftedElements();
    auto* newElements = reinterpret_cast<ObjectElements*>(
        reinterpret_cast<HeapSlot*>(buffer) + numShifted);

    elements_ = newElements->elements();

    MOZ_ASSERT(hasDynamicElements());
  }

  return true;
}

/* static */
bool NativeObject::fixupAfterSwap(JSContext* cx, Handle<NativeObject*> obj,
                                  gc::AllocKind kind,
                                  HandleValueVector slotValues) {
  // This object has just been swapped with some other object, and its shape
  // no longer reflects its allocated size. Correct this information and
  // fill the slots in with the specified values.
  MOZ_ASSERT_IF(!obj->inDictionaryMode(),
                obj->slotSpan() == slotValues.length());

  // Make sure the shape's numFixedSlots() is correct.
  size_t nfixed = gc::GetGCKindSlots(kind);
  if (nfixed != obj->shape()->numFixedSlots()) {
    if (!NativeObject::changeNumFixedSlotsAfterSwap(cx, obj, nfixed)) {
      return false;
    }
    MOZ_ASSERT(obj->shape()->numFixedSlots() == nfixed);
  }

  uint32_t oldDictionarySlotSpan =
      obj->inDictionaryMode() ? slotValues.length() : 0;

  MOZ_ASSERT(!obj->hasUniqueId());
  size_t ndynamic =
      calculateDynamicSlots(nfixed, slotValues.length(), obj->getClass());
  size_t currentSlots = obj->getSlotsHeader()->capacity();
  MOZ_ASSERT(ndynamic >= currentSlots);
  if (ndynamic > currentSlots) {
    if (!obj->growSlots(cx, currentSlots, ndynamic)) {
      return false;
    }
  }

  if (obj->inDictionaryMode()) {
    obj->setDictionaryModeSlotSpan(oldDictionarySlotSpan);
  }

  for (size_t i = 0, len = slotValues.length(); i < len; i++) {
    obj->initSlotUnchecked(i, slotValues[i]);
  }

  MOZ_ASSERT_IF(
      obj->hasDynamicSlots() && gc::IsBufferAlloc(obj->getSlotsHeader()),
      gc::IsNurseryOwned(obj->getSlotsHeader()) == IsInsideNursery(obj));

  MOZ_ASSERT_IF(obj->hasDynamicElements() &&
                    gc::IsBufferAlloc(obj->getUnshiftedElementsHeader()),
                gc::IsNurseryOwned(obj->getUnshiftedElementsHeader()) ==
                    IsInsideNursery(obj));

  return true;
}

[[nodiscard]] bool ProxyObject::prepareForSwap(
    JSContext* cx, MutableHandleValueVector valuesOut) {
  MOZ_ASSERT(valuesOut.empty());

  // Remove the GCPtr<Value>s we're about to swap from the store buffer, to
  // ensure we don't trace bogus values.
  gc::StoreBuffer& sb = cx->runtime()->gc.storeBuffer();

  // Reserve space for the expando, private slot and the reserved slots.
  if (!valuesOut.reserve(2 + numReservedSlots())) {
    return false;
  }

  js::detail::ProxyValueArray* valArray = data.values();
  sb.unputValue(&valArray->expandoSlot);
  sb.unputValue(&valArray->privateSlot);
  valuesOut.infallibleAppend(valArray->expandoSlot);
  valuesOut.infallibleAppend(valArray->privateSlot);

  for (size_t i = 0; i < numReservedSlots(); i++) {
    sb.unputValue(&valArray->reservedSlots.slots[i]);
    valuesOut.infallibleAppend(valArray->reservedSlots.slots[i]);
  }

  if (isTenured() && !usingInlineValueArray()) {
    size_t count = detail::ProxyValueArray::allocCount(numReservedSlots());
    RemoveCellMemory(this, count * sizeof(Value),
                     MemoryUse::ProxyExternalValueArray);
    js_free(valArray);
    data.reservedSlots = nullptr;
  }

  return true;
}

bool ProxyObject::fixupAfterSwap(JSContext* cx,
                                 const HandleValueVector values) {
  MOZ_ASSERT(getClass()->isProxyObject());

  size_t nreserved = numReservedSlots();

  // |values| contains the expando slot, private slot and the reserved slots.
  MOZ_ASSERT(values.length() == 2 + nreserved);

  // Allocate the external value array in malloc memory, even for nursery
  // proxies.
  size_t count = detail::ProxyValueArray::allocCount(nreserved);
  auto* allocation = js_pod_malloc<JS::Value>(count);
  if (!allocation) {
    return false;
  }

  size_t size = count * sizeof(Value);
  if (isTenured()) {
    AddCellMemory(&asTenured(), size, MemoryUse::ProxyExternalValueArray);
  } else if (!cx->nursery().registerMallocedBuffer(allocation, size)) {
    js_free(allocation);
    return false;
  }

  auto* valArray = reinterpret_cast<js::detail::ProxyValueArray*>(allocation);

  valArray->expandoSlot = values[0];
  valArray->privateSlot = values[1];

  for (size_t i = 0; i < nreserved; i++) {
    valArray->reservedSlots.slots[i] = values[i + 2];
  }

  data.reservedSlots = &valArray->reservedSlots;
  MOZ_ASSERT(!usingInlineValueArray());
  return true;
}

static gc::AllocKind SwappableObjectAllocKind(JSObject* obj) {
  MOZ_ASSERT(ObjectMayBeSwapped(obj));

  if (obj->isTenured()) {
    return obj->asTenured().getAllocKind();
  }

  if (obj->is<NativeObject>()) {
    return obj->as<NativeObject>().allocKindForTenure();
  }

  return obj->as<ProxyObject>().allocKindForTenure();
}

/* Use this method with extreme caution. It trades the guts of two objects. */
void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b,
                    AutoEnterOOMUnsafeRegion& oomUnsafe) {
  // Ensure swap doesn't cause a finalizer to be run at the wrong time.
  MOZ_ASSERT(a->isBackgroundFinalized() == b->isBackgroundFinalized());

  MOZ_ASSERT(a->compartment() == b->compartment());

  // You must have entered the objects' compartment before calling this.
  MOZ_ASSERT(cx->compartment() == a->compartment());

  // Only certain types of objects are allowed to be swapped. This allows the
  // JITs to better optimize objects that can never swap and rules out most
  // builtin objects that have special behaviour.
  MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(a));
  MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(b));

  // Don't allow a GC which may observe intermediate state or run before we
  // execute all necessary barriers.
  gc::AutoSuppressGC nogc(cx);

  if (!Watchtower::watchObjectSwap(cx, a, b)) {
    oomUnsafe.crash("watchObjectSwap");
  }

  // Ensure we update any embedded nursery pointers in either object.
  gc::StoreBuffer& storeBuffer = cx->runtime()->gc.storeBuffer();
  if (a->isTenured()) {
    storeBuffer.putWholeCell(a);
  }
  if (b->isTenured()) {
    storeBuffer.putWholeCell(b);
  }
  if (a->isTenured() || b->isTenured()) {
    if (a->zone()->wasGCStarted()) {
      storeBuffer.setMayHavePointersToDeadCells();
    }
  }

  unsigned r = NotifyGCPreSwap(a, b);

  ProxyObject* pa = a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr;
  ProxyObject* pb = b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr;
  bool aIsProxyWithInlineValues = pa && pa->usingInlineValueArray();
  bool bIsProxyWithInlineValues = pb && pb->usingInlineValueArray();

  bool aIsUsedAsPrototype = a->isUsedAsPrototype();
  bool bIsUsedAsPrototype = b->isUsedAsPrototype();

  // Swap element associations.
  Zone* zone = a->zone();

  // Record any associated unique IDs and prepare for swap.
  //
  // Note that unique IDs are NOT swapped but remain associated with the
  // original address.
  uint64_t aid = 0;
  uint64_t bid = 0;
  (void)gc::MaybeGetUniqueId(a, &aid);
  (void)gc::MaybeGetUniqueId(b, &bid);
  NativeObject* na = a->is<NativeObject>() ? &a->as<NativeObject>() : nullptr;
  NativeObject* nb = b->is<NativeObject>() ? &b->as<NativeObject>() : nullptr;
  if ((aid || bid) && (na || nb)) {
    // We can't remove unique IDs from native objects when they are swapped with
    // objects without an ID. Instead ensure they both have IDs so we always
    // have something to overwrite the old ID with.
    if (!gc::GetOrCreateUniqueId(a, &aid) ||
        !gc::GetOrCreateUniqueId(b, &bid)) {
      oomUnsafe.crash("Failed to create unique ID during swap");
    }

    // IDs stored in NativeObjects could shadow those stored in the zone
    // table. Remove any zone table IDs first.
    if (pa && aid) {
      gc::RemoveUniqueId(a);
    }
    if (pb && bid) {
      gc::RemoveUniqueId(b);
    }
  }

  gc::AllocKind ka = SwappableObjectAllocKind(a);
  gc::AllocKind kb = SwappableObjectAllocKind(b);

  size_t sa = gc::Arena::thingSize(ka);
  size_t sb = gc::Arena::thingSize(kb);
  if (sa == sb && a->isTenured() == b->isTenured()) {
    // When both objects are the same size and in the same heap, just do a plain
    // swap of their contents.

    size_t size = sa;
    char tmp[sizeof(JSObject_Slots16)];
    MOZ_ASSERT(size <= sizeof(tmp));

    js_memcpy(tmp, a, size);
    js_memcpy(a, b, size);
    js_memcpy(b, tmp, size);

    zone->swapCellMemory(a, b, MemoryUse::ProxyExternalValueArray);

    if (aIsProxyWithInlineValues) {
      b->as<ProxyObject>().setInlineValueArray();
    }
    if (bIsProxyWithInlineValues) {
      a->as<ProxyObject>().setInlineValueArray();
    }
  } else {
    // When the objects have different sizes, they will have different numbers
    // of fixed slots before and after the swap, so the slots for native objects
    // will need to be rearranged. Remember the original values from the
    // objects.
    RootedValueVector avals(cx);
    RootedValueVector bvals(cx);
    if (na && !na->prepareForSwap(cx, b, &avals)) {
      oomUnsafe.crash("NativeObject::prepareForSwap");
    }
    if (nb && !nb->prepareForSwap(cx, a, &bvals)) {
      oomUnsafe.crash("NativeObject::prepareForSwap");
    }

    // Do the same for proxy value arrays.
    if (pa && !pa->prepareForSwap(cx, &avals)) {
      oomUnsafe.crash("ProxyObject::prepareForSwap");
    }
    if (pb && !pb->prepareForSwap(cx, &bvals)) {
      oomUnsafe.crash("ProxyObject::prepareForSwap");
    }

    // Swap the main fields of the objects, whether they are native objects or
    // proxies.
    char tmp[sizeof(JSObject_Slots0)];
    js_memcpy(&tmp, a, sizeof tmp);
    js_memcpy(a, b, sizeof tmp);
    js_memcpy(b, &tmp, sizeof tmp);

    if (na &&
        !NativeObject::fixupAfterSwap(cx, b.as<NativeObject>(), kb, avals)) {
      oomUnsafe.crash("NativeObject::fixupAfterSwap");
    }
    if (nb &&
        !NativeObject::fixupAfterSwap(cx, a.as<NativeObject>(), ka, bvals)) {
      oomUnsafe.crash("NativeObject::fixupAfterSwap");
    }

    if (pa && !b->as<ProxyObject>().fixupAfterSwap(cx, avals)) {
      oomUnsafe.crash("ProxyObject::fixupAfterSwap");
    }
    if (pb && !a->as<ProxyObject>().fixupAfterSwap(cx, bvals)) {
      oomUnsafe.crash("ProxyObject::fixupAfterSwap");
    }
  }

  // Restore original unique IDs.
  if ((aid || bid) && (na || nb)) {
    if ((aid && !gc::SetOrUpdateUniqueId(cx, a, aid)) ||
        (bid && !gc::SetOrUpdateUniqueId(cx, b, bid))) {
      oomUnsafe.crash("Failed to set unique ID after swap");
    }
  }
  MOZ_ASSERT_IF(aid, gc::GetUniqueIdInfallible(a) == aid);
  MOZ_ASSERT_IF(bid, gc::GetUniqueIdInfallible(b) == bid);

  // Preserve the IsUsedAsPrototype flag on the objects.
  if (aIsUsedAsPrototype) {
    if (!JSObject::setIsUsedAsPrototype(cx, a)) {
      oomUnsafe.crash("setIsUsedAsPrototype");
    }
  }
  if (bIsUsedAsPrototype) {
    if (!JSObject::setIsUsedAsPrototype(cx, b)) {
      oomUnsafe.crash("setIsUsedAsPrototype");
    }
  }

  /*
   * We need a write barrier here. If |a| was marked and |b| was not, then
   * after the swap, |b|'s guts would never be marked. The write barrier
   * solves this.
   *
   * Normally write barriers happen before the write. However, that's not
   * necessary here because nothing is being destroyed. We're just swapping.
   */

  PreWriteBarrier(zone, a.get(), [](JSTracer* trc, JSObject* obj) {
    obj->traceChildren(trc);
  });
  PreWriteBarrier(zone, b.get(), [](JSTracer* trc, JSObject* obj) {
    obj->traceChildren(trc);
  });

  NotifyGCPostSwap(a, b, r);
}

static NativeObject* DefineConstructorAndPrototype(
    JSContext* cx, HandleObject obj, Handle<JSAtom*> atom,
    HandleObject protoProto, const JSClass* clasp, Native constructor,
    unsigned nargs, const JSPropertySpec* ps, const JSFunctionSpec* fs,
    const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
    NativeObject** ctorp) {
  // Create the prototype object.
  Rooted<NativeObject*> proto(
      cx, GlobalObject::createBlankPrototypeInheriting(cx, clasp, protoProto));
  if (!proto) {
    return nullptr;
  }

  Rooted<NativeObject*> ctor(cx);
  if (!constructor) {
    ctor = proto;
  } else {
    ctor = NewNativeConstructor(cx, constructor, nargs, atom);
    if (!ctor) {
      return nullptr;
    }

    if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
      return nullptr;
    }
  }

  if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
      (ctor != proto &&
       !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs))) {
    return nullptr;
  }

  if (clasp->specShouldDefineConstructor()) {
    RootedId id(cx, AtomToId(atom));
    RootedValue value(cx, ObjectValue(*ctor));
    if (!DefineDataProperty(cx, obj, id, value, 0)) {
      return nullptr;
    }
  }

  if (ctorp) {
    *ctorp = ctor;
  }
  return proto;
}

NativeObject* js::InitClass(JSContext* cx, HandleObject obj,
                            const JSClass* protoClass, HandleObject protoProto_,
                            const char* name, Native constructor,
                            unsigned nargs, const JSPropertySpec* ps,
                            const JSFunctionSpec* fs,
                            const JSPropertySpec* static_ps,
                            const JSFunctionSpec* static_fs,
                            NativeObject** ctorp) {
  Rooted<JSAtom*> atom(cx, Atomize(cx, name, strlen(name)));
  if (!atom) {
    return nullptr;
  }

  /*
   * All instances of the class will inherit properties from the prototype
   * object we are about to create (in DefineConstructorAndPrototype), which
   * in turn will inherit from protoProto.
   *
   * If protoProto is nullptr, default to Object.prototype.
   * If protoClass is nullptr, default to PlainObject.
   */

  RootedObject protoProto(cx, protoProto_);
  if (!protoProto) {
    protoProto = &cx->global()->getObjectPrototype();
  }
  if (!protoClass) {
    protoClass = &PlainObject::class_;
  }

  return DefineConstructorAndPrototype(cx, obj, atom, protoProto, protoClass,
                                       constructor, nargs, ps, fs, static_ps,
                                       static_fs, ctorp);
}

/**
 * Returns the original Object.prototype from the embedding-provided incumbent
 * global.
 *
 * Really, we want the incumbent global itself so we can pass it to other
 * embedding hooks which need it. Specifically, the enqueue promise hook
 * takes an incumbent global so it can set that on the PromiseCallbackJob
 * it creates.
 *
 * The reason for not just returning the global itself is that we'd need to
 * wrap it into the current compartment, and later unwrap it. Unwrapping
 * globals is tricky, though: we might accidentally unwrap through an inner
 * to its outer window and end up with the wrong global. Plain objects don't
 * have this problem, so we use the global's Object.prototype. The code using
 * it - e.g. EnqueuePromiseReactionJob - can then unwrap the object and get
 * its global without fear of unwrapping too far.
 */

bool js::GetObjectFromHostDefinedData(JSContext* cx, MutableHandleObject obj) {
  if (!cx->runtime()->getHostDefinedData(cx, obj)) {
    return false;
  }

  // The object might be from a different compartment, so wrap it.
  if (obj && !cx->compartment()->wrap(cx, obj)) {
    return false;
  }

  return true;
}

static bool IsStandardPrototype(JSObject* obj, JSProtoKey key) {
  return obj->nonCCWGlobal().maybeGetPrototype(key) == obj;
}

JSProtoKey JS::IdentifyStandardInstance(JSObject* obj) {
  // Note: The prototype shares its JSClass with instances.
  MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
  JSProtoKey key = StandardProtoKeyOrNull(obj);
  if (key != JSProto_Null && !IsStandardPrototype(obj, key)) {
    return key;
  }
  return JSProto_Null;
}

JSProtoKey JS::IdentifyStandardPrototype(JSObject* obj) {
  // Note: The prototype shares its JSClass with instances.
  MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
  JSProtoKey key = StandardProtoKeyOrNull(obj);
  if (key != JSProto_Null && IsStandardPrototype(obj, key)) {
    return key;
  }
  return JSProto_Null;
}

JSProtoKey JS::IdentifyStandardInstanceOrPrototype(JSObject* obj) {
  return StandardProtoKeyOrNull(obj);
}

JSProtoKey JS::IdentifyStandardConstructor(JSObject* obj) {
  // Note that isNativeConstructor does not imply that we are a standard
  // constructor, but the converse is true (at least until we start having
  // self-hosted constructors for standard classes). This lets us avoid a costly
  // loop for many functions (which, depending on the call site, may be the
  // common case).
  if (!obj->is<JSFunction>() ||
      !(obj->as<JSFunction>().flags().isNativeConstructor())) {
    return JSProto_Null;
  }

  static_assert(JSProto_Null == 0,
                "Loop below can start at 1 to skip JSProto_Null");

  GlobalObject& global = obj->as<JSFunction>().global();
  for (size_t k = 1; k < JSProto_LIMIT; ++k) {
    JSProtoKey key = static_cast<JSProtoKey>(k);
    if (global.maybeGetConstructor(key) == obj) {
      return key;
    }
  }

  return JSProto_Null;
}

bool js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
                        MutableHandleObject objp, PropertyResult* propp) {
  if (LookupPropertyOp op = obj->getOpsLookupProperty()) {
    return op(cx, obj, id, objp, propp);
  }
  return NativeLookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp,
                                           propp);
}

bool js::LookupName(JSContext* cx, Handle<PropertyName*> name,
                    HandleObject envChain, MutableHandleObject objp,
                    MutableHandleObject pobjp, PropertyResult* propp) {
  RootedId id(cx, NameToId(name));

  for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) {
    if (!LookupProperty(cx, env, id, pobjp, propp)) {
      return false;
    }
    if (propp->isFound()) {
      objp.set(env);
      return true;
    }
  }

  objp.set(nullptr);
  pobjp.set(nullptr);
  propp->setNotFound();
  return true;
}

bool js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
                        NativeObject** pobjp, PropertyResult* propp) {
  AutoAssertNoPendingException nogc(cx);

  MOZ_ASSERT(!*pobjp && propp->isNotFound());

  for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
    if (env->getOpsLookupProperty()) {
      return false;
    }
    if (!NativeLookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(),
                                          NameToId(name), pobjp, propp)) {
      return false;
    }
    if (propp->isFound()) {
      return true;
    }
  }

  return true;
}

static bool IsTemporalDeadZone(JSContext* cx, HandleObject env, HandleId id,
                               const PropertyResult& prop, bool* isTDZ) {
  MOZ_ASSERT(prop.isFound());

  // We do our own explicit checking for |this|
  if (id.isAtom(cx->names().dot_this_)) {
    *isTDZ = false;
    return true;
  }

  // Treat Debugger environments specially for TDZ checks, as they
  // look like non-native environments but in fact wrap native
  // environments.
  if (env->is<DebugEnvironmentProxy>()) {
    RootedValue v(cx);
    auto envProxy = env.as<DebugEnvironmentProxy>();
    if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v)) {
      return false;
    }
    *isTDZ = IsUninitializedLexical(v);
    return true;
  }

  *isTDZ = IsUninitializedLexicalSlot(env, prop);
  return true;
}

JSObject* js::LookupNameWithGlobalDefault(JSContext* cx,
                                          Handle<PropertyName*> name,
                                          HandleObject envChain) {
  RootedId id(cx, NameToId(name));

  RootedObject pobj(cx);
  PropertyResult prop;

  RootedObject env(cx, envChain);
  for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) {
    if (!LookupProperty(cx, env, id, &pobj, &prop)) {
      return nullptr;
    }
    if (prop.isFound()) {
      break;
    }
  }

  // Uninitialized lexicals can't appear on the prototype chain, so only check
  // for TDZ when |pobj == env|.
  //
  // JSOp::BindName is always directly followed by JSOp::GetBoundName, so don't
  // bother to create a RuntimeLexicalErrorObject.
  if (pobj == env) {
    MOZ_ASSERT(prop.isFound());

    bool isTDZ;
    if (!IsTemporalDeadZone(cx, env, id, prop, &isTDZ)) {
      return nullptr;
    }
    if (isTDZ) {
      ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name);
      return nullptr;
    }
  }

  return env;
}

JSObject* js::LookupNameUnqualified(JSContext* cx, Handle<PropertyName*> name,
                                    HandleObject envChain) {
  RootedId id(cx, NameToId(name));

  RootedObject pobj(cx);
  PropertyResult prop;

  RootedObject env(cx, envChain);
  for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) {
    if (!LookupProperty(cx, env, id, &pobj, &prop)) {
      return nullptr;
    }
    if (prop.isFound()) {
      break;
    }
  }

  // Uninitialized lexicals can't appear on the prototype chain, so only check
  // for TDZ and `const` bindings when |pobj == env|.
  //
  // See note above RuntimeLexicalErrorObject.
  if (pobj == env) {
    MOZ_ASSERT(prop.isFound());

    bool isTDZ;
    if (!IsTemporalDeadZone(cx, env, id, prop, &isTDZ)) {
      return nullptr;
    }
    if (isTDZ) {
      return RuntimeLexicalErrorObject::create(cx, env,
                                               JSMSG_UNINITIALIZED_LEXICAL);
    }

    if (env->is<LexicalEnvironmentObject>() &&
        !prop.propertyInfo().writable()) {
      // Assigning to a named lambda callee name is a no-op in sloppy mode.
      if (!(env->is<BlockLexicalEnvironmentObject>() &&
            env->as<BlockLexicalEnvironmentObject>().scope().kind() ==
                ScopeKind::NamedLambda)) {
        MOZ_ASSERT(name != cx->names().dot_this_);
        return RuntimeLexicalErrorObject::create(cx, env,
                                                 JSMSG_BAD_CONST_ASSIGN);
      }
    }
  }

  return env;
}

bool js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id,
                        bool* result) {
  if (obj->is<ProxyObject>()) {
    return Proxy::hasOwn(cx, obj, id, result);
  }

  if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
    Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
    if (!op(cx, obj, id, &desc)) {
      return false;
    }
    *result = desc.isSome();
    return true;
  }

  PropertyResult prop;
  if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop)) {
    return false;
  }
  *result = prop.isFound();
  return true;
}

bool js::LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id,
                            NativeObject** objp, PropertyResult* propp) {
  if (obj->getOpsLookupProperty()) {
    return false;
  }
  return NativeLookupPropertyInline<NoGC, LookupResolveMode::CheckMayResolve>(
      cx, &obj->as<NativeObject>(), id, objp, propp);
}

bool js::LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id,
                               PropertyResult* propp) {
  if (obj->getOpsLookupProperty()) {
    return false;
  }
  return NativeLookupOwnPropertyInline<NoGC,
                                       LookupResolveMode::CheckMayResolve>(
      cx, &obj->as<NativeObject>(), id, propp);
}

static inline bool NativeGetPureInline(NativeObject* pobj, jsid id,
                                       PropertyResult prop, Value* vp,
                                       JSContext* cx) {
  if (prop.isDenseElement()) {
    *vp = pobj->getDenseElement(prop.denseElementIndex());
    return true;
  }
  if (prop.isTypedArrayElement()) {
    size_t idx = prop.typedArrayElementIndex();
    return pobj->as<TypedArrayObject>().getElement<NoGC>(cx, idx, vp);
  }

  // Fail if we have a custom getter.
  PropertyInfo propInfo = prop.propertyInfo();
  if (!propInfo.isDataProperty()) {
    return false;
  }

  *vp = pobj->getSlot(propInfo.slot());
  MOZ_ASSERT(!vp->isMagic());
  return true;
}

bool js::GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp) {
  NativeObject* pobj;
  PropertyResult prop;
  if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
    return false;
  }

  if (prop.isNotFound()) {
    vp->setUndefined();
    return true;
  }

  return NativeGetPureInline(pobj, id, prop, vp, cx);
}

bool js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp,
                            bool* found) {
  PropertyResult prop;
  if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
    return false;
  }

  if (prop.isNotFound()) {
    *found = false;
    vp->setUndefined();
    return true;
  }

  *found = true;
  return obj->is<NativeObject>() &&
         NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp, cx);
}

static inline bool NativeGetGetterPureInline(NativeObject* holder,
                                             PropertyResult prop,
                                             JSFunction** fp) {
  MOZ_ASSERT(prop.isNativeProperty());

  PropertyInfo propInfo = prop.propertyInfo();
  if (holder->hasGetter(propInfo)) {
    JSObject* getter = holder->getGetter(propInfo);
    if (getter->is<JSFunction>()) {
      *fp = &getter->as<JSFunction>();
      return true;
    }
  }

  *fp = nullptr;
  return true;
}

bool js::GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp) {
  /* Just like GetPropertyPure, but get getter function, without invoking
   * it. */

  NativeObject* pobj;
  PropertyResult prop;
  if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
    return false;
  }

  if (prop.isNotFound()) {
    *fp = nullptr;
    return true;
  }

  return prop.isNativeProperty() && NativeGetGetterPureInline(pobj, prop, fp);
}

bool js::GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id,
                          JSFunction** fp) {
  JS::AutoCheckCannotGC nogc;
  PropertyResult prop;
  if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
    return false;
  }

  if (prop.isNotFound()) {
    *fp = nullptr;
    return true;
  }

  return prop.isNativeProperty() &&
         NativeGetGetterPureInline(&obj->as<NativeObject>(), prop, fp);
}

bool js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id,
                                JSNative* native) {
  JS::AutoCheckCannotGC nogc;
  *native = nullptr;
  PropertyResult prop;
  if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
    return false;
  }

  if (!prop.isNativeProperty()) {
    return true;
  }

  PropertyInfo propInfo = prop.propertyInfo();

  NativeObject* nobj = &obj->as<NativeObject>();
  if (!nobj->hasGetter(propInfo)) {
    return true;
  }

  JSObject* getterObj = nobj->getGetter(propInfo);
  if (!getterObj->is<JSFunction>()) {
    return true;
  }

  JSFunction* getter = &getterObj->as<JSFunction>();
  if (!getter->isNativeFun()) {
    return true;
  }

  *native = getter->native();
  return true;
}

bool js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id,
                                bool* result) {
  PropertyResult prop;
  if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
    return false;
  }

  *result = prop.isNativeProperty() && prop.propertyInfo().isDataProperty();
  return true;
}

bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj,
                                bool* isOrdinary, MutableHandleObject protop) {
  if (obj->is<js::ProxyObject>()) {
    return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop);
  }

  *isOrdinary = true;
  protop.set(obj->staticPrototype());
  return true;
}

/*** ES6 standard internal methods ******************************************/

bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto,
                      JS::ObjectOpResult& result) {
  // The proxy trap subsystem fully handles prototype-setting for proxies
  // with dynamic [[Prototype]]s.
  if (obj->hasDynamicPrototype()) {
    MOZ_ASSERT(obj->is<ProxyObject>());
    return Proxy::setPrototype(cx, obj, proto, result);
  }

  /*
   * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return
   * true. Since the values in question are objects, we can just compare
   * pointers.
   */

  if (proto == obj->staticPrototype()) {
    return result.succeed();
  }

  /* Disallow mutation of immutable [[Prototype]]s. */
  if (obj->staticPrototypeIsImmutable()) {
    return result.fail(JSMSG_CANT_SET_PROTO);
  }

  /*
   * Disallow mutating the [[Prototype]] on WebAssembly GC objects.
   */

  if (obj->is<WasmGcObject>()) {
    return result.fail(JSMSG_CANT_SET_PROTO);
  }

  /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
  bool extensible;
  if (!IsExtensible(cx, obj, &extensible)) {
    return false;
  }
  if (!extensible) {
    return result.fail(JSMSG_CANT_SET_PROTO);
  }

  /*
   * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
   * have to do this comparison on the observable WindowProxy, not on the
   * possibly-Window object we're setting the proto on.
   */

  RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
  RootedObject obj2(cx, proto);
  while (obj2) {
    MOZ_ASSERT(!IsWindow(obj2));
    if (obj2 == objMaybeWindowProxy) {
      return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
    }

    bool isOrdinary;
    if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2)) {
      return false;
    }
    if (!isOrdinary) {
      break;
    }
  }

  Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
  if (!JSObject::setProtoUnchecked(cx, obj, taggedProto)) {
    return false;
  }

  return result.succeed();
}

bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) {
  ObjectOpResult result;
  return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj);
}

/**
 * IsTypedArrayFixedLength ( O )
 *
 * ES2025 draft rev 3e6f71c9402f91344ef9560425cc1e8fc45abf86
 */

static bool IsTypedArrayFixedLength(ResizableTypedArrayObject* obj) {
  MOZ_ASSERT(obj->hasResizableBuffer());

  // Step 1.
  if (obj->isAutoLength()) {
    return false;
  }

  // Steps 2-4.
  return obj->isSharedMemory();
}

bool js::PreventExtensions(JSContext* cx, HandleObject obj,
                           ObjectOpResult& result) {
  if (obj->is<ProxyObject>()) {
    return js::Proxy::preventExtensions(cx, obj, result);
  }

  if (obj->is<WasmGcObject>()) {
    return result.failCantPreventExtensions();
  }

  if (obj->is<ResizableTypedArrayObject>() &&
      !IsTypedArrayFixedLength(&obj->as<ResizableTypedArrayObject>())) {
    return result.failCantPreventExtensions();
  }

  if (!obj->nonProxyIsExtensible()) {
    // If the following assertion fails, there's somewhere else a missing
    // call to shrinkCapacityToInitializedLength() which needs to be found
    // and fixed.
    MOZ_ASSERT_IF(obj->is<NativeObject>(),
                  obj->as<NativeObject>().getDenseInitializedLength() ==
                      obj->as<NativeObject>().getDenseCapacity());

    return result.succeed();
  }

  if (obj->is<NativeObject>()) {
    // Force lazy properties to be resolved.
    Handle<NativeObject*> nobj = obj.as<NativeObject>();
    if (!ResolveLazyProperties(cx, nobj)) {
      return false;
    }

    // Prepare the elements. We have to do this before we mark the object
    // non-extensible; that's fine because these changes are not observable.
    ObjectElements::PrepareForPreventExtensions(cx, nobj);
  }

  // Finally, set the NotExtensible flag on the Shape and ObjectElements.
  if (!JSObject::setFlag(cx, obj, ObjectFlag::NotExtensible)) {
    return false;
  }
  if (obj->is<NativeObject>()) {
    ObjectElements::PreventExtensions(&obj->as<NativeObject>());
  }

  return result.succeed();
}

bool js::PreventExtensions(JSContext* cx, HandleObject obj) {
  ObjectOpResult result;
  return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj);
}

bool js::GetOwnPropertyDescriptor(
    JSContext* cx, HandleObject obj, HandleId id,
    MutableHandle<Maybe<PropertyDescriptor>> desc) {
  if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
    bool ok = op(cx, obj, id, desc);
    if (ok && desc.isSome()) {
      desc->assertComplete();
    }
    return ok;
  }

  return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc);
}

bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
                        Handle<PropertyDescriptor> desc) {
  ObjectOpResult result;
  return DefineProperty(cx, obj, id, desc, result) &&
         result.checkStrict(cx, obj, id);
}

bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
                        Handle<PropertyDescriptor> desc,
                        ObjectOpResult& result) {
  desc.assertValid();
  if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
    return op(cx, obj, id, desc, result);
  }
  return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
}

bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
                                HandleObject getter, HandleObject setter,
                                unsigned attrs, ObjectOpResult& result) {
  Rooted<PropertyDescriptor> desc(
      cx, PropertyDescriptor::Accessor(
              getter ? mozilla::Some(getter) : mozilla::Nothing(),
              setter ? mozilla::Some(setter) : mozilla::Nothing(), attrs));

  if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
    return op(cx, obj, id, desc, result);
  }
  return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
}

bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id,
                            HandleValue value, unsigned attrs,
                            ObjectOpResult& result) {
  Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Data(value, attrs));
  if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
    return op(cx, obj, id, desc, result);
  }
  return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
}

bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
                                HandleObject getter, HandleObject setter,
                                unsigned attrs) {
  ObjectOpResult result;
  if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result)) {
    return false;
  }
  if (!result) {
    result.reportError(cx, obj, id);
    return false;
  }
  return true;
}

bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id,
                            HandleValue value, unsigned attrs) {
  ObjectOpResult result;
--> --------------------

--> maximum size reached

--> --------------------

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

¤ Dauer der Verarbeitung: 0.26 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.